roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * @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(config){
1404     var data = config;
1405     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
1406         data = config.data;
1407     }
1408     Roo.data.MemoryProxy.superclass.constructor.call(this);
1409     this.data = data;
1410 };
1411
1412 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1413     
1414     /**
1415      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1416      */
1417     /**
1418      * Load data from the requested source (in this case an in-memory
1419      * data object passed to the constructor), read the data object into
1420      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1421      * process that block using the passed callback.
1422      * @param {Object} params This parameter is not used by the MemoryProxy class.
1423      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1424      * object into a block of Roo.data.Records.
1425      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1426      * The function must be passed <ul>
1427      * <li>The Record block object</li>
1428      * <li>The "arg" argument from the load function</li>
1429      * <li>A boolean success indicator</li>
1430      * </ul>
1431      * @param {Object} scope The scope in which to call the callback
1432      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1433      */
1434     load : function(params, reader, callback, scope, arg){
1435         params = params || {};
1436         var result;
1437         try {
1438             result = reader.readRecords(params.data ? params.data :this.data);
1439         }catch(e){
1440             this.fireEvent("loadexception", this, arg, null, e);
1441             callback.call(scope, null, arg, false);
1442             return;
1443         }
1444         callback.call(scope, result, arg, true);
1445     },
1446     
1447     // private
1448     update : function(params, records){
1449         
1450     }
1451 });/*
1452  * Based on:
1453  * Ext JS Library 1.1.1
1454  * Copyright(c) 2006-2007, Ext JS, LLC.
1455  *
1456  * Originally Released Under LGPL - original licence link has changed is not relivant.
1457  *
1458  * Fork - LGPL
1459  * <script type="text/javascript">
1460  */
1461 /**
1462  * @class Roo.data.HttpProxy
1463  * @extends Roo.data.DataProxy
1464  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1465  * configured to reference a certain URL.<br><br>
1466  * <p>
1467  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1468  * from which the running page was served.<br><br>
1469  * <p>
1470  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1471  * <p>
1472  * Be aware that to enable the browser to parse an XML document, the server must set
1473  * the Content-Type header in the HTTP response to "text/xml".
1474  * @constructor
1475  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1476  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1477  * will be used to make the request.
1478  */
1479 Roo.data.HttpProxy = function(conn){
1480     Roo.data.HttpProxy.superclass.constructor.call(this);
1481     // is conn a conn config or a real conn?
1482     this.conn = conn;
1483     this.useAjax = !conn || !conn.events;
1484   
1485 };
1486
1487 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1488     // thse are take from connection...
1489     
1490     /**
1491      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1492      */
1493     /**
1494      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1495      * extra parameters to each request made by this object. (defaults to undefined)
1496      */
1497     /**
1498      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1499      *  to each request made by this object. (defaults to undefined)
1500      */
1501     /**
1502      * @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)
1503      */
1504     /**
1505      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1506      */
1507      /**
1508      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1509      * @type Boolean
1510      */
1511   
1512
1513     /**
1514      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1515      * @type Boolean
1516      */
1517     /**
1518      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1519      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1520      * a finer-grained basis than the DataProxy events.
1521      */
1522     getConnection : function(){
1523         return this.useAjax ? Roo.Ajax : this.conn;
1524     },
1525
1526     /**
1527      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1528      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1529      * process that block using the passed callback.
1530      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1531      * for the request to the remote server.
1532      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1533      * object into a block of Roo.data.Records.
1534      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1535      * The function must be passed <ul>
1536      * <li>The Record block object</li>
1537      * <li>The "arg" argument from the load function</li>
1538      * <li>A boolean success indicator</li>
1539      * </ul>
1540      * @param {Object} scope The scope in which to call the callback
1541      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1542      */
1543     load : function(params, reader, callback, scope, arg){
1544         if(this.fireEvent("beforeload", this, params) !== false){
1545             var  o = {
1546                 params : params || {},
1547                 request: {
1548                     callback : callback,
1549                     scope : scope,
1550                     arg : arg
1551                 },
1552                 reader: reader,
1553                 callback : this.loadResponse,
1554                 scope: this
1555             };
1556             if(this.useAjax){
1557                 Roo.applyIf(o, this.conn);
1558                 if(this.activeRequest){
1559                     Roo.Ajax.abort(this.activeRequest);
1560                 }
1561                 this.activeRequest = Roo.Ajax.request(o);
1562             }else{
1563                 this.conn.request(o);
1564             }
1565         }else{
1566             callback.call(scope||this, null, arg, false);
1567         }
1568     },
1569
1570     // private
1571     loadResponse : function(o, success, response){
1572         delete this.activeRequest;
1573         if(!success){
1574             this.fireEvent("loadexception", this, o, response);
1575             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1576             return;
1577         }
1578         var result;
1579         try {
1580             result = o.reader.read(response);
1581         }catch(e){
1582             o.success = false;
1583             o.raw = { errorMsg : response.responseText };
1584             this.fireEvent("loadexception", this, o, response, e);
1585             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1586             return;
1587         }
1588         
1589         this.fireEvent("load", this, o, o.request.arg);
1590         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1591     },
1592
1593     // private
1594     update : function(dataSet){
1595
1596     },
1597
1598     // private
1599     updateResponse : function(dataSet){
1600
1601     }
1602 });/*
1603  * Based on:
1604  * Ext JS Library 1.1.1
1605  * Copyright(c) 2006-2007, Ext JS, LLC.
1606  *
1607  * Originally Released Under LGPL - original licence link has changed is not relivant.
1608  *
1609  * Fork - LGPL
1610  * <script type="text/javascript">
1611  */
1612
1613 /**
1614  * @class Roo.data.ScriptTagProxy
1615  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1616  * other than the originating domain of the running page.<br><br>
1617  * <p>
1618  * <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
1619  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1620  * <p>
1621  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1622  * source code that is used as the source inside a &lt;script> tag.<br><br>
1623  * <p>
1624  * In order for the browser to process the returned data, the server must wrap the data object
1625  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1626  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1627  * depending on whether the callback name was passed:
1628  * <p>
1629  * <pre><code>
1630 boolean scriptTag = false;
1631 String cb = request.getParameter("callback");
1632 if (cb != null) {
1633     scriptTag = true;
1634     response.setContentType("text/javascript");
1635 } else {
1636     response.setContentType("application/x-json");
1637 }
1638 Writer out = response.getWriter();
1639 if (scriptTag) {
1640     out.write(cb + "(");
1641 }
1642 out.print(dataBlock.toJsonString());
1643 if (scriptTag) {
1644     out.write(");");
1645 }
1646 </pre></code>
1647  *
1648  * @constructor
1649  * @param {Object} config A configuration object.
1650  */
1651 Roo.data.ScriptTagProxy = function(config){
1652     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1653     Roo.apply(this, config);
1654     this.head = document.getElementsByTagName("head")[0];
1655 };
1656
1657 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1658
1659 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1660     /**
1661      * @cfg {String} url The URL from which to request the data object.
1662      */
1663     /**
1664      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1665      */
1666     timeout : 30000,
1667     /**
1668      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1669      * the server the name of the callback function set up by the load call to process the returned data object.
1670      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1671      * javascript output which calls this named function passing the data object as its only parameter.
1672      */
1673     callbackParam : "callback",
1674     /**
1675      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1676      * name to the request.
1677      */
1678     nocache : true,
1679
1680     /**
1681      * Load data from the configured URL, read the data object into
1682      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1683      * process that block using the passed callback.
1684      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1685      * for the request to the remote server.
1686      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1687      * object into a block of Roo.data.Records.
1688      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1689      * The function must be passed <ul>
1690      * <li>The Record block object</li>
1691      * <li>The "arg" argument from the load function</li>
1692      * <li>A boolean success indicator</li>
1693      * </ul>
1694      * @param {Object} scope The scope in which to call the callback
1695      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1696      */
1697     load : function(params, reader, callback, scope, arg){
1698         if(this.fireEvent("beforeload", this, params) !== false){
1699
1700             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1701
1702             var url = this.url;
1703             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1704             if(this.nocache){
1705                 url += "&_dc=" + (new Date().getTime());
1706             }
1707             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1708             var trans = {
1709                 id : transId,
1710                 cb : "stcCallback"+transId,
1711                 scriptId : "stcScript"+transId,
1712                 params : params,
1713                 arg : arg,
1714                 url : url,
1715                 callback : callback,
1716                 scope : scope,
1717                 reader : reader
1718             };
1719             var conn = this;
1720
1721             window[trans.cb] = function(o){
1722                 conn.handleResponse(o, trans);
1723             };
1724
1725             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1726
1727             if(this.autoAbort !== false){
1728                 this.abort();
1729             }
1730
1731             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1732
1733             var script = document.createElement("script");
1734             script.setAttribute("src", url);
1735             script.setAttribute("type", "text/javascript");
1736             script.setAttribute("id", trans.scriptId);
1737             this.head.appendChild(script);
1738
1739             this.trans = trans;
1740         }else{
1741             callback.call(scope||this, null, arg, false);
1742         }
1743     },
1744
1745     // private
1746     isLoading : function(){
1747         return this.trans ? true : false;
1748     },
1749
1750     /**
1751      * Abort the current server request.
1752      */
1753     abort : function(){
1754         if(this.isLoading()){
1755             this.destroyTrans(this.trans);
1756         }
1757     },
1758
1759     // private
1760     destroyTrans : function(trans, isLoaded){
1761         this.head.removeChild(document.getElementById(trans.scriptId));
1762         clearTimeout(trans.timeoutId);
1763         if(isLoaded){
1764             window[trans.cb] = undefined;
1765             try{
1766                 delete window[trans.cb];
1767             }catch(e){}
1768         }else{
1769             // if hasn't been loaded, wait for load to remove it to prevent script error
1770             window[trans.cb] = function(){
1771                 window[trans.cb] = undefined;
1772                 try{
1773                     delete window[trans.cb];
1774                 }catch(e){}
1775             };
1776         }
1777     },
1778
1779     // private
1780     handleResponse : function(o, trans){
1781         this.trans = false;
1782         this.destroyTrans(trans, true);
1783         var result;
1784         try {
1785             result = trans.reader.readRecords(o);
1786         }catch(e){
1787             this.fireEvent("loadexception", this, o, trans.arg, e);
1788             trans.callback.call(trans.scope||window, null, trans.arg, false);
1789             return;
1790         }
1791         this.fireEvent("load", this, o, trans.arg);
1792         trans.callback.call(trans.scope||window, result, trans.arg, true);
1793     },
1794
1795     // private
1796     handleFailure : function(trans){
1797         this.trans = false;
1798         this.destroyTrans(trans, false);
1799         this.fireEvent("loadexception", this, null, trans.arg);
1800         trans.callback.call(trans.scope||window, null, trans.arg, false);
1801     }
1802 });/*
1803  * Based on:
1804  * Ext JS Library 1.1.1
1805  * Copyright(c) 2006-2007, Ext JS, LLC.
1806  *
1807  * Originally Released Under LGPL - original licence link has changed is not relivant.
1808  *
1809  * Fork - LGPL
1810  * <script type="text/javascript">
1811  */
1812
1813 /**
1814  * @class Roo.data.JsonReader
1815  * @extends Roo.data.DataReader
1816  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1817  * based on mappings in a provided Roo.data.Record constructor.
1818  * 
1819  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1820  * in the reply previously. 
1821  * 
1822  * <p>
1823  * Example code:
1824  * <pre><code>
1825 var RecordDef = Roo.data.Record.create([
1826     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1827     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1828 ]);
1829 var myReader = new Roo.data.JsonReader({
1830     totalProperty: "results",    // The property which contains the total dataset size (optional)
1831     root: "rows",                // The property which contains an Array of row objects
1832     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1833 }, RecordDef);
1834 </code></pre>
1835  * <p>
1836  * This would consume a JSON file like this:
1837  * <pre><code>
1838 { 'results': 2, 'rows': [
1839     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1840     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1841 }
1842 </code></pre>
1843  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1844  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1845  * paged from the remote server.
1846  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1847  * @cfg {String} root name of the property which contains the Array of row objects.
1848  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1849  * @cfg {Array} fields Array of field definition objects
1850  * @constructor
1851  * Create a new JsonReader
1852  * @param {Object} meta Metadata configuration options
1853  * @param {Object} recordType Either an Array of field definition objects,
1854  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1855  */
1856 Roo.data.JsonReader = function(meta, recordType){
1857     
1858     meta = meta || {};
1859     // set some defaults:
1860     Roo.applyIf(meta, {
1861         totalProperty: 'total',
1862         successProperty : 'success',
1863         root : 'data',
1864         id : 'id'
1865     });
1866     
1867     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1868 };
1869 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1870     
1871     readerType : 'Json',
1872     
1873     /**
1874      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1875      * Used by Store query builder to append _requestMeta to params.
1876      * 
1877      */
1878     metaFromRemote : false,
1879     /**
1880      * This method is only used by a DataProxy which has retrieved data from a remote server.
1881      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1882      * @return {Object} data A data block which is used by an Roo.data.Store object as
1883      * a cache of Roo.data.Records.
1884      */
1885     read : function(response){
1886         var json = response.responseText;
1887        
1888         var o = /* eval:var:o */ eval("("+json+")");
1889         if(!o) {
1890             throw {message: "JsonReader.read: Json object not found"};
1891         }
1892         
1893         if(o.metaData){
1894             
1895             delete this.ef;
1896             this.metaFromRemote = true;
1897             this.meta = o.metaData;
1898             this.recordType = Roo.data.Record.create(o.metaData.fields);
1899             this.onMetaChange(this.meta, this.recordType, o);
1900         }
1901         return this.readRecords(o);
1902     },
1903
1904     // private function a store will implement
1905     onMetaChange : function(meta, recordType, o){
1906
1907     },
1908
1909     /**
1910          * @ignore
1911          */
1912     simpleAccess: function(obj, subsc) {
1913         return obj[subsc];
1914     },
1915
1916         /**
1917          * @ignore
1918          */
1919     getJsonAccessor: function(){
1920         var re = /[\[\.]/;
1921         return function(expr) {
1922             try {
1923                 return(re.test(expr))
1924                     ? new Function("obj", "return obj." + expr)
1925                     : function(obj){
1926                         return obj[expr];
1927                     };
1928             } catch(e){}
1929             return Roo.emptyFn;
1930         };
1931     }(),
1932
1933     /**
1934      * Create a data block containing Roo.data.Records from an XML document.
1935      * @param {Object} o An object which contains an Array of row objects in the property specified
1936      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1937      * which contains the total size of the dataset.
1938      * @return {Object} data A data block which is used by an Roo.data.Store object as
1939      * a cache of Roo.data.Records.
1940      */
1941     readRecords : function(o){
1942         /**
1943          * After any data loads, the raw JSON data is available for further custom processing.
1944          * @type Object
1945          */
1946         this.o = o;
1947         var s = this.meta, Record = this.recordType,
1948             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1949
1950 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1951         if (!this.ef) {
1952             if(s.totalProperty) {
1953                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1954                 }
1955                 if(s.successProperty) {
1956                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1957                 }
1958                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1959                 if (s.id) {
1960                         var g = this.getJsonAccessor(s.id);
1961                         this.getId = function(rec) {
1962                                 var r = g(rec);  
1963                                 return (r === undefined || r === "") ? null : r;
1964                         };
1965                 } else {
1966                         this.getId = function(){return null;};
1967                 }
1968             this.ef = [];
1969             for(var jj = 0; jj < fl; jj++){
1970                 f = fi[jj];
1971                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1972                 this.ef[jj] = this.getJsonAccessor(map);
1973             }
1974         }
1975
1976         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1977         if(s.totalProperty){
1978             var vt = parseInt(this.getTotal(o), 10);
1979             if(!isNaN(vt)){
1980                 totalRecords = vt;
1981             }
1982         }
1983         if(s.successProperty){
1984             var vs = this.getSuccess(o);
1985             if(vs === false || vs === 'false'){
1986                 success = false;
1987             }
1988         }
1989         var records = [];
1990         for(var i = 0; i < c; i++){
1991             var n = root[i];
1992             var values = {};
1993             var id = this.getId(n);
1994             for(var j = 0; j < fl; j++){
1995                 f = fi[j];
1996                                 var v = this.ef[j](n);
1997                                 if (!f.convert) {
1998                                         Roo.log('missing convert for ' + f.name);
1999                                         Roo.log(f);
2000                                         continue;
2001                                 }
2002                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
2003             }
2004                         if (!Record) {
2005                                 return {
2006                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2007                                         success : false,
2008                                         records : [],
2009                                         totalRecords : 0
2010                                 };
2011                         }
2012             var record = new Record(values, id);
2013             record.json = n;
2014             records[i] = record;
2015         }
2016         return {
2017             raw : o,
2018             success : success,
2019             records : records,
2020             totalRecords : totalRecords
2021         };
2022     },
2023     // used when loading children.. @see loadDataFromChildren
2024     toLoadData: function(rec)
2025     {
2026         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2027         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2028         return { data : data, total : data.length };
2029         
2030     }
2031 });/*
2032  * Based on:
2033  * Ext JS Library 1.1.1
2034  * Copyright(c) 2006-2007, Ext JS, LLC.
2035  *
2036  * Originally Released Under LGPL - original licence link has changed is not relivant.
2037  *
2038  * Fork - LGPL
2039  * <script type="text/javascript">
2040  */
2041
2042 /**
2043  * @class Roo.data.XmlReader
2044  * @extends Roo.data.DataReader
2045  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2046  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2047  * <p>
2048  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2049  * header in the HTTP response must be set to "text/xml".</em>
2050  * <p>
2051  * Example code:
2052  * <pre><code>
2053 var RecordDef = Roo.data.Record.create([
2054    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2055    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2056 ]);
2057 var myReader = new Roo.data.XmlReader({
2058    totalRecords: "results", // The element which contains the total dataset size (optional)
2059    record: "row",           // The repeated element which contains row information
2060    id: "id"                 // The element within the row that provides an ID for the record (optional)
2061 }, RecordDef);
2062 </code></pre>
2063  * <p>
2064  * This would consume an XML file like this:
2065  * <pre><code>
2066 &lt;?xml?>
2067 &lt;dataset>
2068  &lt;results>2&lt;/results>
2069  &lt;row>
2070    &lt;id>1&lt;/id>
2071    &lt;name>Bill&lt;/name>
2072    &lt;occupation>Gardener&lt;/occupation>
2073  &lt;/row>
2074  &lt;row>
2075    &lt;id>2&lt;/id>
2076    &lt;name>Ben&lt;/name>
2077    &lt;occupation>Horticulturalist&lt;/occupation>
2078  &lt;/row>
2079 &lt;/dataset>
2080 </code></pre>
2081  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2082  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2083  * paged from the remote server.
2084  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2085  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2086  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2087  * a record identifier value.
2088  * @constructor
2089  * Create a new XmlReader
2090  * @param {Object} meta Metadata configuration options
2091  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2092  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2093  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2094  */
2095 Roo.data.XmlReader = function(meta, recordType){
2096     meta = meta || {};
2097     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2098 };
2099 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2100     
2101     readerType : 'Xml',
2102     
2103     /**
2104      * This method is only used by a DataProxy which has retrieved data from a remote server.
2105          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2106          * to contain a method called 'responseXML' that returns an XML document object.
2107      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2108      * a cache of Roo.data.Records.
2109      */
2110     read : function(response){
2111         var doc = response.responseXML;
2112         if(!doc) {
2113             throw {message: "XmlReader.read: XML Document not available"};
2114         }
2115         return this.readRecords(doc);
2116     },
2117
2118     /**
2119      * Create a data block containing Roo.data.Records from an XML document.
2120          * @param {Object} doc A parsed XML document.
2121      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2122      * a cache of Roo.data.Records.
2123      */
2124     readRecords : function(doc){
2125         /**
2126          * After any data loads/reads, the raw XML Document is available for further custom processing.
2127          * @type XMLDocument
2128          */
2129         this.xmlData = doc;
2130         var root = doc.documentElement || doc;
2131         var q = Roo.DomQuery;
2132         var recordType = this.recordType, fields = recordType.prototype.fields;
2133         var sid = this.meta.id;
2134         var totalRecords = 0, success = true;
2135         if(this.meta.totalRecords){
2136             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2137         }
2138         
2139         if(this.meta.success){
2140             var sv = q.selectValue(this.meta.success, root, true);
2141             success = sv !== false && sv !== 'false';
2142         }
2143         var records = [];
2144         var ns = q.select(this.meta.record, root);
2145         for(var i = 0, len = ns.length; i < len; i++) {
2146                 var n = ns[i];
2147                 var values = {};
2148                 var id = sid ? q.selectValue(sid, n) : undefined;
2149                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2150                     var f = fields.items[j];
2151                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2152                     v = f.convert(v);
2153                     values[f.name] = v;
2154                 }
2155                 var record = new recordType(values, id);
2156                 record.node = n;
2157                 records[records.length] = record;
2158             }
2159
2160             return {
2161                 success : success,
2162                 records : records,
2163                 totalRecords : totalRecords || records.length
2164             };
2165     }
2166 });/*
2167  * Based on:
2168  * Ext JS Library 1.1.1
2169  * Copyright(c) 2006-2007, Ext JS, LLC.
2170  *
2171  * Originally Released Under LGPL - original licence link has changed is not relivant.
2172  *
2173  * Fork - LGPL
2174  * <script type="text/javascript">
2175  */
2176
2177 /**
2178  * @class Roo.data.ArrayReader
2179  * @extends Roo.data.DataReader
2180  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2181  * Each element of that Array represents a row of data fields. The
2182  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2183  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2184  * <p>
2185  * Example code:.
2186  * <pre><code>
2187 var RecordDef = Roo.data.Record.create([
2188     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2189     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2190 ]);
2191 var myReader = new Roo.data.ArrayReader({
2192     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2193 }, RecordDef);
2194 </code></pre>
2195  * <p>
2196  * This would consume an Array like this:
2197  * <pre><code>
2198 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2199   </code></pre>
2200  
2201  * @constructor
2202  * Create a new JsonReader
2203  * @param {Object} meta Metadata configuration options.
2204  * @param {Object|Array} recordType Either an Array of field definition objects
2205  * 
2206  * @cfg {Array} fields Array of field definition objects
2207  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2208  * as specified to {@link Roo.data.Record#create},
2209  * or an {@link Roo.data.Record} object
2210  *
2211  * 
2212  * created using {@link Roo.data.Record#create}.
2213  */
2214 Roo.data.ArrayReader = function(meta, recordType)
2215 {    
2216     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2217 };
2218
2219 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2220     
2221       /**
2222      * Create a data block containing Roo.data.Records from an XML document.
2223      * @param {Object} o An Array of row objects which represents the dataset.
2224      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2225      * a cache of Roo.data.Records.
2226      */
2227     readRecords : function(o)
2228     {
2229         var sid = this.meta ? this.meta.id : null;
2230         var recordType = this.recordType, fields = recordType.prototype.fields;
2231         var records = [];
2232         var root = o;
2233         for(var i = 0; i < root.length; i++){
2234             var n = root[i];
2235             var values = {};
2236             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2237             for(var j = 0, jlen = fields.length; j < jlen; j++){
2238                 var f = fields.items[j];
2239                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2240                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2241                 v = f.convert(v);
2242                 values[f.name] = v;
2243             }
2244             var record = new recordType(values, id);
2245             record.json = n;
2246             records[records.length] = record;
2247         }
2248         return {
2249             records : records,
2250             totalRecords : records.length
2251         };
2252     },
2253     // used when loading children.. @see loadDataFromChildren
2254     toLoadData: function(rec)
2255     {
2256         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2257         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2258         
2259     }
2260     
2261     
2262 });/*
2263  * Based on:
2264  * Ext JS Library 1.1.1
2265  * Copyright(c) 2006-2007, Ext JS, LLC.
2266  *
2267  * Originally Released Under LGPL - original licence link has changed is not relivant.
2268  *
2269  * Fork - LGPL
2270  * <script type="text/javascript">
2271  */
2272
2273
2274 /**
2275  * @class Roo.data.Tree
2276  * @extends Roo.util.Observable
2277  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2278  * in the tree have most standard DOM functionality.
2279  * @constructor
2280  * @param {Node} root (optional) The root node
2281  */
2282 Roo.data.Tree = function(root){
2283    this.nodeHash = {};
2284    /**
2285     * The root node for this tree
2286     * @type Node
2287     */
2288    this.root = null;
2289    if(root){
2290        this.setRootNode(root);
2291    }
2292    this.addEvents({
2293        /**
2294         * @event append
2295         * Fires when a new child node is appended to a node in this tree.
2296         * @param {Tree} tree The owner tree
2297         * @param {Node} parent The parent node
2298         * @param {Node} node The newly appended node
2299         * @param {Number} index The index of the newly appended node
2300         */
2301        "append" : true,
2302        /**
2303         * @event remove
2304         * Fires when a child node is removed from a node in this tree.
2305         * @param {Tree} tree The owner tree
2306         * @param {Node} parent The parent node
2307         * @param {Node} node The child node removed
2308         */
2309        "remove" : true,
2310        /**
2311         * @event move
2312         * Fires when a node is moved to a new location in the tree
2313         * @param {Tree} tree The owner tree
2314         * @param {Node} node The node moved
2315         * @param {Node} oldParent The old parent of this node
2316         * @param {Node} newParent The new parent of this node
2317         * @param {Number} index The index it was moved to
2318         */
2319        "move" : true,
2320        /**
2321         * @event insert
2322         * Fires when a new child node is inserted in a node in this tree.
2323         * @param {Tree} tree The owner tree
2324         * @param {Node} parent The parent node
2325         * @param {Node} node The child node inserted
2326         * @param {Node} refNode The child node the node was inserted before
2327         */
2328        "insert" : true,
2329        /**
2330         * @event beforeappend
2331         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2332         * @param {Tree} tree The owner tree
2333         * @param {Node} parent The parent node
2334         * @param {Node} node The child node to be appended
2335         */
2336        "beforeappend" : true,
2337        /**
2338         * @event beforeremove
2339         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2340         * @param {Tree} tree The owner tree
2341         * @param {Node} parent The parent node
2342         * @param {Node} node The child node to be removed
2343         */
2344        "beforeremove" : true,
2345        /**
2346         * @event beforemove
2347         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2348         * @param {Tree} tree The owner tree
2349         * @param {Node} node The node being moved
2350         * @param {Node} oldParent The parent of the node
2351         * @param {Node} newParent The new parent the node is moving to
2352         * @param {Number} index The index it is being moved to
2353         */
2354        "beforemove" : true,
2355        /**
2356         * @event beforeinsert
2357         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2358         * @param {Tree} tree The owner tree
2359         * @param {Node} parent The parent node
2360         * @param {Node} node The child node to be inserted
2361         * @param {Node} refNode The child node the node is being inserted before
2362         */
2363        "beforeinsert" : true
2364    });
2365
2366     Roo.data.Tree.superclass.constructor.call(this);
2367 };
2368
2369 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2370     pathSeparator: "/",
2371
2372     proxyNodeEvent : function(){
2373         return this.fireEvent.apply(this, arguments);
2374     },
2375
2376     /**
2377      * Returns the root node for this tree.
2378      * @return {Node}
2379      */
2380     getRootNode : function(){
2381         return this.root;
2382     },
2383
2384     /**
2385      * Sets the root node for this tree.
2386      * @param {Node} node
2387      * @return {Node}
2388      */
2389     setRootNode : function(node){
2390         this.root = node;
2391         node.ownerTree = this;
2392         node.isRoot = true;
2393         this.registerNode(node);
2394         return node;
2395     },
2396
2397     /**
2398      * Gets a node in this tree by its id.
2399      * @param {String} id
2400      * @return {Node}
2401      */
2402     getNodeById : function(id){
2403         return this.nodeHash[id];
2404     },
2405
2406     registerNode : function(node){
2407         this.nodeHash[node.id] = node;
2408     },
2409
2410     unregisterNode : function(node){
2411         delete this.nodeHash[node.id];
2412     },
2413
2414     toString : function(){
2415         return "[Tree"+(this.id?" "+this.id:"")+"]";
2416     }
2417 });
2418
2419 /**
2420  * @class Roo.data.Node
2421  * @extends Roo.util.Observable
2422  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2423  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2424  * @constructor
2425  * @param {Object} attributes The attributes/config for the node
2426  */
2427 Roo.data.Node = function(attributes){
2428     /**
2429      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2430      * @type {Object}
2431      */
2432     this.attributes = attributes || {};
2433     this.leaf = this.attributes.leaf;
2434     /**
2435      * The node id. @type String
2436      */
2437     this.id = this.attributes.id;
2438     if(!this.id){
2439         this.id = Roo.id(null, "ynode-");
2440         this.attributes.id = this.id;
2441     }
2442      
2443     
2444     /**
2445      * All child nodes of this node. @type Array
2446      */
2447     this.childNodes = [];
2448     if(!this.childNodes.indexOf){ // indexOf is a must
2449         this.childNodes.indexOf = function(o){
2450             for(var i = 0, len = this.length; i < len; i++){
2451                 if(this[i] == o) {
2452                     return i;
2453                 }
2454             }
2455             return -1;
2456         };
2457     }
2458     /**
2459      * The parent node for this node. @type Node
2460      */
2461     this.parentNode = null;
2462     /**
2463      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2464      */
2465     this.firstChild = null;
2466     /**
2467      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2468      */
2469     this.lastChild = null;
2470     /**
2471      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2472      */
2473     this.previousSibling = null;
2474     /**
2475      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2476      */
2477     this.nextSibling = null;
2478
2479     this.addEvents({
2480        /**
2481         * @event append
2482         * Fires when a new child node is appended
2483         * @param {Tree} tree The owner tree
2484         * @param {Node} this This node
2485         * @param {Node} node The newly appended node
2486         * @param {Number} index The index of the newly appended node
2487         */
2488        "append" : true,
2489        /**
2490         * @event remove
2491         * Fires when a child node is removed
2492         * @param {Tree} tree The owner tree
2493         * @param {Node} this This node
2494         * @param {Node} node The removed node
2495         */
2496        "remove" : true,
2497        /**
2498         * @event move
2499         * Fires when this node is moved to a new location in the tree
2500         * @param {Tree} tree The owner tree
2501         * @param {Node} this This node
2502         * @param {Node} oldParent The old parent of this node
2503         * @param {Node} newParent The new parent of this node
2504         * @param {Number} index The index it was moved to
2505         */
2506        "move" : true,
2507        /**
2508         * @event insert
2509         * Fires when a new child node is inserted.
2510         * @param {Tree} tree The owner tree
2511         * @param {Node} this This node
2512         * @param {Node} node The child node inserted
2513         * @param {Node} refNode The child node the node was inserted before
2514         */
2515        "insert" : true,
2516        /**
2517         * @event beforeappend
2518         * Fires before a new child is appended, return false to cancel the append.
2519         * @param {Tree} tree The owner tree
2520         * @param {Node} this This node
2521         * @param {Node} node The child node to be appended
2522         */
2523        "beforeappend" : true,
2524        /**
2525         * @event beforeremove
2526         * Fires before a child is removed, return false to cancel the remove.
2527         * @param {Tree} tree The owner tree
2528         * @param {Node} this This node
2529         * @param {Node} node The child node to be removed
2530         */
2531        "beforeremove" : true,
2532        /**
2533         * @event beforemove
2534         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2535         * @param {Tree} tree The owner tree
2536         * @param {Node} this This node
2537         * @param {Node} oldParent The parent of this node
2538         * @param {Node} newParent The new parent this node is moving to
2539         * @param {Number} index The index it is being moved to
2540         */
2541        "beforemove" : true,
2542        /**
2543         * @event beforeinsert
2544         * Fires before a new child is inserted, return false to cancel the insert.
2545         * @param {Tree} tree The owner tree
2546         * @param {Node} this This node
2547         * @param {Node} node The child node to be inserted
2548         * @param {Node} refNode The child node the node is being inserted before
2549         */
2550        "beforeinsert" : true
2551    });
2552     this.listeners = this.attributes.listeners;
2553     Roo.data.Node.superclass.constructor.call(this);
2554 };
2555
2556 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2557     fireEvent : function(evtName){
2558         // first do standard event for this node
2559         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2560             return false;
2561         }
2562         // then bubble it up to the tree if the event wasn't cancelled
2563         var ot = this.getOwnerTree();
2564         if(ot){
2565             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2566                 return false;
2567             }
2568         }
2569         return true;
2570     },
2571
2572     /**
2573      * Returns true if this node is a leaf
2574      * @return {Boolean}
2575      */
2576     isLeaf : function(){
2577         return this.leaf === true;
2578     },
2579
2580     // private
2581     setFirstChild : function(node){
2582         this.firstChild = node;
2583     },
2584
2585     //private
2586     setLastChild : function(node){
2587         this.lastChild = node;
2588     },
2589
2590
2591     /**
2592      * Returns true if this node is the last child of its parent
2593      * @return {Boolean}
2594      */
2595     isLast : function(){
2596        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2597     },
2598
2599     /**
2600      * Returns true if this node is the first child of its parent
2601      * @return {Boolean}
2602      */
2603     isFirst : function(){
2604        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2605     },
2606
2607     hasChildNodes : function(){
2608         return !this.isLeaf() && this.childNodes.length > 0;
2609     },
2610
2611     /**
2612      * Insert node(s) as the last child node of this node.
2613      * @param {Node/Array} node The node or Array of nodes to append
2614      * @return {Node} The appended node if single append, or null if an array was passed
2615      */
2616     appendChild : function(node){
2617         var multi = false;
2618         if(node instanceof Array){
2619             multi = node;
2620         }else if(arguments.length > 1){
2621             multi = arguments;
2622         }
2623         
2624         // if passed an array or multiple args do them one by one
2625         if(multi){
2626             for(var i = 0, len = multi.length; i < len; i++) {
2627                 this.appendChild(multi[i]);
2628             }
2629         }else{
2630             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2631                 return false;
2632             }
2633             var index = this.childNodes.length;
2634             var oldParent = node.parentNode;
2635             // it's a move, make sure we move it cleanly
2636             if(oldParent){
2637                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2638                     return false;
2639                 }
2640                 oldParent.removeChild(node);
2641             }
2642             
2643             index = this.childNodes.length;
2644             if(index == 0){
2645                 this.setFirstChild(node);
2646             }
2647             this.childNodes.push(node);
2648             node.parentNode = this;
2649             var ps = this.childNodes[index-1];
2650             if(ps){
2651                 node.previousSibling = ps;
2652                 ps.nextSibling = node;
2653             }else{
2654                 node.previousSibling = null;
2655             }
2656             node.nextSibling = null;
2657             this.setLastChild(node);
2658             node.setOwnerTree(this.getOwnerTree());
2659             this.fireEvent("append", this.ownerTree, this, node, index);
2660             if(this.ownerTree) {
2661                 this.ownerTree.fireEvent("appendnode", this, node, index);
2662             }
2663             if(oldParent){
2664                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2665             }
2666             return node;
2667         }
2668     },
2669
2670     /**
2671      * Removes a child node from this node.
2672      * @param {Node} node The node to remove
2673      * @return {Node} The removed node
2674      */
2675     removeChild : function(node){
2676         var index = this.childNodes.indexOf(node);
2677         if(index == -1){
2678             return false;
2679         }
2680         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2681             return false;
2682         }
2683
2684         // remove it from childNodes collection
2685         this.childNodes.splice(index, 1);
2686
2687         // update siblings
2688         if(node.previousSibling){
2689             node.previousSibling.nextSibling = node.nextSibling;
2690         }
2691         if(node.nextSibling){
2692             node.nextSibling.previousSibling = node.previousSibling;
2693         }
2694
2695         // update child refs
2696         if(this.firstChild == node){
2697             this.setFirstChild(node.nextSibling);
2698         }
2699         if(this.lastChild == node){
2700             this.setLastChild(node.previousSibling);
2701         }
2702
2703         node.setOwnerTree(null);
2704         // clear any references from the node
2705         node.parentNode = null;
2706         node.previousSibling = null;
2707         node.nextSibling = null;
2708         this.fireEvent("remove", this.ownerTree, this, node);
2709         return node;
2710     },
2711
2712     /**
2713      * Inserts the first node before the second node in this nodes childNodes collection.
2714      * @param {Node} node The node to insert
2715      * @param {Node} refNode The node to insert before (if null the node is appended)
2716      * @return {Node} The inserted node
2717      */
2718     insertBefore : function(node, refNode){
2719         if(!refNode){ // like standard Dom, refNode can be null for append
2720             return this.appendChild(node);
2721         }
2722         // nothing to do
2723         if(node == refNode){
2724             return false;
2725         }
2726
2727         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2728             return false;
2729         }
2730         var index = this.childNodes.indexOf(refNode);
2731         var oldParent = node.parentNode;
2732         var refIndex = index;
2733
2734         // when moving internally, indexes will change after remove
2735         if(oldParent == this && this.childNodes.indexOf(node) < index){
2736             refIndex--;
2737         }
2738
2739         // it's a move, make sure we move it cleanly
2740         if(oldParent){
2741             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2742                 return false;
2743             }
2744             oldParent.removeChild(node);
2745         }
2746         if(refIndex == 0){
2747             this.setFirstChild(node);
2748         }
2749         this.childNodes.splice(refIndex, 0, node);
2750         node.parentNode = this;
2751         var ps = this.childNodes[refIndex-1];
2752         if(ps){
2753             node.previousSibling = ps;
2754             ps.nextSibling = node;
2755         }else{
2756             node.previousSibling = null;
2757         }
2758         node.nextSibling = refNode;
2759         refNode.previousSibling = node;
2760         node.setOwnerTree(this.getOwnerTree());
2761         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2762         if(oldParent){
2763             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2764         }
2765         return node;
2766     },
2767
2768     /**
2769      * Returns the child node at the specified index.
2770      * @param {Number} index
2771      * @return {Node}
2772      */
2773     item : function(index){
2774         return this.childNodes[index];
2775     },
2776
2777     /**
2778      * Replaces one child node in this node with another.
2779      * @param {Node} newChild The replacement node
2780      * @param {Node} oldChild The node to replace
2781      * @return {Node} The replaced node
2782      */
2783     replaceChild : function(newChild, oldChild){
2784         this.insertBefore(newChild, oldChild);
2785         this.removeChild(oldChild);
2786         return oldChild;
2787     },
2788
2789     /**
2790      * Returns the index of a child node
2791      * @param {Node} node
2792      * @return {Number} The index of the node or -1 if it was not found
2793      */
2794     indexOf : function(child){
2795         return this.childNodes.indexOf(child);
2796     },
2797
2798     /**
2799      * Returns the tree this node is in.
2800      * @return {Tree}
2801      */
2802     getOwnerTree : function(){
2803         // if it doesn't have one, look for one
2804         if(!this.ownerTree){
2805             var p = this;
2806             while(p){
2807                 if(p.ownerTree){
2808                     this.ownerTree = p.ownerTree;
2809                     break;
2810                 }
2811                 p = p.parentNode;
2812             }
2813         }
2814         return this.ownerTree;
2815     },
2816
2817     /**
2818      * Returns depth of this node (the root node has a depth of 0)
2819      * @return {Number}
2820      */
2821     getDepth : function(){
2822         var depth = 0;
2823         var p = this;
2824         while(p.parentNode){
2825             ++depth;
2826             p = p.parentNode;
2827         }
2828         return depth;
2829     },
2830
2831     // private
2832     setOwnerTree : function(tree){
2833         // if it's move, we need to update everyone
2834         if(tree != this.ownerTree){
2835             if(this.ownerTree){
2836                 this.ownerTree.unregisterNode(this);
2837             }
2838             this.ownerTree = tree;
2839             var cs = this.childNodes;
2840             for(var i = 0, len = cs.length; i < len; i++) {
2841                 cs[i].setOwnerTree(tree);
2842             }
2843             if(tree){
2844                 tree.registerNode(this);
2845             }
2846         }
2847     },
2848
2849     /**
2850      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2851      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2852      * @return {String} The path
2853      */
2854     getPath : function(attr){
2855         attr = attr || "id";
2856         var p = this.parentNode;
2857         var b = [this.attributes[attr]];
2858         while(p){
2859             b.unshift(p.attributes[attr]);
2860             p = p.parentNode;
2861         }
2862         var sep = this.getOwnerTree().pathSeparator;
2863         return sep + b.join(sep);
2864     },
2865
2866     /**
2867      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2868      * function call will be the scope provided or the current node. The arguments to the function
2869      * will be the args provided or the current node. If the function returns false at any point,
2870      * the bubble is stopped.
2871      * @param {Function} fn The function to call
2872      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2873      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2874      */
2875     bubble : function(fn, scope, args){
2876         var p = this;
2877         while(p){
2878             if(fn.call(scope || p, args || p) === false){
2879                 break;
2880             }
2881             p = p.parentNode;
2882         }
2883     },
2884
2885     /**
2886      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2887      * function call will be the scope provided or the current node. The arguments to the function
2888      * will be the args provided or the current node. If the function returns false at any point,
2889      * the cascade is stopped on that branch.
2890      * @param {Function} fn The function to call
2891      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2892      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2893      */
2894     cascade : function(fn, scope, args){
2895         if(fn.call(scope || this, args || this) !== false){
2896             var cs = this.childNodes;
2897             for(var i = 0, len = cs.length; i < len; i++) {
2898                 cs[i].cascade(fn, scope, args);
2899             }
2900         }
2901     },
2902
2903     /**
2904      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2905      * function call will be the scope provided or the current node. The arguments to the function
2906      * will be the args provided or the current node. If the function returns false at any point,
2907      * the iteration stops.
2908      * @param {Function} fn The function to call
2909      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2910      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2911      */
2912     eachChild : function(fn, scope, args){
2913         var cs = this.childNodes;
2914         for(var i = 0, len = cs.length; i < len; i++) {
2915                 if(fn.call(scope || this, args || cs[i]) === false){
2916                     break;
2917                 }
2918         }
2919     },
2920
2921     /**
2922      * Finds the first child that has the attribute with the specified value.
2923      * @param {String} attribute The attribute name
2924      * @param {Mixed} value The value to search for
2925      * @return {Node} The found child or null if none was found
2926      */
2927     findChild : function(attribute, value){
2928         var cs = this.childNodes;
2929         for(var i = 0, len = cs.length; i < len; i++) {
2930                 if(cs[i].attributes[attribute] == value){
2931                     return cs[i];
2932                 }
2933         }
2934         return null;
2935     },
2936
2937     /**
2938      * Finds the first child by a custom function. The child matches if the function passed
2939      * returns true.
2940      * @param {Function} fn
2941      * @param {Object} scope (optional)
2942      * @return {Node} The found child or null if none was found
2943      */
2944     findChildBy : function(fn, scope){
2945         var cs = this.childNodes;
2946         for(var i = 0, len = cs.length; i < len; i++) {
2947                 if(fn.call(scope||cs[i], cs[i]) === true){
2948                     return cs[i];
2949                 }
2950         }
2951         return null;
2952     },
2953
2954     /**
2955      * Sorts this nodes children using the supplied sort function
2956      * @param {Function} fn
2957      * @param {Object} scope (optional)
2958      */
2959     sort : function(fn, scope){
2960         var cs = this.childNodes;
2961         var len = cs.length;
2962         if(len > 0){
2963             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2964             cs.sort(sortFn);
2965             for(var i = 0; i < len; i++){
2966                 var n = cs[i];
2967                 n.previousSibling = cs[i-1];
2968                 n.nextSibling = cs[i+1];
2969                 if(i == 0){
2970                     this.setFirstChild(n);
2971                 }
2972                 if(i == len-1){
2973                     this.setLastChild(n);
2974                 }
2975             }
2976         }
2977     },
2978
2979     /**
2980      * Returns true if this node is an ancestor (at any point) of the passed node.
2981      * @param {Node} node
2982      * @return {Boolean}
2983      */
2984     contains : function(node){
2985         return node.isAncestor(this);
2986     },
2987
2988     /**
2989      * Returns true if the passed node is an ancestor (at any point) of this node.
2990      * @param {Node} node
2991      * @return {Boolean}
2992      */
2993     isAncestor : function(node){
2994         var p = this.parentNode;
2995         while(p){
2996             if(p == node){
2997                 return true;
2998             }
2999             p = p.parentNode;
3000         }
3001         return false;
3002     },
3003
3004     toString : function(){
3005         return "[Node"+(this.id?" "+this.id:"")+"]";
3006     }
3007 });/*
3008  * Based on:
3009  * Ext JS Library 1.1.1
3010  * Copyright(c) 2006-2007, Ext JS, LLC.
3011  *
3012  * Originally Released Under LGPL - original licence link has changed is not relivant.
3013  *
3014  * Fork - LGPL
3015  * <script type="text/javascript">
3016  */
3017
3018
3019 /**
3020  * @class Roo.Shadow
3021  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3022  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3023  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3024  * @constructor
3025  * Create a new Shadow
3026  * @param {Object} config The config object
3027  */
3028 Roo.Shadow = function(config){
3029     Roo.apply(this, config);
3030     if(typeof this.mode != "string"){
3031         this.mode = this.defaultMode;
3032     }
3033     var o = this.offset, a = {h: 0};
3034     var rad = Math.floor(this.offset/2);
3035     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3036         case "drop":
3037             a.w = 0;
3038             a.l = a.t = o;
3039             a.t -= 1;
3040             if(Roo.isIE){
3041                 a.l -= this.offset + rad;
3042                 a.t -= this.offset + rad;
3043                 a.w -= rad;
3044                 a.h -= rad;
3045                 a.t += 1;
3046             }
3047         break;
3048         case "sides":
3049             a.w = (o*2);
3050             a.l = -o;
3051             a.t = o-1;
3052             if(Roo.isIE){
3053                 a.l -= (this.offset - rad);
3054                 a.t -= this.offset + rad;
3055                 a.l += 1;
3056                 a.w -= (this.offset - rad)*2;
3057                 a.w -= rad + 1;
3058                 a.h -= 1;
3059             }
3060         break;
3061         case "frame":
3062             a.w = a.h = (o*2);
3063             a.l = a.t = -o;
3064             a.t += 1;
3065             a.h -= 2;
3066             if(Roo.isIE){
3067                 a.l -= (this.offset - rad);
3068                 a.t -= (this.offset - rad);
3069                 a.l += 1;
3070                 a.w -= (this.offset + rad + 1);
3071                 a.h -= (this.offset + rad);
3072                 a.h += 1;
3073             }
3074         break;
3075     };
3076
3077     this.adjusts = a;
3078 };
3079
3080 Roo.Shadow.prototype = {
3081     /**
3082      * @cfg {String} mode
3083      * The shadow display mode.  Supports the following options:<br />
3084      * sides: Shadow displays on both sides and bottom only<br />
3085      * frame: Shadow displays equally on all four sides<br />
3086      * drop: Traditional bottom-right drop shadow (default)
3087      */
3088     mode: false,
3089     /**
3090      * @cfg {String} offset
3091      * The number of pixels to offset the shadow from the element (defaults to 4)
3092      */
3093     offset: 4,
3094
3095     // private
3096     defaultMode: "drop",
3097
3098     /**
3099      * Displays the shadow under the target element
3100      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3101      */
3102     show : function(target){
3103         target = Roo.get(target);
3104         if(!this.el){
3105             this.el = Roo.Shadow.Pool.pull();
3106             if(this.el.dom.nextSibling != target.dom){
3107                 this.el.insertBefore(target);
3108             }
3109         }
3110         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3111         if(Roo.isIE){
3112             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3113         }
3114         this.realign(
3115             target.getLeft(true),
3116             target.getTop(true),
3117             target.getWidth(),
3118             target.getHeight()
3119         );
3120         this.el.dom.style.display = "block";
3121     },
3122
3123     /**
3124      * Returns true if the shadow is visible, else false
3125      */
3126     isVisible : function(){
3127         return this.el ? true : false;  
3128     },
3129
3130     /**
3131      * Direct alignment when values are already available. Show must be called at least once before
3132      * calling this method to ensure it is initialized.
3133      * @param {Number} left The target element left position
3134      * @param {Number} top The target element top position
3135      * @param {Number} width The target element width
3136      * @param {Number} height The target element height
3137      */
3138     realign : function(l, t, w, h){
3139         if(!this.el){
3140             return;
3141         }
3142         var a = this.adjusts, d = this.el.dom, s = d.style;
3143         var iea = 0;
3144         s.left = (l+a.l)+"px";
3145         s.top = (t+a.t)+"px";
3146         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3147  
3148         if(s.width != sws || s.height != shs){
3149             s.width = sws;
3150             s.height = shs;
3151             if(!Roo.isIE){
3152                 var cn = d.childNodes;
3153                 var sww = Math.max(0, (sw-12))+"px";
3154                 cn[0].childNodes[1].style.width = sww;
3155                 cn[1].childNodes[1].style.width = sww;
3156                 cn[2].childNodes[1].style.width = sww;
3157                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3158             }
3159         }
3160     },
3161
3162     /**
3163      * Hides this shadow
3164      */
3165     hide : function(){
3166         if(this.el){
3167             this.el.dom.style.display = "none";
3168             Roo.Shadow.Pool.push(this.el);
3169             delete this.el;
3170         }
3171     },
3172
3173     /**
3174      * Adjust the z-index of this shadow
3175      * @param {Number} zindex The new z-index
3176      */
3177     setZIndex : function(z){
3178         this.zIndex = z;
3179         if(this.el){
3180             this.el.setStyle("z-index", z);
3181         }
3182     }
3183 };
3184
3185 // Private utility class that manages the internal Shadow cache
3186 Roo.Shadow.Pool = function(){
3187     var p = [];
3188     var markup = Roo.isIE ?
3189                  '<div class="x-ie-shadow"></div>' :
3190                  '<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>';
3191     return {
3192         pull : function(){
3193             var sh = p.shift();
3194             if(!sh){
3195                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3196                 sh.autoBoxAdjust = false;
3197             }
3198             return sh;
3199         },
3200
3201         push : function(sh){
3202             p.push(sh);
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215
3216
3217 /**
3218  * @class Roo.SplitBar
3219  * @extends Roo.util.Observable
3220  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3221  * <br><br>
3222  * Usage:
3223  * <pre><code>
3224 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3225                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3226 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3227 split.minSize = 100;
3228 split.maxSize = 600;
3229 split.animate = true;
3230 split.on('moved', splitterMoved);
3231 </code></pre>
3232  * @constructor
3233  * Create a new SplitBar
3234  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3235  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3236  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3237  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3238                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3239                         position of the SplitBar).
3240  */
3241 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3242     
3243     /** @private */
3244     this.el = Roo.get(dragElement, true);
3245     this.el.dom.unselectable = "on";
3246     /** @private */
3247     this.resizingEl = Roo.get(resizingElement, true);
3248
3249     /**
3250      * @private
3251      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3252      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3253      * @type Number
3254      */
3255     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3256     
3257     /**
3258      * The minimum size of the resizing element. (Defaults to 0)
3259      * @type Number
3260      */
3261     this.minSize = 0;
3262     
3263     /**
3264      * The maximum size of the resizing element. (Defaults to 2000)
3265      * @type Number
3266      */
3267     this.maxSize = 2000;
3268     
3269     /**
3270      * Whether to animate the transition to the new size
3271      * @type Boolean
3272      */
3273     this.animate = false;
3274     
3275     /**
3276      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3277      * @type Boolean
3278      */
3279     this.useShim = false;
3280     
3281     /** @private */
3282     this.shim = null;
3283     
3284     if(!existingProxy){
3285         /** @private */
3286         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3287     }else{
3288         this.proxy = Roo.get(existingProxy).dom;
3289     }
3290     /** @private */
3291     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3292     
3293     /** @private */
3294     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3295     
3296     /** @private */
3297     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3298     
3299     /** @private */
3300     this.dragSpecs = {};
3301     
3302     /**
3303      * @private The adapter to use to positon and resize elements
3304      */
3305     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3306     this.adapter.init(this);
3307     
3308     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3309         /** @private */
3310         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3311         this.el.addClass("x-splitbar-h");
3312     }else{
3313         /** @private */
3314         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3315         this.el.addClass("x-splitbar-v");
3316     }
3317     
3318     this.addEvents({
3319         /**
3320          * @event resize
3321          * Fires when the splitter is moved (alias for {@link #event-moved})
3322          * @param {Roo.SplitBar} this
3323          * @param {Number} newSize the new width or height
3324          */
3325         "resize" : true,
3326         /**
3327          * @event moved
3328          * Fires when the splitter is moved
3329          * @param {Roo.SplitBar} this
3330          * @param {Number} newSize the new width or height
3331          */
3332         "moved" : true,
3333         /**
3334          * @event beforeresize
3335          * Fires before the splitter is dragged
3336          * @param {Roo.SplitBar} this
3337          */
3338         "beforeresize" : true,
3339
3340         "beforeapply" : true
3341     });
3342
3343     Roo.util.Observable.call(this);
3344 };
3345
3346 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3347     onStartProxyDrag : function(x, y){
3348         this.fireEvent("beforeresize", this);
3349         if(!this.overlay){
3350             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3351             o.unselectable();
3352             o.enableDisplayMode("block");
3353             // all splitbars share the same overlay
3354             Roo.SplitBar.prototype.overlay = o;
3355         }
3356         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3357         this.overlay.show();
3358         Roo.get(this.proxy).setDisplayed("block");
3359         var size = this.adapter.getElementSize(this);
3360         this.activeMinSize = this.getMinimumSize();;
3361         this.activeMaxSize = this.getMaximumSize();;
3362         var c1 = size - this.activeMinSize;
3363         var c2 = Math.max(this.activeMaxSize - size, 0);
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             this.dd.resetConstraints();
3366             this.dd.setXConstraint(
3367                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3368                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3369             );
3370             this.dd.setYConstraint(0, 0);
3371         }else{
3372             this.dd.resetConstraints();
3373             this.dd.setXConstraint(0, 0);
3374             this.dd.setYConstraint(
3375                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3376                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3377             );
3378          }
3379         this.dragSpecs.startSize = size;
3380         this.dragSpecs.startPoint = [x, y];
3381         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3382     },
3383     
3384     /** 
3385      * @private Called after the drag operation by the DDProxy
3386      */
3387     onEndProxyDrag : function(e){
3388         Roo.get(this.proxy).setDisplayed(false);
3389         var endPoint = Roo.lib.Event.getXY(e);
3390         if(this.overlay){
3391             this.overlay.hide();
3392         }
3393         var newSize;
3394         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3395             newSize = this.dragSpecs.startSize + 
3396                 (this.placement == Roo.SplitBar.LEFT ?
3397                     endPoint[0] - this.dragSpecs.startPoint[0] :
3398                     this.dragSpecs.startPoint[0] - endPoint[0]
3399                 );
3400         }else{
3401             newSize = this.dragSpecs.startSize + 
3402                 (this.placement == Roo.SplitBar.TOP ?
3403                     endPoint[1] - this.dragSpecs.startPoint[1] :
3404                     this.dragSpecs.startPoint[1] - endPoint[1]
3405                 );
3406         }
3407         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3408         if(newSize != this.dragSpecs.startSize){
3409             if(this.fireEvent('beforeapply', this, newSize) !== false){
3410                 this.adapter.setElementSize(this, newSize);
3411                 this.fireEvent("moved", this, newSize);
3412                 this.fireEvent("resize", this, newSize);
3413             }
3414         }
3415     },
3416     
3417     /**
3418      * Get the adapter this SplitBar uses
3419      * @return The adapter object
3420      */
3421     getAdapter : function(){
3422         return this.adapter;
3423     },
3424     
3425     /**
3426      * Set the adapter this SplitBar uses
3427      * @param {Object} adapter A SplitBar adapter object
3428      */
3429     setAdapter : function(adapter){
3430         this.adapter = adapter;
3431         this.adapter.init(this);
3432     },
3433     
3434     /**
3435      * Gets the minimum size for the resizing element
3436      * @return {Number} The minimum size
3437      */
3438     getMinimumSize : function(){
3439         return this.minSize;
3440     },
3441     
3442     /**
3443      * Sets the minimum size for the resizing element
3444      * @param {Number} minSize The minimum size
3445      */
3446     setMinimumSize : function(minSize){
3447         this.minSize = minSize;
3448     },
3449     
3450     /**
3451      * Gets the maximum size for the resizing element
3452      * @return {Number} The maximum size
3453      */
3454     getMaximumSize : function(){
3455         return this.maxSize;
3456     },
3457     
3458     /**
3459      * Sets the maximum size for the resizing element
3460      * @param {Number} maxSize The maximum size
3461      */
3462     setMaximumSize : function(maxSize){
3463         this.maxSize = maxSize;
3464     },
3465     
3466     /**
3467      * Sets the initialize size for the resizing element
3468      * @param {Number} size The initial size
3469      */
3470     setCurrentSize : function(size){
3471         var oldAnimate = this.animate;
3472         this.animate = false;
3473         this.adapter.setElementSize(this, size);
3474         this.animate = oldAnimate;
3475     },
3476     
3477     /**
3478      * Destroy this splitbar. 
3479      * @param {Boolean} removeEl True to remove the element
3480      */
3481     destroy : function(removeEl){
3482         if(this.shim){
3483             this.shim.remove();
3484         }
3485         this.dd.unreg();
3486         this.proxy.parentNode.removeChild(this.proxy);
3487         if(removeEl){
3488             this.el.remove();
3489         }
3490     }
3491 });
3492
3493 /**
3494  * @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.
3495  */
3496 Roo.SplitBar.createProxy = function(dir){
3497     var proxy = new Roo.Element(document.createElement("div"));
3498     proxy.unselectable();
3499     var cls = 'x-splitbar-proxy';
3500     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3501     document.body.appendChild(proxy.dom);
3502     return proxy.dom;
3503 };
3504
3505 /** 
3506  * @class Roo.SplitBar.BasicLayoutAdapter
3507  * Default Adapter. It assumes the splitter and resizing element are not positioned
3508  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3509  */
3510 Roo.SplitBar.BasicLayoutAdapter = function(){
3511 };
3512
3513 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3514     // do nothing for now
3515     init : function(s){
3516     
3517     },
3518     /**
3519      * Called before drag operations to get the current size of the resizing element. 
3520      * @param {Roo.SplitBar} s The SplitBar using this adapter
3521      */
3522      getElementSize : function(s){
3523         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3524             return s.resizingEl.getWidth();
3525         }else{
3526             return s.resizingEl.getHeight();
3527         }
3528     },
3529     
3530     /**
3531      * Called after drag operations to set the size of the resizing element.
3532      * @param {Roo.SplitBar} s The SplitBar using this adapter
3533      * @param {Number} newSize The new size to set
3534      * @param {Function} onComplete A function to be invoked when resizing is complete
3535      */
3536     setElementSize : function(s, newSize, onComplete){
3537         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3538             if(!s.animate){
3539                 s.resizingEl.setWidth(newSize);
3540                 if(onComplete){
3541                     onComplete(s, newSize);
3542                 }
3543             }else{
3544                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3545             }
3546         }else{
3547             
3548             if(!s.animate){
3549                 s.resizingEl.setHeight(newSize);
3550                 if(onComplete){
3551                     onComplete(s, newSize);
3552                 }
3553             }else{
3554                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3555             }
3556         }
3557     }
3558 };
3559
3560 /** 
3561  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3562  * @extends Roo.SplitBar.BasicLayoutAdapter
3563  * Adapter that  moves the splitter element to align with the resized sizing element. 
3564  * Used with an absolute positioned SplitBar.
3565  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3566  * document.body, make sure you assign an id to the body element.
3567  */
3568 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3569     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3570     this.container = Roo.get(container);
3571 };
3572
3573 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3574     init : function(s){
3575         this.basic.init(s);
3576     },
3577     
3578     getElementSize : function(s){
3579         return this.basic.getElementSize(s);
3580     },
3581     
3582     setElementSize : function(s, newSize, onComplete){
3583         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3584     },
3585     
3586     moveSplitter : function(s){
3587         var yes = Roo.SplitBar;
3588         switch(s.placement){
3589             case yes.LEFT:
3590                 s.el.setX(s.resizingEl.getRight());
3591                 break;
3592             case yes.RIGHT:
3593                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3594                 break;
3595             case yes.TOP:
3596                 s.el.setY(s.resizingEl.getBottom());
3597                 break;
3598             case yes.BOTTOM:
3599                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3600                 break;
3601         }
3602     }
3603 };
3604
3605 /**
3606  * Orientation constant - Create a vertical SplitBar
3607  * @static
3608  * @type Number
3609  */
3610 Roo.SplitBar.VERTICAL = 1;
3611
3612 /**
3613  * Orientation constant - Create a horizontal SplitBar
3614  * @static
3615  * @type Number
3616  */
3617 Roo.SplitBar.HORIZONTAL = 2;
3618
3619 /**
3620  * Placement constant - The resizing element is to the left of the splitter element
3621  * @static
3622  * @type Number
3623  */
3624 Roo.SplitBar.LEFT = 1;
3625
3626 /**
3627  * Placement constant - The resizing element is to the right of the splitter element
3628  * @static
3629  * @type Number
3630  */
3631 Roo.SplitBar.RIGHT = 2;
3632
3633 /**
3634  * Placement constant - The resizing element is positioned above the splitter element
3635  * @static
3636  * @type Number
3637  */
3638 Roo.SplitBar.TOP = 3;
3639
3640 /**
3641  * Placement constant - The resizing element is positioned under splitter element
3642  * @static
3643  * @type Number
3644  */
3645 Roo.SplitBar.BOTTOM = 4;
3646 /*
3647  * Based on:
3648  * Ext JS Library 1.1.1
3649  * Copyright(c) 2006-2007, Ext JS, LLC.
3650  *
3651  * Originally Released Under LGPL - original licence link has changed is not relivant.
3652  *
3653  * Fork - LGPL
3654  * <script type="text/javascript">
3655  */
3656
3657 /**
3658  * @class Roo.View
3659  * @extends Roo.util.Observable
3660  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3661  * This class also supports single and multi selection modes. <br>
3662  * Create a data model bound view:
3663  <pre><code>
3664  var store = new Roo.data.Store(...);
3665
3666  var view = new Roo.View({
3667     el : "my-element",
3668     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3669  
3670     singleSelect: true,
3671     selectedClass: "ydataview-selected",
3672     store: store
3673  });
3674
3675  // listen for node click?
3676  view.on("click", function(vw, index, node, e){
3677  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3678  });
3679
3680  // load XML data
3681  dataModel.load("foobar.xml");
3682  </code></pre>
3683  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3684  * <br><br>
3685  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3686  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3687  * 
3688  * Note: old style constructor is still suported (container, template, config)
3689  * 
3690  * @constructor
3691  * Create a new View
3692  * @param {Object} config The config object
3693  * 
3694  */
3695 Roo.View = function(config, depreciated_tpl, depreciated_config){
3696     
3697     this.parent = false;
3698     
3699     if (typeof(depreciated_tpl) == 'undefined') {
3700         // new way.. - universal constructor.
3701         Roo.apply(this, config);
3702         this.el  = Roo.get(this.el);
3703     } else {
3704         // old format..
3705         this.el  = Roo.get(config);
3706         this.tpl = depreciated_tpl;
3707         Roo.apply(this, depreciated_config);
3708     }
3709     this.wrapEl  = this.el.wrap().wrap();
3710     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3711     
3712     
3713     if(typeof(this.tpl) == "string"){
3714         this.tpl = new Roo.Template(this.tpl);
3715     } else {
3716         // support xtype ctors..
3717         this.tpl = new Roo.factory(this.tpl, Roo);
3718     }
3719     
3720     
3721     this.tpl.compile();
3722     
3723     /** @private */
3724     this.addEvents({
3725         /**
3726          * @event beforeclick
3727          * Fires before a click is processed. Returns false to cancel the default action.
3728          * @param {Roo.View} this
3729          * @param {Number} index The index of the target node
3730          * @param {HTMLElement} node The target node
3731          * @param {Roo.EventObject} e The raw event object
3732          */
3733             "beforeclick" : true,
3734         /**
3735          * @event click
3736          * Fires when a template node is clicked.
3737          * @param {Roo.View} this
3738          * @param {Number} index The index of the target node
3739          * @param {HTMLElement} node The target node
3740          * @param {Roo.EventObject} e The raw event object
3741          */
3742             "click" : true,
3743         /**
3744          * @event dblclick
3745          * Fires when a template node is double clicked.
3746          * @param {Roo.View} this
3747          * @param {Number} index The index of the target node
3748          * @param {HTMLElement} node The target node
3749          * @param {Roo.EventObject} e The raw event object
3750          */
3751             "dblclick" : true,
3752         /**
3753          * @event contextmenu
3754          * Fires when a template node is right clicked.
3755          * @param {Roo.View} this
3756          * @param {Number} index The index of the target node
3757          * @param {HTMLElement} node The target node
3758          * @param {Roo.EventObject} e The raw event object
3759          */
3760             "contextmenu" : true,
3761         /**
3762          * @event selectionchange
3763          * Fires when the selected nodes change.
3764          * @param {Roo.View} this
3765          * @param {Array} selections Array of the selected nodes
3766          */
3767             "selectionchange" : true,
3768     
3769         /**
3770          * @event beforeselect
3771          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3772          * @param {Roo.View} this
3773          * @param {HTMLElement} node The node to be selected
3774          * @param {Array} selections Array of currently selected nodes
3775          */
3776             "beforeselect" : true,
3777         /**
3778          * @event preparedata
3779          * Fires on every row to render, to allow you to change the data.
3780          * @param {Roo.View} this
3781          * @param {Object} data to be rendered (change this)
3782          */
3783           "preparedata" : true
3784           
3785           
3786         });
3787
3788
3789
3790     this.el.on({
3791         "click": this.onClick,
3792         "dblclick": this.onDblClick,
3793         "contextmenu": this.onContextMenu,
3794         scope:this
3795     });
3796
3797     this.selections = [];
3798     this.nodes = [];
3799     this.cmp = new Roo.CompositeElementLite([]);
3800     if(this.store){
3801         this.store = Roo.factory(this.store, Roo.data);
3802         this.setStore(this.store, true);
3803     }
3804     
3805     if ( this.footer && this.footer.xtype) {
3806            
3807          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3808         
3809         this.footer.dataSource = this.store;
3810         this.footer.container = fctr;
3811         this.footer = Roo.factory(this.footer, Roo);
3812         fctr.insertFirst(this.el);
3813         
3814         // this is a bit insane - as the paging toolbar seems to detach the el..
3815 //        dom.parentNode.parentNode.parentNode
3816          // they get detached?
3817     }
3818     
3819     
3820     Roo.View.superclass.constructor.call(this);
3821     
3822     
3823 };
3824
3825 Roo.extend(Roo.View, Roo.util.Observable, {
3826     
3827      /**
3828      * @cfg {Roo.data.Store} store Data store to load data from.
3829      */
3830     store : false,
3831     
3832     /**
3833      * @cfg {String|Roo.Element} el The container element.
3834      */
3835     el : '',
3836     
3837     /**
3838      * @cfg {String|Roo.Template} tpl The template used by this View 
3839      */
3840     tpl : false,
3841     /**
3842      * @cfg {String} dataName the named area of the template to use as the data area
3843      *                          Works with domtemplates roo-name="name"
3844      */
3845     dataName: false,
3846     /**
3847      * @cfg {String} selectedClass The css class to add to selected nodes
3848      */
3849     selectedClass : "x-view-selected",
3850      /**
3851      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3852      */
3853     emptyText : "",
3854     
3855     /**
3856      * @cfg {String} text to display on mask (default Loading)
3857      */
3858     mask : false,
3859     /**
3860      * @cfg {Boolean} multiSelect Allow multiple selection
3861      */
3862     multiSelect : false,
3863     /**
3864      * @cfg {Boolean} singleSelect Allow single selection
3865      */
3866     singleSelect:  false,
3867     
3868     /**
3869      * @cfg {Boolean} toggleSelect - selecting 
3870      */
3871     toggleSelect : false,
3872     
3873     /**
3874      * @cfg {Boolean} tickable - selecting 
3875      */
3876     tickable : false,
3877     
3878     /**
3879      * Returns the element this view is bound to.
3880      * @return {Roo.Element}
3881      */
3882     getEl : function(){
3883         return this.wrapEl;
3884     },
3885     
3886     
3887
3888     /**
3889      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3890      */
3891     refresh : function(){
3892         //Roo.log('refresh');
3893         var t = this.tpl;
3894         
3895         // if we are using something like 'domtemplate', then
3896         // the what gets used is:
3897         // t.applySubtemplate(NAME, data, wrapping data..)
3898         // the outer template then get' applied with
3899         //     the store 'extra data'
3900         // and the body get's added to the
3901         //      roo-name="data" node?
3902         //      <span class='roo-tpl-{name}'></span> ?????
3903         
3904         
3905         
3906         this.clearSelections();
3907         this.el.update("");
3908         var html = [];
3909         var records = this.store.getRange();
3910         if(records.length < 1) {
3911             
3912             // is this valid??  = should it render a template??
3913             
3914             this.el.update(this.emptyText);
3915             return;
3916         }
3917         var el = this.el;
3918         if (this.dataName) {
3919             this.el.update(t.apply(this.store.meta)); //????
3920             el = this.el.child('.roo-tpl-' + this.dataName);
3921         }
3922         
3923         for(var i = 0, len = records.length; i < len; i++){
3924             var data = this.prepareData(records[i].data, i, records[i]);
3925             this.fireEvent("preparedata", this, data, i, records[i]);
3926             
3927             var d = Roo.apply({}, data);
3928             
3929             if(this.tickable){
3930                 Roo.apply(d, {'roo-id' : Roo.id()});
3931                 
3932                 var _this = this;
3933             
3934                 Roo.each(this.parent.item, function(item){
3935                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3936                         return;
3937                     }
3938                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3939                 });
3940             }
3941             
3942             html[html.length] = Roo.util.Format.trim(
3943                 this.dataName ?
3944                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3945                     t.apply(d)
3946             );
3947         }
3948         
3949         
3950         
3951         el.update(html.join(""));
3952         this.nodes = el.dom.childNodes;
3953         this.updateIndexes(0);
3954     },
3955     
3956
3957     /**
3958      * Function to override to reformat the data that is sent to
3959      * the template for each node.
3960      * DEPRICATED - use the preparedata event handler.
3961      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3962      * a JSON object for an UpdateManager bound view).
3963      */
3964     prepareData : function(data, index, record)
3965     {
3966         this.fireEvent("preparedata", this, data, index, record);
3967         return data;
3968     },
3969
3970     onUpdate : function(ds, record){
3971         // Roo.log('on update');   
3972         this.clearSelections();
3973         var index = this.store.indexOf(record);
3974         var n = this.nodes[index];
3975         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3976         n.parentNode.removeChild(n);
3977         this.updateIndexes(index, index);
3978     },
3979
3980     
3981     
3982 // --------- FIXME     
3983     onAdd : function(ds, records, index)
3984     {
3985         //Roo.log(['on Add', ds, records, index] );        
3986         this.clearSelections();
3987         if(this.nodes.length == 0){
3988             this.refresh();
3989             return;
3990         }
3991         var n = this.nodes[index];
3992         for(var i = 0, len = records.length; i < len; i++){
3993             var d = this.prepareData(records[i].data, i, records[i]);
3994             if(n){
3995                 this.tpl.insertBefore(n, d);
3996             }else{
3997                 
3998                 this.tpl.append(this.el, d);
3999             }
4000         }
4001         this.updateIndexes(index);
4002     },
4003
4004     onRemove : function(ds, record, index){
4005        // Roo.log('onRemove');
4006         this.clearSelections();
4007         var el = this.dataName  ?
4008             this.el.child('.roo-tpl-' + this.dataName) :
4009             this.el; 
4010         
4011         el.dom.removeChild(this.nodes[index]);
4012         this.updateIndexes(index);
4013     },
4014
4015     /**
4016      * Refresh an individual node.
4017      * @param {Number} index
4018      */
4019     refreshNode : function(index){
4020         this.onUpdate(this.store, this.store.getAt(index));
4021     },
4022
4023     updateIndexes : function(startIndex, endIndex){
4024         var ns = this.nodes;
4025         startIndex = startIndex || 0;
4026         endIndex = endIndex || ns.length - 1;
4027         for(var i = startIndex; i <= endIndex; i++){
4028             ns[i].nodeIndex = i;
4029         }
4030     },
4031
4032     /**
4033      * Changes the data store this view uses and refresh the view.
4034      * @param {Store} store
4035      */
4036     setStore : function(store, initial){
4037         if(!initial && this.store){
4038             this.store.un("datachanged", this.refresh);
4039             this.store.un("add", this.onAdd);
4040             this.store.un("remove", this.onRemove);
4041             this.store.un("update", this.onUpdate);
4042             this.store.un("clear", this.refresh);
4043             this.store.un("beforeload", this.onBeforeLoad);
4044             this.store.un("load", this.onLoad);
4045             this.store.un("loadexception", this.onLoad);
4046         }
4047         if(store){
4048           
4049             store.on("datachanged", this.refresh, this);
4050             store.on("add", this.onAdd, this);
4051             store.on("remove", this.onRemove, this);
4052             store.on("update", this.onUpdate, this);
4053             store.on("clear", this.refresh, this);
4054             store.on("beforeload", this.onBeforeLoad, this);
4055             store.on("load", this.onLoad, this);
4056             store.on("loadexception", this.onLoad, this);
4057         }
4058         
4059         if(store){
4060             this.refresh();
4061         }
4062     },
4063     /**
4064      * onbeforeLoad - masks the loading area.
4065      *
4066      */
4067     onBeforeLoad : function(store,opts)
4068     {
4069          //Roo.log('onBeforeLoad');   
4070         if (!opts.add) {
4071             this.el.update("");
4072         }
4073         this.el.mask(this.mask ? this.mask : "Loading" ); 
4074     },
4075     onLoad : function ()
4076     {
4077         this.el.unmask();
4078     },
4079     
4080
4081     /**
4082      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4083      * @param {HTMLElement} node
4084      * @return {HTMLElement} The template node
4085      */
4086     findItemFromChild : function(node){
4087         var el = this.dataName  ?
4088             this.el.child('.roo-tpl-' + this.dataName,true) :
4089             this.el.dom; 
4090         
4091         if(!node || node.parentNode == el){
4092                     return node;
4093             }
4094             var p = node.parentNode;
4095             while(p && p != el){
4096             if(p.parentNode == el){
4097                 return p;
4098             }
4099             p = p.parentNode;
4100         }
4101             return null;
4102     },
4103
4104     /** @ignore */
4105     onClick : function(e){
4106         var item = this.findItemFromChild(e.getTarget());
4107         if(item){
4108             var index = this.indexOf(item);
4109             if(this.onItemClick(item, index, e) !== false){
4110                 this.fireEvent("click", this, index, item, e);
4111             }
4112         }else{
4113             this.clearSelections();
4114         }
4115     },
4116
4117     /** @ignore */
4118     onContextMenu : function(e){
4119         var item = this.findItemFromChild(e.getTarget());
4120         if(item){
4121             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4122         }
4123     },
4124
4125     /** @ignore */
4126     onDblClick : function(e){
4127         var item = this.findItemFromChild(e.getTarget());
4128         if(item){
4129             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4130         }
4131     },
4132
4133     onItemClick : function(item, index, e)
4134     {
4135         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4136             return false;
4137         }
4138         if (this.toggleSelect) {
4139             var m = this.isSelected(item) ? 'unselect' : 'select';
4140             //Roo.log(m);
4141             var _t = this;
4142             _t[m](item, true, false);
4143             return true;
4144         }
4145         if(this.multiSelect || this.singleSelect){
4146             if(this.multiSelect && e.shiftKey && this.lastSelection){
4147                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4148             }else{
4149                 this.select(item, this.multiSelect && e.ctrlKey);
4150                 this.lastSelection = item;
4151             }
4152             
4153             if(!this.tickable){
4154                 e.preventDefault();
4155             }
4156             
4157         }
4158         return true;
4159     },
4160
4161     /**
4162      * Get the number of selected nodes.
4163      * @return {Number}
4164      */
4165     getSelectionCount : function(){
4166         return this.selections.length;
4167     },
4168
4169     /**
4170      * Get the currently selected nodes.
4171      * @return {Array} An array of HTMLElements
4172      */
4173     getSelectedNodes : function(){
4174         return this.selections;
4175     },
4176
4177     /**
4178      * Get the indexes of the selected nodes.
4179      * @return {Array}
4180      */
4181     getSelectedIndexes : function(){
4182         var indexes = [], s = this.selections;
4183         for(var i = 0, len = s.length; i < len; i++){
4184             indexes.push(s[i].nodeIndex);
4185         }
4186         return indexes;
4187     },
4188
4189     /**
4190      * Clear all selections
4191      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4192      */
4193     clearSelections : function(suppressEvent){
4194         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4195             this.cmp.elements = this.selections;
4196             this.cmp.removeClass(this.selectedClass);
4197             this.selections = [];
4198             if(!suppressEvent){
4199                 this.fireEvent("selectionchange", this, this.selections);
4200             }
4201         }
4202     },
4203
4204     /**
4205      * Returns true if the passed node is selected
4206      * @param {HTMLElement/Number} node The node or node index
4207      * @return {Boolean}
4208      */
4209     isSelected : function(node){
4210         var s = this.selections;
4211         if(s.length < 1){
4212             return false;
4213         }
4214         node = this.getNode(node);
4215         return s.indexOf(node) !== -1;
4216     },
4217
4218     /**
4219      * Selects nodes.
4220      * @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
4221      * @param {Boolean} keepExisting (optional) true to keep existing selections
4222      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4223      */
4224     select : function(nodeInfo, keepExisting, suppressEvent){
4225         if(nodeInfo instanceof Array){
4226             if(!keepExisting){
4227                 this.clearSelections(true);
4228             }
4229             for(var i = 0, len = nodeInfo.length; i < len; i++){
4230                 this.select(nodeInfo[i], true, true);
4231             }
4232             return;
4233         } 
4234         var node = this.getNode(nodeInfo);
4235         if(!node || this.isSelected(node)){
4236             return; // already selected.
4237         }
4238         if(!keepExisting){
4239             this.clearSelections(true);
4240         }
4241         
4242         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4243             Roo.fly(node).addClass(this.selectedClass);
4244             this.selections.push(node);
4245             if(!suppressEvent){
4246                 this.fireEvent("selectionchange", this, this.selections);
4247             }
4248         }
4249         
4250         
4251     },
4252       /**
4253      * Unselects nodes.
4254      * @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
4255      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4256      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4257      */
4258     unselect : function(nodeInfo, keepExisting, suppressEvent)
4259     {
4260         if(nodeInfo instanceof Array){
4261             Roo.each(this.selections, function(s) {
4262                 this.unselect(s, nodeInfo);
4263             }, this);
4264             return;
4265         }
4266         var node = this.getNode(nodeInfo);
4267         if(!node || !this.isSelected(node)){
4268             //Roo.log("not selected");
4269             return; // not selected.
4270         }
4271         // fireevent???
4272         var ns = [];
4273         Roo.each(this.selections, function(s) {
4274             if (s == node ) {
4275                 Roo.fly(node).removeClass(this.selectedClass);
4276
4277                 return;
4278             }
4279             ns.push(s);
4280         },this);
4281         
4282         this.selections= ns;
4283         this.fireEvent("selectionchange", this, this.selections);
4284     },
4285
4286     /**
4287      * Gets a template node.
4288      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4289      * @return {HTMLElement} The node or null if it wasn't found
4290      */
4291     getNode : function(nodeInfo){
4292         if(typeof nodeInfo == "string"){
4293             return document.getElementById(nodeInfo);
4294         }else if(typeof nodeInfo == "number"){
4295             return this.nodes[nodeInfo];
4296         }
4297         return nodeInfo;
4298     },
4299
4300     /**
4301      * Gets a range template nodes.
4302      * @param {Number} startIndex
4303      * @param {Number} endIndex
4304      * @return {Array} An array of nodes
4305      */
4306     getNodes : function(start, end){
4307         var ns = this.nodes;
4308         start = start || 0;
4309         end = typeof end == "undefined" ? ns.length - 1 : end;
4310         var nodes = [];
4311         if(start <= end){
4312             for(var i = start; i <= end; i++){
4313                 nodes.push(ns[i]);
4314             }
4315         } else{
4316             for(var i = start; i >= end; i--){
4317                 nodes.push(ns[i]);
4318             }
4319         }
4320         return nodes;
4321     },
4322
4323     /**
4324      * Finds the index of the passed node
4325      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4326      * @return {Number} The index of the node or -1
4327      */
4328     indexOf : function(node){
4329         node = this.getNode(node);
4330         if(typeof node.nodeIndex == "number"){
4331             return node.nodeIndex;
4332         }
4333         var ns = this.nodes;
4334         for(var i = 0, len = ns.length; i < len; i++){
4335             if(ns[i] == node){
4336                 return i;
4337             }
4338         }
4339         return -1;
4340     }
4341 });
4342 /*
4343  * Based on:
4344  * Ext JS Library 1.1.1
4345  * Copyright(c) 2006-2007, Ext JS, LLC.
4346  *
4347  * Originally Released Under LGPL - original licence link has changed is not relivant.
4348  *
4349  * Fork - LGPL
4350  * <script type="text/javascript">
4351  */
4352
4353 /**
4354  * @class Roo.JsonView
4355  * @extends Roo.View
4356  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4357 <pre><code>
4358 var view = new Roo.JsonView({
4359     container: "my-element",
4360     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4361     multiSelect: true, 
4362     jsonRoot: "data" 
4363 });
4364
4365 // listen for node click?
4366 view.on("click", function(vw, index, node, e){
4367     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4368 });
4369
4370 // direct load of JSON data
4371 view.load("foobar.php");
4372
4373 // Example from my blog list
4374 var tpl = new Roo.Template(
4375     '&lt;div class="entry"&gt;' +
4376     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4377     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4378     "&lt;/div&gt;&lt;hr /&gt;"
4379 );
4380
4381 var moreView = new Roo.JsonView({
4382     container :  "entry-list", 
4383     template : tpl,
4384     jsonRoot: "posts"
4385 });
4386 moreView.on("beforerender", this.sortEntries, this);
4387 moreView.load({
4388     url: "/blog/get-posts.php",
4389     params: "allposts=true",
4390     text: "Loading Blog Entries..."
4391 });
4392 </code></pre>
4393
4394 * Note: old code is supported with arguments : (container, template, config)
4395
4396
4397  * @constructor
4398  * Create a new JsonView
4399  * 
4400  * @param {Object} config The config object
4401  * 
4402  */
4403 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4404     
4405     
4406     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4407
4408     var um = this.el.getUpdateManager();
4409     um.setRenderer(this);
4410     um.on("update", this.onLoad, this);
4411     um.on("failure", this.onLoadException, this);
4412
4413     /**
4414      * @event beforerender
4415      * Fires before rendering of the downloaded JSON data.
4416      * @param {Roo.JsonView} this
4417      * @param {Object} data The JSON data loaded
4418      */
4419     /**
4420      * @event load
4421      * Fires when data is loaded.
4422      * @param {Roo.JsonView} this
4423      * @param {Object} data The JSON data loaded
4424      * @param {Object} response The raw Connect response object
4425      */
4426     /**
4427      * @event loadexception
4428      * Fires when loading fails.
4429      * @param {Roo.JsonView} this
4430      * @param {Object} response The raw Connect response object
4431      */
4432     this.addEvents({
4433         'beforerender' : true,
4434         'load' : true,
4435         'loadexception' : true
4436     });
4437 };
4438 Roo.extend(Roo.JsonView, Roo.View, {
4439     /**
4440      * @type {String} The root property in the loaded JSON object that contains the data
4441      */
4442     jsonRoot : "",
4443
4444     /**
4445      * Refreshes the view.
4446      */
4447     refresh : function(){
4448         this.clearSelections();
4449         this.el.update("");
4450         var html = [];
4451         var o = this.jsonData;
4452         if(o && o.length > 0){
4453             for(var i = 0, len = o.length; i < len; i++){
4454                 var data = this.prepareData(o[i], i, o);
4455                 html[html.length] = this.tpl.apply(data);
4456             }
4457         }else{
4458             html.push(this.emptyText);
4459         }
4460         this.el.update(html.join(""));
4461         this.nodes = this.el.dom.childNodes;
4462         this.updateIndexes(0);
4463     },
4464
4465     /**
4466      * 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.
4467      * @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:
4468      <pre><code>
4469      view.load({
4470          url: "your-url.php",
4471          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4472          callback: yourFunction,
4473          scope: yourObject, //(optional scope)
4474          discardUrl: false,
4475          nocache: false,
4476          text: "Loading...",
4477          timeout: 30,
4478          scripts: false
4479      });
4480      </code></pre>
4481      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4482      * 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.
4483      * @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}
4484      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4485      * @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.
4486      */
4487     load : function(){
4488         var um = this.el.getUpdateManager();
4489         um.update.apply(um, arguments);
4490     },
4491
4492     // note - render is a standard framework call...
4493     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4494     render : function(el, response){
4495         
4496         this.clearSelections();
4497         this.el.update("");
4498         var o;
4499         try{
4500             if (response != '') {
4501                 o = Roo.util.JSON.decode(response.responseText);
4502                 if(this.jsonRoot){
4503                     
4504                     o = o[this.jsonRoot];
4505                 }
4506             }
4507         } catch(e){
4508         }
4509         /**
4510          * The current JSON data or null
4511          */
4512         this.jsonData = o;
4513         this.beforeRender();
4514         this.refresh();
4515     },
4516
4517 /**
4518  * Get the number of records in the current JSON dataset
4519  * @return {Number}
4520  */
4521     getCount : function(){
4522         return this.jsonData ? this.jsonData.length : 0;
4523     },
4524
4525 /**
4526  * Returns the JSON object for the specified node(s)
4527  * @param {HTMLElement/Array} node The node or an array of nodes
4528  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4529  * you get the JSON object for the node
4530  */
4531     getNodeData : function(node){
4532         if(node instanceof Array){
4533             var data = [];
4534             for(var i = 0, len = node.length; i < len; i++){
4535                 data.push(this.getNodeData(node[i]));
4536             }
4537             return data;
4538         }
4539         return this.jsonData[this.indexOf(node)] || null;
4540     },
4541
4542     beforeRender : function(){
4543         this.snapshot = this.jsonData;
4544         if(this.sortInfo){
4545             this.sort.apply(this, this.sortInfo);
4546         }
4547         this.fireEvent("beforerender", this, this.jsonData);
4548     },
4549
4550     onLoad : function(el, o){
4551         this.fireEvent("load", this, this.jsonData, o);
4552     },
4553
4554     onLoadException : function(el, o){
4555         this.fireEvent("loadexception", this, o);
4556     },
4557
4558 /**
4559  * Filter the data by a specific property.
4560  * @param {String} property A property on your JSON objects
4561  * @param {String/RegExp} value Either string that the property values
4562  * should start with, or a RegExp to test against the property
4563  */
4564     filter : function(property, value){
4565         if(this.jsonData){
4566             var data = [];
4567             var ss = this.snapshot;
4568             if(typeof value == "string"){
4569                 var vlen = value.length;
4570                 if(vlen == 0){
4571                     this.clearFilter();
4572                     return;
4573                 }
4574                 value = value.toLowerCase();
4575                 for(var i = 0, len = ss.length; i < len; i++){
4576                     var o = ss[i];
4577                     if(o[property].substr(0, vlen).toLowerCase() == value){
4578                         data.push(o);
4579                     }
4580                 }
4581             } else if(value.exec){ // regex?
4582                 for(var i = 0, len = ss.length; i < len; i++){
4583                     var o = ss[i];
4584                     if(value.test(o[property])){
4585                         data.push(o);
4586                     }
4587                 }
4588             } else{
4589                 return;
4590             }
4591             this.jsonData = data;
4592             this.refresh();
4593         }
4594     },
4595
4596 /**
4597  * Filter by a function. The passed function will be called with each
4598  * object in the current dataset. If the function returns true the value is kept,
4599  * otherwise it is filtered.
4600  * @param {Function} fn
4601  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4602  */
4603     filterBy : function(fn, scope){
4604         if(this.jsonData){
4605             var data = [];
4606             var ss = this.snapshot;
4607             for(var i = 0, len = ss.length; i < len; i++){
4608                 var o = ss[i];
4609                 if(fn.call(scope || this, o)){
4610                     data.push(o);
4611                 }
4612             }
4613             this.jsonData = data;
4614             this.refresh();
4615         }
4616     },
4617
4618 /**
4619  * Clears the current filter.
4620  */
4621     clearFilter : function(){
4622         if(this.snapshot && this.jsonData != this.snapshot){
4623             this.jsonData = this.snapshot;
4624             this.refresh();
4625         }
4626     },
4627
4628
4629 /**
4630  * Sorts the data for this view and refreshes it.
4631  * @param {String} property A property on your JSON objects to sort on
4632  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4633  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4634  */
4635     sort : function(property, dir, sortType){
4636         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4637         if(this.jsonData){
4638             var p = property;
4639             var dsc = dir && dir.toLowerCase() == "desc";
4640             var f = function(o1, o2){
4641                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4642                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4643                 ;
4644                 if(v1 < v2){
4645                     return dsc ? +1 : -1;
4646                 } else if(v1 > v2){
4647                     return dsc ? -1 : +1;
4648                 } else{
4649                     return 0;
4650                 }
4651             };
4652             this.jsonData.sort(f);
4653             this.refresh();
4654             if(this.jsonData != this.snapshot){
4655                 this.snapshot.sort(f);
4656             }
4657         }
4658     }
4659 });/*
4660  * Based on:
4661  * Ext JS Library 1.1.1
4662  * Copyright(c) 2006-2007, Ext JS, LLC.
4663  *
4664  * Originally Released Under LGPL - original licence link has changed is not relivant.
4665  *
4666  * Fork - LGPL
4667  * <script type="text/javascript">
4668  */
4669  
4670
4671 /**
4672  * @class Roo.ColorPalette
4673  * @extends Roo.Component
4674  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4675  * Here's an example of typical usage:
4676  * <pre><code>
4677 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4678 cp.render('my-div');
4679
4680 cp.on('select', function(palette, selColor){
4681     // do something with selColor
4682 });
4683 </code></pre>
4684  * @constructor
4685  * Create a new ColorPalette
4686  * @param {Object} config The config object
4687  */
4688 Roo.ColorPalette = function(config){
4689     Roo.ColorPalette.superclass.constructor.call(this, config);
4690     this.addEvents({
4691         /**
4692              * @event select
4693              * Fires when a color is selected
4694              * @param {ColorPalette} this
4695              * @param {String} color The 6-digit color hex code (without the # symbol)
4696              */
4697         select: true
4698     });
4699
4700     if(this.handler){
4701         this.on("select", this.handler, this.scope, true);
4702     }
4703 };
4704 Roo.extend(Roo.ColorPalette, Roo.Component, {
4705     /**
4706      * @cfg {String} itemCls
4707      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4708      */
4709     itemCls : "x-color-palette",
4710     /**
4711      * @cfg {String} value
4712      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4713      * the hex codes are case-sensitive.
4714      */
4715     value : null,
4716     clickEvent:'click',
4717     // private
4718     ctype: "Roo.ColorPalette",
4719
4720     /**
4721      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4722      */
4723     allowReselect : false,
4724
4725     /**
4726      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4727      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4728      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4729      * of colors with the width setting until the box is symmetrical.</p>
4730      * <p>You can override individual colors if needed:</p>
4731      * <pre><code>
4732 var cp = new Roo.ColorPalette();
4733 cp.colors[0] = "FF0000";  // change the first box to red
4734 </code></pre>
4735
4736 Or you can provide a custom array of your own for complete control:
4737 <pre><code>
4738 var cp = new Roo.ColorPalette();
4739 cp.colors = ["000000", "993300", "333300"];
4740 </code></pre>
4741      * @type Array
4742      */
4743     colors : [
4744         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4745         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4746         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4747         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4748         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4749     ],
4750
4751     // private
4752     onRender : function(container, position){
4753         var t = new Roo.MasterTemplate(
4754             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4755         );
4756         var c = this.colors;
4757         for(var i = 0, len = c.length; i < len; i++){
4758             t.add([c[i]]);
4759         }
4760         var el = document.createElement("div");
4761         el.className = this.itemCls;
4762         t.overwrite(el);
4763         container.dom.insertBefore(el, position);
4764         this.el = Roo.get(el);
4765         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4766         if(this.clickEvent != 'click'){
4767             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4768         }
4769     },
4770
4771     // private
4772     afterRender : function(){
4773         Roo.ColorPalette.superclass.afterRender.call(this);
4774         if(this.value){
4775             var s = this.value;
4776             this.value = null;
4777             this.select(s);
4778         }
4779     },
4780
4781     // private
4782     handleClick : function(e, t){
4783         e.preventDefault();
4784         if(!this.disabled){
4785             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4786             this.select(c.toUpperCase());
4787         }
4788     },
4789
4790     /**
4791      * Selects the specified color in the palette (fires the select event)
4792      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4793      */
4794     select : function(color){
4795         color = color.replace("#", "");
4796         if(color != this.value || this.allowReselect){
4797             var el = this.el;
4798             if(this.value){
4799                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4800             }
4801             el.child("a.color-"+color).addClass("x-color-palette-sel");
4802             this.value = color;
4803             this.fireEvent("select", this, color);
4804         }
4805     }
4806 });/*
4807  * Based on:
4808  * Ext JS Library 1.1.1
4809  * Copyright(c) 2006-2007, Ext JS, LLC.
4810  *
4811  * Originally Released Under LGPL - original licence link has changed is not relivant.
4812  *
4813  * Fork - LGPL
4814  * <script type="text/javascript">
4815  */
4816  
4817 /**
4818  * @class Roo.DatePicker
4819  * @extends Roo.Component
4820  * Simple date picker class.
4821  * @constructor
4822  * Create a new DatePicker
4823  * @param {Object} config The config object
4824  */
4825 Roo.DatePicker = function(config){
4826     Roo.DatePicker.superclass.constructor.call(this, config);
4827
4828     this.value = config && config.value ?
4829                  config.value.clearTime() : new Date().clearTime();
4830
4831     this.addEvents({
4832         /**
4833              * @event select
4834              * Fires when a date is selected
4835              * @param {DatePicker} this
4836              * @param {Date} date The selected date
4837              */
4838         'select': true,
4839         /**
4840              * @event monthchange
4841              * Fires when the displayed month changes 
4842              * @param {DatePicker} this
4843              * @param {Date} date The selected month
4844              */
4845         'monthchange': true
4846     });
4847
4848     if(this.handler){
4849         this.on("select", this.handler,  this.scope || this);
4850     }
4851     // build the disabledDatesRE
4852     if(!this.disabledDatesRE && this.disabledDates){
4853         var dd = this.disabledDates;
4854         var re = "(?:";
4855         for(var i = 0; i < dd.length; i++){
4856             re += dd[i];
4857             if(i != dd.length-1) {
4858                 re += "|";
4859             }
4860         }
4861         this.disabledDatesRE = new RegExp(re + ")");
4862     }
4863 };
4864
4865 Roo.extend(Roo.DatePicker, Roo.Component, {
4866     /**
4867      * @cfg {String} todayText
4868      * The text to display on the button that selects the current date (defaults to "Today")
4869      */
4870     todayText : "Today",
4871     /**
4872      * @cfg {String} okText
4873      * The text to display on the ok button
4874      */
4875     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4876     /**
4877      * @cfg {String} cancelText
4878      * The text to display on the cancel button
4879      */
4880     cancelText : "Cancel",
4881     /**
4882      * @cfg {String} todayTip
4883      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4884      */
4885     todayTip : "{0} (Spacebar)",
4886     /**
4887      * @cfg {Date} minDate
4888      * Minimum allowable date (JavaScript date object, defaults to null)
4889      */
4890     minDate : null,
4891     /**
4892      * @cfg {Date} maxDate
4893      * Maximum allowable date (JavaScript date object, defaults to null)
4894      */
4895     maxDate : null,
4896     /**
4897      * @cfg {String} minText
4898      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4899      */
4900     minText : "This date is before the minimum date",
4901     /**
4902      * @cfg {String} maxText
4903      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4904      */
4905     maxText : "This date is after the maximum date",
4906     /**
4907      * @cfg {String} format
4908      * The default date format string which can be overriden for localization support.  The format must be
4909      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4910      */
4911     format : "m/d/y",
4912     /**
4913      * @cfg {Array} disabledDays
4914      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4915      */
4916     disabledDays : null,
4917     /**
4918      * @cfg {String} disabledDaysText
4919      * The tooltip to display when the date falls on a disabled day (defaults to "")
4920      */
4921     disabledDaysText : "",
4922     /**
4923      * @cfg {RegExp} disabledDatesRE
4924      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4925      */
4926     disabledDatesRE : null,
4927     /**
4928      * @cfg {String} disabledDatesText
4929      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4930      */
4931     disabledDatesText : "",
4932     /**
4933      * @cfg {Boolean} constrainToViewport
4934      * True to constrain the date picker to the viewport (defaults to true)
4935      */
4936     constrainToViewport : true,
4937     /**
4938      * @cfg {Array} monthNames
4939      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4940      */
4941     monthNames : Date.monthNames,
4942     /**
4943      * @cfg {Array} dayNames
4944      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4945      */
4946     dayNames : Date.dayNames,
4947     /**
4948      * @cfg {String} nextText
4949      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4950      */
4951     nextText: 'Next Month (Control+Right)',
4952     /**
4953      * @cfg {String} prevText
4954      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4955      */
4956     prevText: 'Previous Month (Control+Left)',
4957     /**
4958      * @cfg {String} monthYearText
4959      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4960      */
4961     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4962     /**
4963      * @cfg {Number} startDay
4964      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4965      */
4966     startDay : 0,
4967     /**
4968      * @cfg {Bool} showClear
4969      * Show a clear button (usefull for date form elements that can be blank.)
4970      */
4971     
4972     showClear: false,
4973     
4974     /**
4975      * Sets the value of the date field
4976      * @param {Date} value The date to set
4977      */
4978     setValue : function(value){
4979         var old = this.value;
4980         
4981         if (typeof(value) == 'string') {
4982          
4983             value = Date.parseDate(value, this.format);
4984         }
4985         if (!value) {
4986             value = new Date();
4987         }
4988         
4989         this.value = value.clearTime(true);
4990         if(this.el){
4991             this.update(this.value);
4992         }
4993     },
4994
4995     /**
4996      * Gets the current selected value of the date field
4997      * @return {Date} The selected date
4998      */
4999     getValue : function(){
5000         return this.value;
5001     },
5002
5003     // private
5004     focus : function(){
5005         if(this.el){
5006             this.update(this.activeDate);
5007         }
5008     },
5009
5010     // privateval
5011     onRender : function(container, position){
5012         
5013         var m = [
5014              '<table cellspacing="0">',
5015                 '<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>',
5016                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5017         var dn = this.dayNames;
5018         for(var i = 0; i < 7; i++){
5019             var d = this.startDay+i;
5020             if(d > 6){
5021                 d = d-7;
5022             }
5023             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5024         }
5025         m[m.length] = "</tr></thead><tbody><tr>";
5026         for(var i = 0; i < 42; i++) {
5027             if(i % 7 == 0 && i != 0){
5028                 m[m.length] = "</tr><tr>";
5029             }
5030             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5031         }
5032         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5033             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5034
5035         var el = document.createElement("div");
5036         el.className = "x-date-picker";
5037         el.innerHTML = m.join("");
5038
5039         container.dom.insertBefore(el, position);
5040
5041         this.el = Roo.get(el);
5042         this.eventEl = Roo.get(el.firstChild);
5043
5044         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5045             handler: this.showPrevMonth,
5046             scope: this,
5047             preventDefault:true,
5048             stopDefault:true
5049         });
5050
5051         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5052             handler: this.showNextMonth,
5053             scope: this,
5054             preventDefault:true,
5055             stopDefault:true
5056         });
5057
5058         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5059
5060         this.monthPicker = this.el.down('div.x-date-mp');
5061         this.monthPicker.enableDisplayMode('block');
5062         
5063         var kn = new Roo.KeyNav(this.eventEl, {
5064             "left" : function(e){
5065                 e.ctrlKey ?
5066                     this.showPrevMonth() :
5067                     this.update(this.activeDate.add("d", -1));
5068             },
5069
5070             "right" : function(e){
5071                 e.ctrlKey ?
5072                     this.showNextMonth() :
5073                     this.update(this.activeDate.add("d", 1));
5074             },
5075
5076             "up" : function(e){
5077                 e.ctrlKey ?
5078                     this.showNextYear() :
5079                     this.update(this.activeDate.add("d", -7));
5080             },
5081
5082             "down" : function(e){
5083                 e.ctrlKey ?
5084                     this.showPrevYear() :
5085                     this.update(this.activeDate.add("d", 7));
5086             },
5087
5088             "pageUp" : function(e){
5089                 this.showNextMonth();
5090             },
5091
5092             "pageDown" : function(e){
5093                 this.showPrevMonth();
5094             },
5095
5096             "enter" : function(e){
5097                 e.stopPropagation();
5098                 return true;
5099             },
5100
5101             scope : this
5102         });
5103
5104         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5105
5106         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5107
5108         this.el.unselectable();
5109         
5110         this.cells = this.el.select("table.x-date-inner tbody td");
5111         this.textNodes = this.el.query("table.x-date-inner tbody span");
5112
5113         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5114             text: "&#160;",
5115             tooltip: this.monthYearText
5116         });
5117
5118         this.mbtn.on('click', this.showMonthPicker, this);
5119         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5120
5121
5122         var today = (new Date()).dateFormat(this.format);
5123         
5124         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5125         if (this.showClear) {
5126             baseTb.add( new Roo.Toolbar.Fill());
5127         }
5128         baseTb.add({
5129             text: String.format(this.todayText, today),
5130             tooltip: String.format(this.todayTip, today),
5131             handler: this.selectToday,
5132             scope: this
5133         });
5134         
5135         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5136             
5137         //});
5138         if (this.showClear) {
5139             
5140             baseTb.add( new Roo.Toolbar.Fill());
5141             baseTb.add({
5142                 text: '&#160;',
5143                 cls: 'x-btn-icon x-btn-clear',
5144                 handler: function() {
5145                     //this.value = '';
5146                     this.fireEvent("select", this, '');
5147                 },
5148                 scope: this
5149             });
5150         }
5151         
5152         
5153         if(Roo.isIE){
5154             this.el.repaint();
5155         }
5156         this.update(this.value);
5157     },
5158
5159     createMonthPicker : function(){
5160         if(!this.monthPicker.dom.firstChild){
5161             var buf = ['<table border="0" cellspacing="0">'];
5162             for(var i = 0; i < 6; i++){
5163                 buf.push(
5164                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5165                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5166                     i == 0 ?
5167                     '<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>' :
5168                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5169                 );
5170             }
5171             buf.push(
5172                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5173                     this.okText,
5174                     '</button><button type="button" class="x-date-mp-cancel">',
5175                     this.cancelText,
5176                     '</button></td></tr>',
5177                 '</table>'
5178             );
5179             this.monthPicker.update(buf.join(''));
5180             this.monthPicker.on('click', this.onMonthClick, this);
5181             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5182
5183             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5184             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5185
5186             this.mpMonths.each(function(m, a, i){
5187                 i += 1;
5188                 if((i%2) == 0){
5189                     m.dom.xmonth = 5 + Math.round(i * .5);
5190                 }else{
5191                     m.dom.xmonth = Math.round((i-1) * .5);
5192                 }
5193             });
5194         }
5195     },
5196
5197     showMonthPicker : function(){
5198         this.createMonthPicker();
5199         var size = this.el.getSize();
5200         this.monthPicker.setSize(size);
5201         this.monthPicker.child('table').setSize(size);
5202
5203         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5204         this.updateMPMonth(this.mpSelMonth);
5205         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5206         this.updateMPYear(this.mpSelYear);
5207
5208         this.monthPicker.slideIn('t', {duration:.2});
5209     },
5210
5211     updateMPYear : function(y){
5212         this.mpyear = y;
5213         var ys = this.mpYears.elements;
5214         for(var i = 1; i <= 10; i++){
5215             var td = ys[i-1], y2;
5216             if((i%2) == 0){
5217                 y2 = y + Math.round(i * .5);
5218                 td.firstChild.innerHTML = y2;
5219                 td.xyear = y2;
5220             }else{
5221                 y2 = y - (5-Math.round(i * .5));
5222                 td.firstChild.innerHTML = y2;
5223                 td.xyear = y2;
5224             }
5225             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5226         }
5227     },
5228
5229     updateMPMonth : function(sm){
5230         this.mpMonths.each(function(m, a, i){
5231             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5232         });
5233     },
5234
5235     selectMPMonth: function(m){
5236         
5237     },
5238
5239     onMonthClick : function(e, t){
5240         e.stopEvent();
5241         var el = new Roo.Element(t), pn;
5242         if(el.is('button.x-date-mp-cancel')){
5243             this.hideMonthPicker();
5244         }
5245         else if(el.is('button.x-date-mp-ok')){
5246             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5247             this.hideMonthPicker();
5248         }
5249         else if(pn = el.up('td.x-date-mp-month', 2)){
5250             this.mpMonths.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelMonth = pn.dom.xmonth;
5253         }
5254         else if(pn = el.up('td.x-date-mp-year', 2)){
5255             this.mpYears.removeClass('x-date-mp-sel');
5256             pn.addClass('x-date-mp-sel');
5257             this.mpSelYear = pn.dom.xyear;
5258         }
5259         else if(el.is('a.x-date-mp-prev')){
5260             this.updateMPYear(this.mpyear-10);
5261         }
5262         else if(el.is('a.x-date-mp-next')){
5263             this.updateMPYear(this.mpyear+10);
5264         }
5265     },
5266
5267     onMonthDblClick : function(e, t){
5268         e.stopEvent();
5269         var el = new Roo.Element(t), pn;
5270         if(pn = el.up('td.x-date-mp-month', 2)){
5271             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5272             this.hideMonthPicker();
5273         }
5274         else if(pn = el.up('td.x-date-mp-year', 2)){
5275             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5276             this.hideMonthPicker();
5277         }
5278     },
5279
5280     hideMonthPicker : function(disableAnim){
5281         if(this.monthPicker){
5282             if(disableAnim === true){
5283                 this.monthPicker.hide();
5284             }else{
5285                 this.monthPicker.slideOut('t', {duration:.2});
5286             }
5287         }
5288     },
5289
5290     // private
5291     showPrevMonth : function(e){
5292         this.update(this.activeDate.add("mo", -1));
5293     },
5294
5295     // private
5296     showNextMonth : function(e){
5297         this.update(this.activeDate.add("mo", 1));
5298     },
5299
5300     // private
5301     showPrevYear : function(){
5302         this.update(this.activeDate.add("y", -1));
5303     },
5304
5305     // private
5306     showNextYear : function(){
5307         this.update(this.activeDate.add("y", 1));
5308     },
5309
5310     // private
5311     handleMouseWheel : function(e){
5312         var delta = e.getWheelDelta();
5313         if(delta > 0){
5314             this.showPrevMonth();
5315             e.stopEvent();
5316         } else if(delta < 0){
5317             this.showNextMonth();
5318             e.stopEvent();
5319         }
5320     },
5321
5322     // private
5323     handleDateClick : function(e, t){
5324         e.stopEvent();
5325         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5326             this.setValue(new Date(t.dateValue));
5327             this.fireEvent("select", this, this.value);
5328         }
5329     },
5330
5331     // private
5332     selectToday : function(){
5333         this.setValue(new Date().clearTime());
5334         this.fireEvent("select", this, this.value);
5335     },
5336
5337     // private
5338     update : function(date)
5339     {
5340         var vd = this.activeDate;
5341         this.activeDate = date;
5342         if(vd && this.el){
5343             var t = date.getTime();
5344             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5345                 this.cells.removeClass("x-date-selected");
5346                 this.cells.each(function(c){
5347                    if(c.dom.firstChild.dateValue == t){
5348                        c.addClass("x-date-selected");
5349                        setTimeout(function(){
5350                             try{c.dom.firstChild.focus();}catch(e){}
5351                        }, 50);
5352                        return false;
5353                    }
5354                 });
5355                 return;
5356             }
5357         }
5358         
5359         var days = date.getDaysInMonth();
5360         var firstOfMonth = date.getFirstDateOfMonth();
5361         var startingPos = firstOfMonth.getDay()-this.startDay;
5362
5363         if(startingPos <= this.startDay){
5364             startingPos += 7;
5365         }
5366
5367         var pm = date.add("mo", -1);
5368         var prevStart = pm.getDaysInMonth()-startingPos;
5369
5370         var cells = this.cells.elements;
5371         var textEls = this.textNodes;
5372         days += startingPos;
5373
5374         // convert everything to numbers so it's fast
5375         var day = 86400000;
5376         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5377         var today = new Date().clearTime().getTime();
5378         var sel = date.clearTime().getTime();
5379         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5380         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5381         var ddMatch = this.disabledDatesRE;
5382         var ddText = this.disabledDatesText;
5383         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5384         var ddaysText = this.disabledDaysText;
5385         var format = this.format;
5386
5387         var setCellClass = function(cal, cell){
5388             cell.title = "";
5389             var t = d.getTime();
5390             cell.firstChild.dateValue = t;
5391             if(t == today){
5392                 cell.className += " x-date-today";
5393                 cell.title = cal.todayText;
5394             }
5395             if(t == sel){
5396                 cell.className += " x-date-selected";
5397                 setTimeout(function(){
5398                     try{cell.firstChild.focus();}catch(e){}
5399                 }, 50);
5400             }
5401             // disabling
5402             if(t < min) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.minText;
5405                 return;
5406             }
5407             if(t > max) {
5408                 cell.className = " x-date-disabled";
5409                 cell.title = cal.maxText;
5410                 return;
5411             }
5412             if(ddays){
5413                 if(ddays.indexOf(d.getDay()) != -1){
5414                     cell.title = ddaysText;
5415                     cell.className = " x-date-disabled";
5416                 }
5417             }
5418             if(ddMatch && format){
5419                 var fvalue = d.dateFormat(format);
5420                 if(ddMatch.test(fvalue)){
5421                     cell.title = ddText.replace("%0", fvalue);
5422                     cell.className = " x-date-disabled";
5423                 }
5424             }
5425         };
5426
5427         var i = 0;
5428         for(; i < startingPos; i++) {
5429             textEls[i].innerHTML = (++prevStart);
5430             d.setDate(d.getDate()+1);
5431             cells[i].className = "x-date-prevday";
5432             setCellClass(this, cells[i]);
5433         }
5434         for(; i < days; i++){
5435             intDay = i - startingPos + 1;
5436             textEls[i].innerHTML = (intDay);
5437             d.setDate(d.getDate()+1);
5438             cells[i].className = "x-date-active";
5439             setCellClass(this, cells[i]);
5440         }
5441         var extraDays = 0;
5442         for(; i < 42; i++) {
5443              textEls[i].innerHTML = (++extraDays);
5444              d.setDate(d.getDate()+1);
5445              cells[i].className = "x-date-nextday";
5446              setCellClass(this, cells[i]);
5447         }
5448
5449         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5450         this.fireEvent('monthchange', this, date);
5451         
5452         if(!this.internalRender){
5453             var main = this.el.dom.firstChild;
5454             var w = main.offsetWidth;
5455             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5456             Roo.fly(main).setWidth(w);
5457             this.internalRender = true;
5458             // opera does not respect the auto grow header center column
5459             // then, after it gets a width opera refuses to recalculate
5460             // without a second pass
5461             if(Roo.isOpera && !this.secondPass){
5462                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5463                 this.secondPass = true;
5464                 this.update.defer(10, this, [date]);
5465             }
5466         }
5467         
5468         
5469     }
5470 });        /*
5471  * Based on:
5472  * Ext JS Library 1.1.1
5473  * Copyright(c) 2006-2007, Ext JS, LLC.
5474  *
5475  * Originally Released Under LGPL - original licence link has changed is not relivant.
5476  *
5477  * Fork - LGPL
5478  * <script type="text/javascript">
5479  */
5480 /**
5481  * @class Roo.TabPanel
5482  * @extends Roo.util.Observable
5483  * A lightweight tab container.
5484  * <br><br>
5485  * Usage:
5486  * <pre><code>
5487 // basic tabs 1, built from existing content
5488 var tabs = new Roo.TabPanel("tabs1");
5489 tabs.addTab("script", "View Script");
5490 tabs.addTab("markup", "View Markup");
5491 tabs.activate("script");
5492
5493 // more advanced tabs, built from javascript
5494 var jtabs = new Roo.TabPanel("jtabs");
5495 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5496
5497 // set up the UpdateManager
5498 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5499 var updater = tab2.getUpdateManager();
5500 updater.setDefaultUrl("ajax1.htm");
5501 tab2.on('activate', updater.refresh, updater, true);
5502
5503 // Use setUrl for Ajax loading
5504 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5505 tab3.setUrl("ajax2.htm", null, true);
5506
5507 // Disabled tab
5508 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5509 tab4.disable();
5510
5511 jtabs.activate("jtabs-1");
5512  * </code></pre>
5513  * @constructor
5514  * Create a new TabPanel.
5515  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5516  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5517  */
5518 Roo.TabPanel = function(container, config){
5519     /**
5520     * The container element for this TabPanel.
5521     * @type Roo.Element
5522     */
5523     this.el = Roo.get(container, true);
5524     if(config){
5525         if(typeof config == "boolean"){
5526             this.tabPosition = config ? "bottom" : "top";
5527         }else{
5528             Roo.apply(this, config);
5529         }
5530     }
5531     if(this.tabPosition == "bottom"){
5532         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5533         this.el.addClass("x-tabs-bottom");
5534     }
5535     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5536     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5537     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5538     if(Roo.isIE){
5539         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5540     }
5541     if(this.tabPosition != "bottom"){
5542         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5543          * @type Roo.Element
5544          */
5545         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5546         this.el.addClass("x-tabs-top");
5547     }
5548     this.items = [];
5549
5550     this.bodyEl.setStyle("position", "relative");
5551
5552     this.active = null;
5553     this.activateDelegate = this.activate.createDelegate(this);
5554
5555     this.addEvents({
5556         /**
5557          * @event tabchange
5558          * Fires when the active tab changes
5559          * @param {Roo.TabPanel} this
5560          * @param {Roo.TabPanelItem} activePanel The new active tab
5561          */
5562         "tabchange": true,
5563         /**
5564          * @event beforetabchange
5565          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5566          * @param {Roo.TabPanel} this
5567          * @param {Object} e Set cancel to true on this object to cancel the tab change
5568          * @param {Roo.TabPanelItem} tab The tab being changed to
5569          */
5570         "beforetabchange" : true
5571     });
5572
5573     Roo.EventManager.onWindowResize(this.onResize, this);
5574     this.cpad = this.el.getPadding("lr");
5575     this.hiddenCount = 0;
5576
5577
5578     // toolbar on the tabbar support...
5579     if (this.toolbar) {
5580         var tcfg = this.toolbar;
5581         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5582         this.toolbar = new Roo.Toolbar(tcfg);
5583         if (Roo.isSafari) {
5584             var tbl = tcfg.container.child('table', true);
5585             tbl.setAttribute('width', '100%');
5586         }
5587         
5588     }
5589    
5590
5591
5592     Roo.TabPanel.superclass.constructor.call(this);
5593 };
5594
5595 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5596     /*
5597      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5598      */
5599     tabPosition : "top",
5600     /*
5601      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5602      */
5603     currentTabWidth : 0,
5604     /*
5605      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5606      */
5607     minTabWidth : 40,
5608     /*
5609      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5610      */
5611     maxTabWidth : 250,
5612     /*
5613      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5614      */
5615     preferredTabWidth : 175,
5616     /*
5617      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5618      */
5619     resizeTabs : false,
5620     /*
5621      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5622      */
5623     monitorResize : true,
5624     /*
5625      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5626      */
5627     toolbar : false,
5628
5629     /**
5630      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5631      * @param {String} id The id of the div to use <b>or create</b>
5632      * @param {String} text The text for the tab
5633      * @param {String} content (optional) Content to put in the TabPanelItem body
5634      * @param {Boolean} closable (optional) True to create a close icon on the tab
5635      * @return {Roo.TabPanelItem} The created TabPanelItem
5636      */
5637     addTab : function(id, text, content, closable){
5638         var item = new Roo.TabPanelItem(this, id, text, closable);
5639         this.addTabItem(item);
5640         if(content){
5641             item.setContent(content);
5642         }
5643         return item;
5644     },
5645
5646     /**
5647      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5648      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5649      * @return {Roo.TabPanelItem}
5650      */
5651     getTab : function(id){
5652         return this.items[id];
5653     },
5654
5655     /**
5656      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5657      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5658      */
5659     hideTab : function(id){
5660         var t = this.items[id];
5661         if(!t.isHidden()){
5662            t.setHidden(true);
5663            this.hiddenCount++;
5664            this.autoSizeTabs();
5665         }
5666     },
5667
5668     /**
5669      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5670      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5671      */
5672     unhideTab : function(id){
5673         var t = this.items[id];
5674         if(t.isHidden()){
5675            t.setHidden(false);
5676            this.hiddenCount--;
5677            this.autoSizeTabs();
5678         }
5679     },
5680
5681     /**
5682      * Adds an existing {@link Roo.TabPanelItem}.
5683      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5684      */
5685     addTabItem : function(item){
5686         this.items[item.id] = item;
5687         this.items.push(item);
5688         if(this.resizeTabs){
5689            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5690            this.autoSizeTabs();
5691         }else{
5692             item.autoSize();
5693         }
5694     },
5695
5696     /**
5697      * Removes a {@link Roo.TabPanelItem}.
5698      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5699      */
5700     removeTab : function(id){
5701         var items = this.items;
5702         var tab = items[id];
5703         if(!tab) { return; }
5704         var index = items.indexOf(tab);
5705         if(this.active == tab && items.length > 1){
5706             var newTab = this.getNextAvailable(index);
5707             if(newTab) {
5708                 newTab.activate();
5709             }
5710         }
5711         this.stripEl.dom.removeChild(tab.pnode.dom);
5712         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5713             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5714         }
5715         items.splice(index, 1);
5716         delete this.items[tab.id];
5717         tab.fireEvent("close", tab);
5718         tab.purgeListeners();
5719         this.autoSizeTabs();
5720     },
5721
5722     getNextAvailable : function(start){
5723         var items = this.items;
5724         var index = start;
5725         // look for a next tab that will slide over to
5726         // replace the one being removed
5727         while(index < items.length){
5728             var item = items[++index];
5729             if(item && !item.isHidden()){
5730                 return item;
5731             }
5732         }
5733         // if one isn't found select the previous tab (on the left)
5734         index = start;
5735         while(index >= 0){
5736             var item = items[--index];
5737             if(item && !item.isHidden()){
5738                 return item;
5739             }
5740         }
5741         return null;
5742     },
5743
5744     /**
5745      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5746      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5747      */
5748     disableTab : function(id){
5749         var tab = this.items[id];
5750         if(tab && this.active != tab){
5751             tab.disable();
5752         }
5753     },
5754
5755     /**
5756      * Enables a {@link Roo.TabPanelItem} that is disabled.
5757      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5758      */
5759     enableTab : function(id){
5760         var tab = this.items[id];
5761         tab.enable();
5762     },
5763
5764     /**
5765      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5766      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5767      * @return {Roo.TabPanelItem} The TabPanelItem.
5768      */
5769     activate : function(id){
5770         var tab = this.items[id];
5771         if(!tab){
5772             return null;
5773         }
5774         if(tab == this.active || tab.disabled){
5775             return tab;
5776         }
5777         var e = {};
5778         this.fireEvent("beforetabchange", this, e, tab);
5779         if(e.cancel !== true && !tab.disabled){
5780             if(this.active){
5781                 this.active.hide();
5782             }
5783             this.active = this.items[id];
5784             this.active.show();
5785             this.fireEvent("tabchange", this, this.active);
5786         }
5787         return tab;
5788     },
5789
5790     /**
5791      * Gets the active {@link Roo.TabPanelItem}.
5792      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5793      */
5794     getActiveTab : function(){
5795         return this.active;
5796     },
5797
5798     /**
5799      * Updates the tab body element to fit the height of the container element
5800      * for overflow scrolling
5801      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5802      */
5803     syncHeight : function(targetHeight){
5804         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5805         var bm = this.bodyEl.getMargins();
5806         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5807         this.bodyEl.setHeight(newHeight);
5808         return newHeight;
5809     },
5810
5811     onResize : function(){
5812         if(this.monitorResize){
5813             this.autoSizeTabs();
5814         }
5815     },
5816
5817     /**
5818      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5819      */
5820     beginUpdate : function(){
5821         this.updating = true;
5822     },
5823
5824     /**
5825      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5826      */
5827     endUpdate : function(){
5828         this.updating = false;
5829         this.autoSizeTabs();
5830     },
5831
5832     /**
5833      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5834      */
5835     autoSizeTabs : function(){
5836         var count = this.items.length;
5837         var vcount = count - this.hiddenCount;
5838         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5839             return;
5840         }
5841         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5842         var availWidth = Math.floor(w / vcount);
5843         var b = this.stripBody;
5844         if(b.getWidth() > w){
5845             var tabs = this.items;
5846             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5847             if(availWidth < this.minTabWidth){
5848                 /*if(!this.sleft){    // incomplete scrolling code
5849                     this.createScrollButtons();
5850                 }
5851                 this.showScroll();
5852                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5853             }
5854         }else{
5855             if(this.currentTabWidth < this.preferredTabWidth){
5856                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5857             }
5858         }
5859     },
5860
5861     /**
5862      * Returns the number of tabs in this TabPanel.
5863      * @return {Number}
5864      */
5865      getCount : function(){
5866          return this.items.length;
5867      },
5868
5869     /**
5870      * Resizes all the tabs to the passed width
5871      * @param {Number} The new width
5872      */
5873     setTabWidth : function(width){
5874         this.currentTabWidth = width;
5875         for(var i = 0, len = this.items.length; i < len; i++) {
5876                 if(!this.items[i].isHidden()) {
5877                 this.items[i].setWidth(width);
5878             }
5879         }
5880     },
5881
5882     /**
5883      * Destroys this TabPanel
5884      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5885      */
5886     destroy : function(removeEl){
5887         Roo.EventManager.removeResizeListener(this.onResize, this);
5888         for(var i = 0, len = this.items.length; i < len; i++){
5889             this.items[i].purgeListeners();
5890         }
5891         if(removeEl === true){
5892             this.el.update("");
5893             this.el.remove();
5894         }
5895     }
5896 });
5897
5898 /**
5899  * @class Roo.TabPanelItem
5900  * @extends Roo.util.Observable
5901  * Represents an individual item (tab plus body) in a TabPanel.
5902  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5903  * @param {String} id The id of this TabPanelItem
5904  * @param {String} text The text for the tab of this TabPanelItem
5905  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5906  */
5907 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5908     /**
5909      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5910      * @type Roo.TabPanel
5911      */
5912     this.tabPanel = tabPanel;
5913     /**
5914      * The id for this TabPanelItem
5915      * @type String
5916      */
5917     this.id = id;
5918     /** @private */
5919     this.disabled = false;
5920     /** @private */
5921     this.text = text;
5922     /** @private */
5923     this.loaded = false;
5924     this.closable = closable;
5925
5926     /**
5927      * The body element for this TabPanelItem.
5928      * @type Roo.Element
5929      */
5930     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5931     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5932     this.bodyEl.setStyle("display", "block");
5933     this.bodyEl.setStyle("zoom", "1");
5934     this.hideAction();
5935
5936     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5937     /** @private */
5938     this.el = Roo.get(els.el, true);
5939     this.inner = Roo.get(els.inner, true);
5940     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5941     this.pnode = Roo.get(els.el.parentNode, true);
5942     this.el.on("mousedown", this.onTabMouseDown, this);
5943     this.el.on("click", this.onTabClick, this);
5944     /** @private */
5945     if(closable){
5946         var c = Roo.get(els.close, true);
5947         c.dom.title = this.closeText;
5948         c.addClassOnOver("close-over");
5949         c.on("click", this.closeClick, this);
5950      }
5951
5952     this.addEvents({
5953          /**
5954          * @event activate
5955          * Fires when this tab becomes the active tab.
5956          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5957          * @param {Roo.TabPanelItem} this
5958          */
5959         "activate": true,
5960         /**
5961          * @event beforeclose
5962          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5963          * @param {Roo.TabPanelItem} this
5964          * @param {Object} e Set cancel to true on this object to cancel the close.
5965          */
5966         "beforeclose": true,
5967         /**
5968          * @event close
5969          * Fires when this tab is closed.
5970          * @param {Roo.TabPanelItem} this
5971          */
5972          "close": true,
5973         /**
5974          * @event deactivate
5975          * Fires when this tab is no longer the active tab.
5976          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5977          * @param {Roo.TabPanelItem} this
5978          */
5979          "deactivate" : true
5980     });
5981     this.hidden = false;
5982
5983     Roo.TabPanelItem.superclass.constructor.call(this);
5984 };
5985
5986 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5987     purgeListeners : function(){
5988        Roo.util.Observable.prototype.purgeListeners.call(this);
5989        this.el.removeAllListeners();
5990     },
5991     /**
5992      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5993      */
5994     show : function(){
5995         this.pnode.addClass("on");
5996         this.showAction();
5997         if(Roo.isOpera){
5998             this.tabPanel.stripWrap.repaint();
5999         }
6000         this.fireEvent("activate", this.tabPanel, this);
6001     },
6002
6003     /**
6004      * Returns true if this tab is the active tab.
6005      * @return {Boolean}
6006      */
6007     isActive : function(){
6008         return this.tabPanel.getActiveTab() == this;
6009     },
6010
6011     /**
6012      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6013      */
6014     hide : function(){
6015         this.pnode.removeClass("on");
6016         this.hideAction();
6017         this.fireEvent("deactivate", this.tabPanel, this);
6018     },
6019
6020     hideAction : function(){
6021         this.bodyEl.hide();
6022         this.bodyEl.setStyle("position", "absolute");
6023         this.bodyEl.setLeft("-20000px");
6024         this.bodyEl.setTop("-20000px");
6025     },
6026
6027     showAction : function(){
6028         this.bodyEl.setStyle("position", "relative");
6029         this.bodyEl.setTop("");
6030         this.bodyEl.setLeft("");
6031         this.bodyEl.show();
6032     },
6033
6034     /**
6035      * Set the tooltip for the tab.
6036      * @param {String} tooltip The tab's tooltip
6037      */
6038     setTooltip : function(text){
6039         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6040             this.textEl.dom.qtip = text;
6041             this.textEl.dom.removeAttribute('title');
6042         }else{
6043             this.textEl.dom.title = text;
6044         }
6045     },
6046
6047     onTabClick : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     onTabMouseDown : function(e){
6053         e.preventDefault();
6054         this.tabPanel.activate(this.id);
6055     },
6056
6057     getWidth : function(){
6058         return this.inner.getWidth();
6059     },
6060
6061     setWidth : function(width){
6062         var iwidth = width - this.pnode.getPadding("lr");
6063         this.inner.setWidth(iwidth);
6064         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6065         this.pnode.setWidth(width);
6066     },
6067
6068     /**
6069      * Show or hide the tab
6070      * @param {Boolean} hidden True to hide or false to show.
6071      */
6072     setHidden : function(hidden){
6073         this.hidden = hidden;
6074         this.pnode.setStyle("display", hidden ? "none" : "");
6075     },
6076
6077     /**
6078      * Returns true if this tab is "hidden"
6079      * @return {Boolean}
6080      */
6081     isHidden : function(){
6082         return this.hidden;
6083     },
6084
6085     /**
6086      * Returns the text for this tab
6087      * @return {String}
6088      */
6089     getText : function(){
6090         return this.text;
6091     },
6092
6093     autoSize : function(){
6094         //this.el.beginMeasure();
6095         this.textEl.setWidth(1);
6096         /*
6097          *  #2804 [new] Tabs in Roojs
6098          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6099          */
6100         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6101         //this.el.endMeasure();
6102     },
6103
6104     /**
6105      * Sets the text for the tab (Note: this also sets the tooltip text)
6106      * @param {String} text The tab's text and tooltip
6107      */
6108     setText : function(text){
6109         this.text = text;
6110         this.textEl.update(text);
6111         this.setTooltip(text);
6112         if(!this.tabPanel.resizeTabs){
6113             this.autoSize();
6114         }
6115     },
6116     /**
6117      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6118      */
6119     activate : function(){
6120         this.tabPanel.activate(this.id);
6121     },
6122
6123     /**
6124      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6125      */
6126     disable : function(){
6127         if(this.tabPanel.active != this){
6128             this.disabled = true;
6129             this.pnode.addClass("disabled");
6130         }
6131     },
6132
6133     /**
6134      * Enables this TabPanelItem if it was previously disabled.
6135      */
6136     enable : function(){
6137         this.disabled = false;
6138         this.pnode.removeClass("disabled");
6139     },
6140
6141     /**
6142      * Sets the content for this TabPanelItem.
6143      * @param {String} content The content
6144      * @param {Boolean} loadScripts true to look for and load scripts
6145      */
6146     setContent : function(content, loadScripts){
6147         this.bodyEl.update(content, loadScripts);
6148     },
6149
6150     /**
6151      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6152      * @return {Roo.UpdateManager} The UpdateManager
6153      */
6154     getUpdateManager : function(){
6155         return this.bodyEl.getUpdateManager();
6156     },
6157
6158     /**
6159      * Set a URL to be used to load the content for this TabPanelItem.
6160      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6161      * @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)
6162      * @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)
6163      * @return {Roo.UpdateManager} The UpdateManager
6164      */
6165     setUrl : function(url, params, loadOnce){
6166         if(this.refreshDelegate){
6167             this.un('activate', this.refreshDelegate);
6168         }
6169         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6170         this.on("activate", this.refreshDelegate);
6171         return this.bodyEl.getUpdateManager();
6172     },
6173
6174     /** @private */
6175     _handleRefresh : function(url, params, loadOnce){
6176         if(!loadOnce || !this.loaded){
6177             var updater = this.bodyEl.getUpdateManager();
6178             updater.update(url, params, this._setLoaded.createDelegate(this));
6179         }
6180     },
6181
6182     /**
6183      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6184      *   Will fail silently if the setUrl method has not been called.
6185      *   This does not activate the panel, just updates its content.
6186      */
6187     refresh : function(){
6188         if(this.refreshDelegate){
6189            this.loaded = false;
6190            this.refreshDelegate();
6191         }
6192     },
6193
6194     /** @private */
6195     _setLoaded : function(){
6196         this.loaded = true;
6197     },
6198
6199     /** @private */
6200     closeClick : function(e){
6201         var o = {};
6202         e.stopEvent();
6203         this.fireEvent("beforeclose", this, o);
6204         if(o.cancel !== true){
6205             this.tabPanel.removeTab(this.id);
6206         }
6207     },
6208     /**
6209      * The text displayed in the tooltip for the close icon.
6210      * @type String
6211      */
6212     closeText : "Close this tab"
6213 });
6214
6215 /** @private */
6216 Roo.TabPanel.prototype.createStrip = function(container){
6217     var strip = document.createElement("div");
6218     strip.className = "x-tabs-wrap";
6219     container.appendChild(strip);
6220     return strip;
6221 };
6222 /** @private */
6223 Roo.TabPanel.prototype.createStripList = function(strip){
6224     // div wrapper for retard IE
6225     // returns the "tr" element.
6226     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6227         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6228         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6229     return strip.firstChild.firstChild.firstChild.firstChild;
6230 };
6231 /** @private */
6232 Roo.TabPanel.prototype.createBody = function(container){
6233     var body = document.createElement("div");
6234     Roo.id(body, "tab-body");
6235     Roo.fly(body).addClass("x-tabs-body");
6236     container.appendChild(body);
6237     return body;
6238 };
6239 /** @private */
6240 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6241     var body = Roo.getDom(id);
6242     if(!body){
6243         body = document.createElement("div");
6244         body.id = id;
6245     }
6246     Roo.fly(body).addClass("x-tabs-item-body");
6247     bodyEl.insertBefore(body, bodyEl.firstChild);
6248     return body;
6249 };
6250 /** @private */
6251 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6252     var td = document.createElement("td");
6253     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6254     //stripEl.appendChild(td);
6255     if(closable){
6256         td.className = "x-tabs-closable";
6257         if(!this.closeTpl){
6258             this.closeTpl = new Roo.Template(
6259                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6260                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6261                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6262             );
6263         }
6264         var el = this.closeTpl.overwrite(td, {"text": text});
6265         var close = el.getElementsByTagName("div")[0];
6266         var inner = el.getElementsByTagName("em")[0];
6267         return {"el": el, "close": close, "inner": inner};
6268     } else {
6269         if(!this.tabTpl){
6270             this.tabTpl = new Roo.Template(
6271                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6272                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6273             );
6274         }
6275         var el = this.tabTpl.overwrite(td, {"text": text});
6276         var inner = el.getElementsByTagName("em")[0];
6277         return {"el": el, "inner": inner};
6278     }
6279 };/*
6280  * Based on:
6281  * Ext JS Library 1.1.1
6282  * Copyright(c) 2006-2007, Ext JS, LLC.
6283  *
6284  * Originally Released Under LGPL - original licence link has changed is not relivant.
6285  *
6286  * Fork - LGPL
6287  * <script type="text/javascript">
6288  */
6289
6290 /**
6291  * @class Roo.Button
6292  * @extends Roo.util.Observable
6293  * Simple Button class
6294  * @cfg {String} text The button text
6295  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6296  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6297  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6298  * @cfg {Object} scope The scope of the handler
6299  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6300  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6301  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6302  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6303  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6304  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6305    applies if enableToggle = true)
6306  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6307  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6308   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6309  * @constructor
6310  * Create a new button
6311  * @param {Object} config The config object
6312  */
6313 Roo.Button = function(renderTo, config)
6314 {
6315     if (!config) {
6316         config = renderTo;
6317         renderTo = config.renderTo || false;
6318     }
6319     
6320     Roo.apply(this, config);
6321     this.addEvents({
6322         /**
6323              * @event click
6324              * Fires when this button is clicked
6325              * @param {Button} this
6326              * @param {EventObject} e The click event
6327              */
6328             "click" : true,
6329         /**
6330              * @event toggle
6331              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6332              * @param {Button} this
6333              * @param {Boolean} pressed
6334              */
6335             "toggle" : true,
6336         /**
6337              * @event mouseover
6338              * Fires when the mouse hovers over the button
6339              * @param {Button} this
6340              * @param {Event} e The event object
6341              */
6342         'mouseover' : true,
6343         /**
6344              * @event mouseout
6345              * Fires when the mouse exits the button
6346              * @param {Button} this
6347              * @param {Event} e The event object
6348              */
6349         'mouseout': true,
6350          /**
6351              * @event render
6352              * Fires when the button is rendered
6353              * @param {Button} this
6354              */
6355         'render': true
6356     });
6357     if(this.menu){
6358         this.menu = Roo.menu.MenuMgr.get(this.menu);
6359     }
6360     // register listeners first!!  - so render can be captured..
6361     Roo.util.Observable.call(this);
6362     if(renderTo){
6363         this.render(renderTo);
6364     }
6365     
6366   
6367 };
6368
6369 Roo.extend(Roo.Button, Roo.util.Observable, {
6370     /**
6371      * 
6372      */
6373     
6374     /**
6375      * Read-only. True if this button is hidden
6376      * @type Boolean
6377      */
6378     hidden : false,
6379     /**
6380      * Read-only. True if this button is disabled
6381      * @type Boolean
6382      */
6383     disabled : false,
6384     /**
6385      * Read-only. True if this button is pressed (only if enableToggle = true)
6386      * @type Boolean
6387      */
6388     pressed : false,
6389
6390     /**
6391      * @cfg {Number} tabIndex 
6392      * The DOM tabIndex for this button (defaults to undefined)
6393      */
6394     tabIndex : undefined,
6395
6396     /**
6397      * @cfg {Boolean} enableToggle
6398      * True to enable pressed/not pressed toggling (defaults to false)
6399      */
6400     enableToggle: false,
6401     /**
6402      * @cfg {Roo.menu.Menu} menu
6403      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6404      */
6405     menu : undefined,
6406     /**
6407      * @cfg {String} menuAlign
6408      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6409      */
6410     menuAlign : "tl-bl?",
6411
6412     /**
6413      * @cfg {String} iconCls
6414      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6415      */
6416     iconCls : undefined,
6417     /**
6418      * @cfg {String} type
6419      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6420      */
6421     type : 'button',
6422
6423     // private
6424     menuClassTarget: 'tr',
6425
6426     /**
6427      * @cfg {String} clickEvent
6428      * The type of event to map to the button's event handler (defaults to 'click')
6429      */
6430     clickEvent : 'click',
6431
6432     /**
6433      * @cfg {Boolean} handleMouseEvents
6434      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6435      */
6436     handleMouseEvents : true,
6437
6438     /**
6439      * @cfg {String} tooltipType
6440      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6441      */
6442     tooltipType : 'qtip',
6443
6444     /**
6445      * @cfg {String} cls
6446      * A CSS class to apply to the button's main element.
6447      */
6448     
6449     /**
6450      * @cfg {Roo.Template} template (Optional)
6451      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6452      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6453      * require code modifications if required elements (e.g. a button) aren't present.
6454      */
6455
6456     // private
6457     render : function(renderTo){
6458         var btn;
6459         if(this.hideParent){
6460             this.parentEl = Roo.get(renderTo);
6461         }
6462         if(!this.dhconfig){
6463             if(!this.template){
6464                 if(!Roo.Button.buttonTemplate){
6465                     // hideous table template
6466                     Roo.Button.buttonTemplate = new Roo.Template(
6467                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6468                         '<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>',
6469                         "</tr></tbody></table>");
6470                 }
6471                 this.template = Roo.Button.buttonTemplate;
6472             }
6473             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6474             var btnEl = btn.child("button:first");
6475             btnEl.on('focus', this.onFocus, this);
6476             btnEl.on('blur', this.onBlur, this);
6477             if(this.cls){
6478                 btn.addClass(this.cls);
6479             }
6480             if(this.icon){
6481                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6482             }
6483             if(this.iconCls){
6484                 btnEl.addClass(this.iconCls);
6485                 if(!this.cls){
6486                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6487                 }
6488             }
6489             if(this.tabIndex !== undefined){
6490                 btnEl.dom.tabIndex = this.tabIndex;
6491             }
6492             if(this.tooltip){
6493                 if(typeof this.tooltip == 'object'){
6494                     Roo.QuickTips.tips(Roo.apply({
6495                           target: btnEl.id
6496                     }, this.tooltip));
6497                 } else {
6498                     btnEl.dom[this.tooltipType] = this.tooltip;
6499                 }
6500             }
6501         }else{
6502             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6503         }
6504         this.el = btn;
6505         if(this.id){
6506             this.el.dom.id = this.el.id = this.id;
6507         }
6508         if(this.menu){
6509             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6510             this.menu.on("show", this.onMenuShow, this);
6511             this.menu.on("hide", this.onMenuHide, this);
6512         }
6513         btn.addClass("x-btn");
6514         if(Roo.isIE && !Roo.isIE7){
6515             this.autoWidth.defer(1, this);
6516         }else{
6517             this.autoWidth();
6518         }
6519         if(this.handleMouseEvents){
6520             btn.on("mouseover", this.onMouseOver, this);
6521             btn.on("mouseout", this.onMouseOut, this);
6522             btn.on("mousedown", this.onMouseDown, this);
6523         }
6524         btn.on(this.clickEvent, this.onClick, this);
6525         //btn.on("mouseup", this.onMouseUp, this);
6526         if(this.hidden){
6527             this.hide();
6528         }
6529         if(this.disabled){
6530             this.disable();
6531         }
6532         Roo.ButtonToggleMgr.register(this);
6533         if(this.pressed){
6534             this.el.addClass("x-btn-pressed");
6535         }
6536         if(this.repeat){
6537             var repeater = new Roo.util.ClickRepeater(btn,
6538                 typeof this.repeat == "object" ? this.repeat : {}
6539             );
6540             repeater.on("click", this.onClick,  this);
6541         }
6542         
6543         this.fireEvent('render', this);
6544         
6545     },
6546     /**
6547      * Returns the button's underlying element
6548      * @return {Roo.Element} The element
6549      */
6550     getEl : function(){
6551         return this.el;  
6552     },
6553     
6554     /**
6555      * Destroys this Button and removes any listeners.
6556      */
6557     destroy : function(){
6558         Roo.ButtonToggleMgr.unregister(this);
6559         this.el.removeAllListeners();
6560         this.purgeListeners();
6561         this.el.remove();
6562     },
6563
6564     // private
6565     autoWidth : function(){
6566         if(this.el){
6567             this.el.setWidth("auto");
6568             if(Roo.isIE7 && Roo.isStrict){
6569                 var ib = this.el.child('button');
6570                 if(ib && ib.getWidth() > 20){
6571                     ib.clip();
6572                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6573                 }
6574             }
6575             if(this.minWidth){
6576                 if(this.hidden){
6577                     this.el.beginMeasure();
6578                 }
6579                 if(this.el.getWidth() < this.minWidth){
6580                     this.el.setWidth(this.minWidth);
6581                 }
6582                 if(this.hidden){
6583                     this.el.endMeasure();
6584                 }
6585             }
6586         }
6587     },
6588
6589     /**
6590      * Assigns this button's click handler
6591      * @param {Function} handler The function to call when the button is clicked
6592      * @param {Object} scope (optional) Scope for the function passed in
6593      */
6594     setHandler : function(handler, scope){
6595         this.handler = handler;
6596         this.scope = scope;  
6597     },
6598     
6599     /**
6600      * Sets this button's text
6601      * @param {String} text The button text
6602      */
6603     setText : function(text){
6604         this.text = text;
6605         if(this.el){
6606             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6607         }
6608         this.autoWidth();
6609     },
6610     
6611     /**
6612      * Gets the text for this button
6613      * @return {String} The button text
6614      */
6615     getText : function(){
6616         return this.text;  
6617     },
6618     
6619     /**
6620      * Show this button
6621      */
6622     show: function(){
6623         this.hidden = false;
6624         if(this.el){
6625             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6626         }
6627     },
6628     
6629     /**
6630      * Hide this button
6631      */
6632     hide: function(){
6633         this.hidden = true;
6634         if(this.el){
6635             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6636         }
6637     },
6638     
6639     /**
6640      * Convenience function for boolean show/hide
6641      * @param {Boolean} visible True to show, false to hide
6642      */
6643     setVisible: function(visible){
6644         if(visible) {
6645             this.show();
6646         }else{
6647             this.hide();
6648         }
6649     },
6650     /**
6651          * Similar to toggle, but does not trigger event.
6652          * @param {Boolean} state [required] Force a particular state
6653          */
6654         setPressed : function(state)
6655         {
6656             if(state != this.pressed){
6657             if(state){
6658                 this.el.addClass("x-btn-pressed");
6659                 this.pressed = true;
6660             }else{
6661                 this.el.removeClass("x-btn-pressed");
6662                 this.pressed = false;
6663             }
6664         }
6665         },
6666         
6667     /**
6668      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6669      * @param {Boolean} state (optional) Force a particular state
6670      */
6671     toggle : function(state){
6672         state = state === undefined ? !this.pressed : state;
6673         if(state != this.pressed){
6674             if(state){
6675                 this.el.addClass("x-btn-pressed");
6676                 this.pressed = true;
6677                 this.fireEvent("toggle", this, true);
6678             }else{
6679                 this.el.removeClass("x-btn-pressed");
6680                 this.pressed = false;
6681                 this.fireEvent("toggle", this, false);
6682             }
6683             if(this.toggleHandler){
6684                 this.toggleHandler.call(this.scope || this, this, state);
6685             }
6686         }
6687     },
6688     
6689         
6690         
6691     /**
6692      * Focus the button
6693      */
6694     focus : function(){
6695         this.el.child('button:first').focus();
6696     },
6697     
6698     /**
6699      * Disable this button
6700      */
6701     disable : function(){
6702         if(this.el){
6703             this.el.addClass("x-btn-disabled");
6704         }
6705         this.disabled = true;
6706     },
6707     
6708     /**
6709      * Enable this button
6710      */
6711     enable : function(){
6712         if(this.el){
6713             this.el.removeClass("x-btn-disabled");
6714         }
6715         this.disabled = false;
6716     },
6717
6718     /**
6719      * Convenience function for boolean enable/disable
6720      * @param {Boolean} enabled True to enable, false to disable
6721      */
6722     setDisabled : function(v){
6723         this[v !== true ? "enable" : "disable"]();
6724     },
6725
6726     // private
6727     onClick : function(e)
6728     {
6729         if(e){
6730             e.preventDefault();
6731         }
6732         if(e.button != 0){
6733             return;
6734         }
6735         if(!this.disabled){
6736             if(this.enableToggle){
6737                 this.toggle();
6738             }
6739             if(this.menu && !this.menu.isVisible()){
6740                 this.menu.show(this.el, this.menuAlign);
6741             }
6742             this.fireEvent("click", this, e);
6743             if(this.handler){
6744                 this.el.removeClass("x-btn-over");
6745                 this.handler.call(this.scope || this, this, e);
6746             }
6747         }
6748     },
6749     // private
6750     onMouseOver : function(e){
6751         if(!this.disabled){
6752             this.el.addClass("x-btn-over");
6753             this.fireEvent('mouseover', this, e);
6754         }
6755     },
6756     // private
6757     onMouseOut : function(e){
6758         if(!e.within(this.el,  true)){
6759             this.el.removeClass("x-btn-over");
6760             this.fireEvent('mouseout', this, e);
6761         }
6762     },
6763     // private
6764     onFocus : function(e){
6765         if(!this.disabled){
6766             this.el.addClass("x-btn-focus");
6767         }
6768     },
6769     // private
6770     onBlur : function(e){
6771         this.el.removeClass("x-btn-focus");
6772     },
6773     // private
6774     onMouseDown : function(e){
6775         if(!this.disabled && e.button == 0){
6776             this.el.addClass("x-btn-click");
6777             Roo.get(document).on('mouseup', this.onMouseUp, this);
6778         }
6779     },
6780     // private
6781     onMouseUp : function(e){
6782         if(e.button == 0){
6783             this.el.removeClass("x-btn-click");
6784             Roo.get(document).un('mouseup', this.onMouseUp, this);
6785         }
6786     },
6787     // private
6788     onMenuShow : function(e){
6789         this.el.addClass("x-btn-menu-active");
6790     },
6791     // private
6792     onMenuHide : function(e){
6793         this.el.removeClass("x-btn-menu-active");
6794     }   
6795 });
6796
6797 // Private utility class used by Button
6798 Roo.ButtonToggleMgr = function(){
6799    var groups = {};
6800    
6801    function toggleGroup(btn, state){
6802        if(state){
6803            var g = groups[btn.toggleGroup];
6804            for(var i = 0, l = g.length; i < l; i++){
6805                if(g[i] != btn){
6806                    g[i].toggle(false);
6807                }
6808            }
6809        }
6810    }
6811    
6812    return {
6813        register : function(btn){
6814            if(!btn.toggleGroup){
6815                return;
6816            }
6817            var g = groups[btn.toggleGroup];
6818            if(!g){
6819                g = groups[btn.toggleGroup] = [];
6820            }
6821            g.push(btn);
6822            btn.on("toggle", toggleGroup);
6823        },
6824        
6825        unregister : function(btn){
6826            if(!btn.toggleGroup){
6827                return;
6828            }
6829            var g = groups[btn.toggleGroup];
6830            if(g){
6831                g.remove(btn);
6832                btn.un("toggle", toggleGroup);
6833            }
6834        }
6835    };
6836 }();/*
6837  * Based on:
6838  * Ext JS Library 1.1.1
6839  * Copyright(c) 2006-2007, Ext JS, LLC.
6840  *
6841  * Originally Released Under LGPL - original licence link has changed is not relivant.
6842  *
6843  * Fork - LGPL
6844  * <script type="text/javascript">
6845  */
6846  
6847 /**
6848  * @class Roo.SplitButton
6849  * @extends Roo.Button
6850  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6851  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6852  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6853  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6854  * @cfg {String} arrowTooltip The title attribute of the arrow
6855  * @constructor
6856  * Create a new menu button
6857  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6858  * @param {Object} config The config object
6859  */
6860 Roo.SplitButton = function(renderTo, config){
6861     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6862     /**
6863      * @event arrowclick
6864      * Fires when this button's arrow is clicked
6865      * @param {SplitButton} this
6866      * @param {EventObject} e The click event
6867      */
6868     this.addEvents({"arrowclick":true});
6869 };
6870
6871 Roo.extend(Roo.SplitButton, Roo.Button, {
6872     render : function(renderTo){
6873         // this is one sweet looking template!
6874         var tpl = new Roo.Template(
6875             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6876             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6877             '<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>',
6878             "</tbody></table></td><td>",
6879             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6880             '<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>',
6881             "</tbody></table></td></tr></table>"
6882         );
6883         var btn = tpl.append(renderTo, [this.text, this.type], true);
6884         var btnEl = btn.child("button");
6885         if(this.cls){
6886             btn.addClass(this.cls);
6887         }
6888         if(this.icon){
6889             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6890         }
6891         if(this.iconCls){
6892             btnEl.addClass(this.iconCls);
6893             if(!this.cls){
6894                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6895             }
6896         }
6897         this.el = btn;
6898         if(this.handleMouseEvents){
6899             btn.on("mouseover", this.onMouseOver, this);
6900             btn.on("mouseout", this.onMouseOut, this);
6901             btn.on("mousedown", this.onMouseDown, this);
6902             btn.on("mouseup", this.onMouseUp, this);
6903         }
6904         btn.on(this.clickEvent, this.onClick, this);
6905         if(this.tooltip){
6906             if(typeof this.tooltip == 'object'){
6907                 Roo.QuickTips.tips(Roo.apply({
6908                       target: btnEl.id
6909                 }, this.tooltip));
6910             } else {
6911                 btnEl.dom[this.tooltipType] = this.tooltip;
6912             }
6913         }
6914         if(this.arrowTooltip){
6915             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6916         }
6917         if(this.hidden){
6918             this.hide();
6919         }
6920         if(this.disabled){
6921             this.disable();
6922         }
6923         if(this.pressed){
6924             this.el.addClass("x-btn-pressed");
6925         }
6926         if(Roo.isIE && !Roo.isIE7){
6927             this.autoWidth.defer(1, this);
6928         }else{
6929             this.autoWidth();
6930         }
6931         if(this.menu){
6932             this.menu.on("show", this.onMenuShow, this);
6933             this.menu.on("hide", this.onMenuHide, this);
6934         }
6935         this.fireEvent('render', this);
6936     },
6937
6938     // private
6939     autoWidth : function(){
6940         if(this.el){
6941             var tbl = this.el.child("table:first");
6942             var tbl2 = this.el.child("table:last");
6943             this.el.setWidth("auto");
6944             tbl.setWidth("auto");
6945             if(Roo.isIE7 && Roo.isStrict){
6946                 var ib = this.el.child('button:first');
6947                 if(ib && ib.getWidth() > 20){
6948                     ib.clip();
6949                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6950                 }
6951             }
6952             if(this.minWidth){
6953                 if(this.hidden){
6954                     this.el.beginMeasure();
6955                 }
6956                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6957                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6958                 }
6959                 if(this.hidden){
6960                     this.el.endMeasure();
6961                 }
6962             }
6963             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6964         } 
6965     },
6966     /**
6967      * Sets this button's click handler
6968      * @param {Function} handler The function to call when the button is clicked
6969      * @param {Object} scope (optional) Scope for the function passed above
6970      */
6971     setHandler : function(handler, scope){
6972         this.handler = handler;
6973         this.scope = scope;  
6974     },
6975     
6976     /**
6977      * Sets this button's arrow click handler
6978      * @param {Function} handler The function to call when the arrow is clicked
6979      * @param {Object} scope (optional) Scope for the function passed above
6980      */
6981     setArrowHandler : function(handler, scope){
6982         this.arrowHandler = handler;
6983         this.scope = scope;  
6984     },
6985     
6986     /**
6987      * Focus the button
6988      */
6989     focus : function(){
6990         if(this.el){
6991             this.el.child("button:first").focus();
6992         }
6993     },
6994
6995     // private
6996     onClick : function(e){
6997         e.preventDefault();
6998         if(!this.disabled){
6999             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7000                 if(this.menu && !this.menu.isVisible()){
7001                     this.menu.show(this.el, this.menuAlign);
7002                 }
7003                 this.fireEvent("arrowclick", this, e);
7004                 if(this.arrowHandler){
7005                     this.arrowHandler.call(this.scope || this, this, e);
7006                 }
7007             }else{
7008                 this.fireEvent("click", this, e);
7009                 if(this.handler){
7010                     this.handler.call(this.scope || this, this, e);
7011                 }
7012             }
7013         }
7014     },
7015     // private
7016     onMouseDown : function(e){
7017         if(!this.disabled){
7018             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7019         }
7020     },
7021     // private
7022     onMouseUp : function(e){
7023         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7024     }   
7025 });
7026
7027
7028 // backwards compat
7029 Roo.MenuButton = Roo.SplitButton;/*
7030  * Based on:
7031  * Ext JS Library 1.1.1
7032  * Copyright(c) 2006-2007, Ext JS, LLC.
7033  *
7034  * Originally Released Under LGPL - original licence link has changed is not relivant.
7035  *
7036  * Fork - LGPL
7037  * <script type="text/javascript">
7038  */
7039
7040 /**
7041  * @class Roo.Toolbar
7042  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7043  * Basic Toolbar class.
7044  * @constructor
7045  * Creates a new Toolbar
7046  * @param {Object} container The config object
7047  */ 
7048 Roo.Toolbar = function(container, buttons, config)
7049 {
7050     /// old consturctor format still supported..
7051     if(container instanceof Array){ // omit the container for later rendering
7052         buttons = container;
7053         config = buttons;
7054         container = null;
7055     }
7056     if (typeof(container) == 'object' && container.xtype) {
7057         config = container;
7058         container = config.container;
7059         buttons = config.buttons || []; // not really - use items!!
7060     }
7061     var xitems = [];
7062     if (config && config.items) {
7063         xitems = config.items;
7064         delete config.items;
7065     }
7066     Roo.apply(this, config);
7067     this.buttons = buttons;
7068     
7069     if(container){
7070         this.render(container);
7071     }
7072     this.xitems = xitems;
7073     Roo.each(xitems, function(b) {
7074         this.add(b);
7075     }, this);
7076     
7077 };
7078
7079 Roo.Toolbar.prototype = {
7080     /**
7081      * @cfg {Array} items
7082      * array of button configs or elements to add (will be converted to a MixedCollection)
7083      */
7084     items: false,
7085     /**
7086      * @cfg {String/HTMLElement/Element} container
7087      * The id or element that will contain the toolbar
7088      */
7089     // private
7090     render : function(ct){
7091         this.el = Roo.get(ct);
7092         if(this.cls){
7093             this.el.addClass(this.cls);
7094         }
7095         // using a table allows for vertical alignment
7096         // 100% width is needed by Safari...
7097         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7098         this.tr = this.el.child("tr", true);
7099         var autoId = 0;
7100         this.items = new Roo.util.MixedCollection(false, function(o){
7101             return o.id || ("item" + (++autoId));
7102         });
7103         if(this.buttons){
7104             this.add.apply(this, this.buttons);
7105             delete this.buttons;
7106         }
7107     },
7108
7109     /**
7110      * Adds element(s) to the toolbar -- this function takes a variable number of 
7111      * arguments of mixed type and adds them to the toolbar.
7112      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7113      * <ul>
7114      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7115      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7116      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7117      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7118      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7119      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7120      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7121      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7122      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7123      * </ul>
7124      * @param {Mixed} arg2
7125      * @param {Mixed} etc.
7126      */
7127     add : function(){
7128         var a = arguments, l = a.length;
7129         for(var i = 0; i < l; i++){
7130             this._add(a[i]);
7131         }
7132     },
7133     // private..
7134     _add : function(el) {
7135         
7136         if (el.xtype) {
7137             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7138         }
7139         
7140         if (el.applyTo){ // some kind of form field
7141             return this.addField(el);
7142         } 
7143         if (el.render){ // some kind of Toolbar.Item
7144             return this.addItem(el);
7145         }
7146         if (typeof el == "string"){ // string
7147             if(el == "separator" || el == "-"){
7148                 return this.addSeparator();
7149             }
7150             if (el == " "){
7151                 return this.addSpacer();
7152             }
7153             if(el == "->"){
7154                 return this.addFill();
7155             }
7156             return this.addText(el);
7157             
7158         }
7159         if(el.tagName){ // element
7160             return this.addElement(el);
7161         }
7162         if(typeof el == "object"){ // must be button config?
7163             return this.addButton(el);
7164         }
7165         // and now what?!?!
7166         return false;
7167         
7168     },
7169     
7170     /**
7171      * Add an Xtype element
7172      * @param {Object} xtype Xtype Object
7173      * @return {Object} created Object
7174      */
7175     addxtype : function(e){
7176         return this.add(e);  
7177     },
7178     
7179     /**
7180      * Returns the Element for this toolbar.
7181      * @return {Roo.Element}
7182      */
7183     getEl : function(){
7184         return this.el;  
7185     },
7186     
7187     /**
7188      * Adds a separator
7189      * @return {Roo.Toolbar.Item} The separator item
7190      */
7191     addSeparator : function(){
7192         return this.addItem(new Roo.Toolbar.Separator());
7193     },
7194
7195     /**
7196      * Adds a spacer element
7197      * @return {Roo.Toolbar.Spacer} The spacer item
7198      */
7199     addSpacer : function(){
7200         return this.addItem(new Roo.Toolbar.Spacer());
7201     },
7202
7203     /**
7204      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7205      * @return {Roo.Toolbar.Fill} The fill item
7206      */
7207     addFill : function(){
7208         return this.addItem(new Roo.Toolbar.Fill());
7209     },
7210
7211     /**
7212      * Adds any standard HTML element to the toolbar
7213      * @param {String/HTMLElement/Element} el The element or id of the element to add
7214      * @return {Roo.Toolbar.Item} The element's item
7215      */
7216     addElement : function(el){
7217         return this.addItem(new Roo.Toolbar.Item(el));
7218     },
7219     /**
7220      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7221      * @type Roo.util.MixedCollection  
7222      */
7223     items : false,
7224      
7225     /**
7226      * Adds any Toolbar.Item or subclass
7227      * @param {Roo.Toolbar.Item} item
7228      * @return {Roo.Toolbar.Item} The item
7229      */
7230     addItem : function(item){
7231         var td = this.nextBlock();
7232         item.render(td);
7233         this.items.add(item);
7234         return item;
7235     },
7236     
7237     /**
7238      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7239      * @param {Object/Array} config A button config or array of configs
7240      * @return {Roo.Toolbar.Button/Array}
7241      */
7242     addButton : function(config){
7243         if(config instanceof Array){
7244             var buttons = [];
7245             for(var i = 0, len = config.length; i < len; i++) {
7246                 buttons.push(this.addButton(config[i]));
7247             }
7248             return buttons;
7249         }
7250         var b = config;
7251         if(!(config instanceof Roo.Toolbar.Button)){
7252             b = config.split ?
7253                 new Roo.Toolbar.SplitButton(config) :
7254                 new Roo.Toolbar.Button(config);
7255         }
7256         var td = this.nextBlock();
7257         b.render(td);
7258         this.items.add(b);
7259         return b;
7260     },
7261     
7262     /**
7263      * Adds text to the toolbar
7264      * @param {String} text The text to add
7265      * @return {Roo.Toolbar.Item} The element's item
7266      */
7267     addText : function(text){
7268         return this.addItem(new Roo.Toolbar.TextItem(text));
7269     },
7270     
7271     /**
7272      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7273      * @param {Number} index The index where the item is to be inserted
7274      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7275      * @return {Roo.Toolbar.Button/Item}
7276      */
7277     insertButton : function(index, item){
7278         if(item instanceof Array){
7279             var buttons = [];
7280             for(var i = 0, len = item.length; i < len; i++) {
7281                buttons.push(this.insertButton(index + i, item[i]));
7282             }
7283             return buttons;
7284         }
7285         if (!(item instanceof Roo.Toolbar.Button)){
7286            item = new Roo.Toolbar.Button(item);
7287         }
7288         var td = document.createElement("td");
7289         this.tr.insertBefore(td, this.tr.childNodes[index]);
7290         item.render(td);
7291         this.items.insert(index, item);
7292         return item;
7293     },
7294     
7295     /**
7296      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7297      * @param {Object} config
7298      * @return {Roo.Toolbar.Item} The element's item
7299      */
7300     addDom : function(config, returnEl){
7301         var td = this.nextBlock();
7302         Roo.DomHelper.overwrite(td, config);
7303         var ti = new Roo.Toolbar.Item(td.firstChild);
7304         ti.render(td);
7305         this.items.add(ti);
7306         return ti;
7307     },
7308
7309     /**
7310      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7311      * @type Roo.util.MixedCollection  
7312      */
7313     fields : false,
7314     
7315     /**
7316      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7317      * Note: the field should not have been rendered yet. For a field that has already been
7318      * rendered, use {@link #addElement}.
7319      * @param {Roo.form.Field} field
7320      * @return {Roo.ToolbarItem}
7321      */
7322      
7323       
7324     addField : function(field) {
7325         if (!this.fields) {
7326             var autoId = 0;
7327             this.fields = new Roo.util.MixedCollection(false, function(o){
7328                 return o.id || ("item" + (++autoId));
7329             });
7330
7331         }
7332         
7333         var td = this.nextBlock();
7334         field.render(td);
7335         var ti = new Roo.Toolbar.Item(td.firstChild);
7336         ti.render(td);
7337         this.items.add(ti);
7338         this.fields.add(field);
7339         return ti;
7340     },
7341     /**
7342      * Hide the toolbar
7343      * @method hide
7344      */
7345      
7346       
7347     hide : function()
7348     {
7349         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7350         this.el.child('div').hide();
7351     },
7352     /**
7353      * Show the toolbar
7354      * @method show
7355      */
7356     show : function()
7357     {
7358         this.el.child('div').show();
7359     },
7360       
7361     // private
7362     nextBlock : function(){
7363         var td = document.createElement("td");
7364         this.tr.appendChild(td);
7365         return td;
7366     },
7367
7368     // private
7369     destroy : function(){
7370         if(this.items){ // rendered?
7371             Roo.destroy.apply(Roo, this.items.items);
7372         }
7373         if(this.fields){ // rendered?
7374             Roo.destroy.apply(Roo, this.fields.items);
7375         }
7376         Roo.Element.uncache(this.el, this.tr);
7377     }
7378 };
7379
7380 /**
7381  * @class Roo.Toolbar.Item
7382  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7383  * @constructor
7384  * Creates a new Item
7385  * @param {HTMLElement} el 
7386  */
7387 Roo.Toolbar.Item = function(el){
7388     var cfg = {};
7389     if (typeof (el.xtype) != 'undefined') {
7390         cfg = el;
7391         el = cfg.el;
7392     }
7393     
7394     this.el = Roo.getDom(el);
7395     this.id = Roo.id(this.el);
7396     this.hidden = false;
7397     
7398     this.addEvents({
7399          /**
7400              * @event render
7401              * Fires when the button is rendered
7402              * @param {Button} this
7403              */
7404         'render': true
7405     });
7406     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7407 };
7408 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7409 //Roo.Toolbar.Item.prototype = {
7410     
7411     /**
7412      * Get this item's HTML Element
7413      * @return {HTMLElement}
7414      */
7415     getEl : function(){
7416        return this.el;  
7417     },
7418
7419     // private
7420     render : function(td){
7421         
7422          this.td = td;
7423         td.appendChild(this.el);
7424         
7425         this.fireEvent('render', this);
7426     },
7427     
7428     /**
7429      * Removes and destroys this item.
7430      */
7431     destroy : function(){
7432         this.td.parentNode.removeChild(this.td);
7433     },
7434     
7435     /**
7436      * Shows this item.
7437      */
7438     show: function(){
7439         this.hidden = false;
7440         this.td.style.display = "";
7441     },
7442     
7443     /**
7444      * Hides this item.
7445      */
7446     hide: function(){
7447         this.hidden = true;
7448         this.td.style.display = "none";
7449     },
7450     
7451     /**
7452      * Convenience function for boolean show/hide.
7453      * @param {Boolean} visible true to show/false to hide
7454      */
7455     setVisible: function(visible){
7456         if(visible) {
7457             this.show();
7458         }else{
7459             this.hide();
7460         }
7461     },
7462     
7463     /**
7464      * Try to focus this item.
7465      */
7466     focus : function(){
7467         Roo.fly(this.el).focus();
7468     },
7469     
7470     /**
7471      * Disables this item.
7472      */
7473     disable : function(){
7474         Roo.fly(this.td).addClass("x-item-disabled");
7475         this.disabled = true;
7476         this.el.disabled = true;
7477     },
7478     
7479     /**
7480      * Enables this item.
7481      */
7482     enable : function(){
7483         Roo.fly(this.td).removeClass("x-item-disabled");
7484         this.disabled = false;
7485         this.el.disabled = false;
7486     }
7487 });
7488
7489
7490 /**
7491  * @class Roo.Toolbar.Separator
7492  * @extends Roo.Toolbar.Item
7493  * A simple toolbar separator class
7494  * @constructor
7495  * Creates a new Separator
7496  */
7497 Roo.Toolbar.Separator = function(cfg){
7498     
7499     var s = document.createElement("span");
7500     s.className = "ytb-sep";
7501     if (cfg) {
7502         cfg.el = s;
7503     }
7504     
7505     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7506 };
7507 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7508     enable:Roo.emptyFn,
7509     disable:Roo.emptyFn,
7510     focus:Roo.emptyFn
7511 });
7512
7513 /**
7514  * @class Roo.Toolbar.Spacer
7515  * @extends Roo.Toolbar.Item
7516  * A simple element that adds extra horizontal space to a toolbar.
7517  * @constructor
7518  * Creates a new Spacer
7519  */
7520 Roo.Toolbar.Spacer = function(cfg){
7521     var s = document.createElement("div");
7522     s.className = "ytb-spacer";
7523     if (cfg) {
7524         cfg.el = s;
7525     }
7526     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7527 };
7528 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7529     enable:Roo.emptyFn,
7530     disable:Roo.emptyFn,
7531     focus:Roo.emptyFn
7532 });
7533
7534 /**
7535  * @class Roo.Toolbar.Fill
7536  * @extends Roo.Toolbar.Spacer
7537  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7538  * @constructor
7539  * Creates a new Spacer
7540  */
7541 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7542     // private
7543     render : function(td){
7544         td.style.width = '100%';
7545         Roo.Toolbar.Fill.superclass.render.call(this, td);
7546     }
7547 });
7548
7549 /**
7550  * @class Roo.Toolbar.TextItem
7551  * @extends Roo.Toolbar.Item
7552  * A simple class that renders text directly into a toolbar.
7553  * @constructor
7554  * Creates a new TextItem
7555  * @cfg {string} text 
7556  */
7557 Roo.Toolbar.TextItem = function(cfg){
7558     var  text = cfg || "";
7559     if (typeof(cfg) == 'object') {
7560         text = cfg.text || "";
7561     }  else {
7562         cfg = null;
7563     }
7564     var s = document.createElement("span");
7565     s.className = "ytb-text";
7566     s.innerHTML = text;
7567     if (cfg) {
7568         cfg.el  = s;
7569     }
7570     
7571     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7572 };
7573 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7574     
7575      
7576     enable:Roo.emptyFn,
7577     disable:Roo.emptyFn,
7578     focus:Roo.emptyFn,
7579      /**
7580      * Shows this button
7581      */
7582     show: function(){
7583         this.hidden = false;
7584         this.el.style.display = "";
7585     },
7586     
7587     /**
7588      * Hides this button
7589      */
7590     hide: function(){
7591         this.hidden = true;
7592         this.el.style.display = "none";
7593     }
7594     
7595 });
7596
7597 /**
7598  * @class Roo.Toolbar.Button
7599  * @extends Roo.Button
7600  * A button that renders into a toolbar.
7601  * @constructor
7602  * Creates a new Button
7603  * @param {Object} config A standard {@link Roo.Button} config object
7604  */
7605 Roo.Toolbar.Button = function(config){
7606     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7607 };
7608 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7609 {
7610     
7611     
7612     render : function(td){
7613         this.td = td;
7614         Roo.Toolbar.Button.superclass.render.call(this, td);
7615     },
7616     
7617     /**
7618      * Removes and destroys this button
7619      */
7620     destroy : function(){
7621         Roo.Toolbar.Button.superclass.destroy.call(this);
7622         this.td.parentNode.removeChild(this.td);
7623     },
7624     
7625     /**
7626      * Shows this button
7627      */
7628     show: function(){
7629         this.hidden = false;
7630         this.td.style.display = "";
7631     },
7632     
7633     /**
7634      * Hides this button
7635      */
7636     hide: function(){
7637         this.hidden = true;
7638         this.td.style.display = "none";
7639     },
7640
7641     /**
7642      * Disables this item
7643      */
7644     disable : function(){
7645         Roo.fly(this.td).addClass("x-item-disabled");
7646         this.disabled = true;
7647     },
7648
7649     /**
7650      * Enables this item
7651      */
7652     enable : function(){
7653         Roo.fly(this.td).removeClass("x-item-disabled");
7654         this.disabled = false;
7655     }
7656 });
7657 // backwards compat
7658 Roo.ToolbarButton = Roo.Toolbar.Button;
7659
7660 /**
7661  * @class Roo.Toolbar.SplitButton
7662  * @extends Roo.SplitButton
7663  * A menu button that renders into a toolbar.
7664  * @constructor
7665  * Creates a new SplitButton
7666  * @param {Object} config A standard {@link Roo.SplitButton} config object
7667  */
7668 Roo.Toolbar.SplitButton = function(config){
7669     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7670 };
7671 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7672     render : function(td){
7673         this.td = td;
7674         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7675     },
7676     
7677     /**
7678      * Removes and destroys this button
7679      */
7680     destroy : function(){
7681         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7682         this.td.parentNode.removeChild(this.td);
7683     },
7684     
7685     /**
7686      * Shows this button
7687      */
7688     show: function(){
7689         this.hidden = false;
7690         this.td.style.display = "";
7691     },
7692     
7693     /**
7694      * Hides this button
7695      */
7696     hide: function(){
7697         this.hidden = true;
7698         this.td.style.display = "none";
7699     }
7700 });
7701
7702 // backwards compat
7703 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  
7714 /**
7715  * @class Roo.PagingToolbar
7716  * @extends Roo.Toolbar
7717  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7718  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7719  * @constructor
7720  * Create a new PagingToolbar
7721  * @param {Object} config The config object
7722  */
7723 Roo.PagingToolbar = function(el, ds, config)
7724 {
7725     // old args format still supported... - xtype is prefered..
7726     if (typeof(el) == 'object' && el.xtype) {
7727         // created from xtype...
7728         config = el;
7729         ds = el.dataSource;
7730         el = config.container;
7731     }
7732     var items = [];
7733     if (config.items) {
7734         items = config.items;
7735         config.items = [];
7736     }
7737     
7738     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7739     this.ds = ds;
7740     this.cursor = 0;
7741     this.renderButtons(this.el);
7742     this.bind(ds);
7743     
7744     // supprot items array.
7745    
7746     Roo.each(items, function(e) {
7747         this.add(Roo.factory(e));
7748     },this);
7749     
7750 };
7751
7752 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7753    
7754     /**
7755      * @cfg {String/HTMLElement/Element} container
7756      * container The id or element that will contain the toolbar
7757      */
7758     /**
7759      * @cfg {Boolean} displayInfo
7760      * True to display the displayMsg (defaults to false)
7761      */
7762     
7763     
7764     /**
7765      * @cfg {Number} pageSize
7766      * The number of records to display per page (defaults to 20)
7767      */
7768     pageSize: 20,
7769     /**
7770      * @cfg {String} displayMsg
7771      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7772      */
7773     displayMsg : 'Displaying {0} - {1} of {2}',
7774     /**
7775      * @cfg {String} emptyMsg
7776      * The message to display when no records are found (defaults to "No data to display")
7777      */
7778     emptyMsg : 'No data to display',
7779     /**
7780      * Customizable piece of the default paging text (defaults to "Page")
7781      * @type String
7782      */
7783     beforePageText : "Page",
7784     /**
7785      * Customizable piece of the default paging text (defaults to "of %0")
7786      * @type String
7787      */
7788     afterPageText : "of {0}",
7789     /**
7790      * Customizable piece of the default paging text (defaults to "First Page")
7791      * @type String
7792      */
7793     firstText : "First Page",
7794     /**
7795      * Customizable piece of the default paging text (defaults to "Previous Page")
7796      * @type String
7797      */
7798     prevText : "Previous Page",
7799     /**
7800      * Customizable piece of the default paging text (defaults to "Next Page")
7801      * @type String
7802      */
7803     nextText : "Next Page",
7804     /**
7805      * Customizable piece of the default paging text (defaults to "Last Page")
7806      * @type String
7807      */
7808     lastText : "Last Page",
7809     /**
7810      * Customizable piece of the default paging text (defaults to "Refresh")
7811      * @type String
7812      */
7813     refreshText : "Refresh",
7814
7815     // private
7816     renderButtons : function(el){
7817         Roo.PagingToolbar.superclass.render.call(this, el);
7818         this.first = this.addButton({
7819             tooltip: this.firstText,
7820             cls: "x-btn-icon x-grid-page-first",
7821             disabled: true,
7822             handler: this.onClick.createDelegate(this, ["first"])
7823         });
7824         this.prev = this.addButton({
7825             tooltip: this.prevText,
7826             cls: "x-btn-icon x-grid-page-prev",
7827             disabled: true,
7828             handler: this.onClick.createDelegate(this, ["prev"])
7829         });
7830         //this.addSeparator();
7831         this.add(this.beforePageText);
7832         this.field = Roo.get(this.addDom({
7833            tag: "input",
7834            type: "text",
7835            size: "3",
7836            value: "1",
7837            cls: "x-grid-page-number"
7838         }).el);
7839         this.field.on("keydown", this.onPagingKeydown, this);
7840         this.field.on("focus", function(){this.dom.select();});
7841         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7842         this.field.setHeight(18);
7843         //this.addSeparator();
7844         this.next = this.addButton({
7845             tooltip: this.nextText,
7846             cls: "x-btn-icon x-grid-page-next",
7847             disabled: true,
7848             handler: this.onClick.createDelegate(this, ["next"])
7849         });
7850         this.last = this.addButton({
7851             tooltip: this.lastText,
7852             cls: "x-btn-icon x-grid-page-last",
7853             disabled: true,
7854             handler: this.onClick.createDelegate(this, ["last"])
7855         });
7856         //this.addSeparator();
7857         this.loading = this.addButton({
7858             tooltip: this.refreshText,
7859             cls: "x-btn-icon x-grid-loading",
7860             handler: this.onClick.createDelegate(this, ["refresh"])
7861         });
7862
7863         if(this.displayInfo){
7864             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7865         }
7866     },
7867
7868     // private
7869     updateInfo : function(){
7870         if(this.displayEl){
7871             var count = this.ds.getCount();
7872             var msg = count == 0 ?
7873                 this.emptyMsg :
7874                 String.format(
7875                     this.displayMsg,
7876                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7877                 );
7878             this.displayEl.update(msg);
7879         }
7880     },
7881
7882     // private
7883     onLoad : function(ds, r, o){
7884        this.cursor = o.params ? o.params.start : 0;
7885        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7886
7887        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7888        this.field.dom.value = ap;
7889        this.first.setDisabled(ap == 1);
7890        this.prev.setDisabled(ap == 1);
7891        this.next.setDisabled(ap == ps);
7892        this.last.setDisabled(ap == ps);
7893        this.loading.enable();
7894        this.updateInfo();
7895     },
7896
7897     // private
7898     getPageData : function(){
7899         var total = this.ds.getTotalCount();
7900         return {
7901             total : total,
7902             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7903             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7904         };
7905     },
7906
7907     // private
7908     onLoadError : function(){
7909         this.loading.enable();
7910     },
7911
7912     // private
7913     onPagingKeydown : function(e){
7914         var k = e.getKey();
7915         var d = this.getPageData();
7916         if(k == e.RETURN){
7917             var v = this.field.dom.value, pageNum;
7918             if(!v || isNaN(pageNum = parseInt(v, 10))){
7919                 this.field.dom.value = d.activePage;
7920                 return;
7921             }
7922             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7923             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7924             e.stopEvent();
7925         }
7926         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))
7927         {
7928           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7929           this.field.dom.value = pageNum;
7930           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7931           e.stopEvent();
7932         }
7933         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7934         {
7935           var v = this.field.dom.value, pageNum; 
7936           var increment = (e.shiftKey) ? 10 : 1;
7937           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7938             increment *= -1;
7939           }
7940           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7941             this.field.dom.value = d.activePage;
7942             return;
7943           }
7944           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7945           {
7946             this.field.dom.value = parseInt(v, 10) + increment;
7947             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7948             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7949           }
7950           e.stopEvent();
7951         }
7952     },
7953
7954     // private
7955     beforeLoad : function(){
7956         if(this.loading){
7957             this.loading.disable();
7958         }
7959     },
7960     /**
7961      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7962      * @param {String} which (first|prev|next|last|refresh)  which button to press.
7963      *
7964      */
7965     // private
7966     onClick : function(which){
7967         var ds = this.ds;
7968         switch(which){
7969             case "first":
7970                 ds.load({params:{start: 0, limit: this.pageSize}});
7971             break;
7972             case "prev":
7973                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7974             break;
7975             case "next":
7976                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7977             break;
7978             case "last":
7979                 var total = ds.getTotalCount();
7980                 var extra = total % this.pageSize;
7981                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7982                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7983             break;
7984             case "refresh":
7985                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7986             break;
7987         }
7988     },
7989
7990     /**
7991      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7992      * @param {Roo.data.Store} store The data store to unbind
7993      */
7994     unbind : function(ds){
7995         ds.un("beforeload", this.beforeLoad, this);
7996         ds.un("load", this.onLoad, this);
7997         ds.un("loadexception", this.onLoadError, this);
7998         ds.un("remove", this.updateInfo, this);
7999         ds.un("add", this.updateInfo, this);
8000         this.ds = undefined;
8001     },
8002
8003     /**
8004      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8005      * @param {Roo.data.Store} store The data store to bind
8006      */
8007     bind : function(ds){
8008         ds.on("beforeload", this.beforeLoad, this);
8009         ds.on("load", this.onLoad, this);
8010         ds.on("loadexception", this.onLoadError, this);
8011         ds.on("remove", this.updateInfo, this);
8012         ds.on("add", this.updateInfo, this);
8013         this.ds = ds;
8014     }
8015 });/*
8016  * Based on:
8017  * Ext JS Library 1.1.1
8018  * Copyright(c) 2006-2007, Ext JS, LLC.
8019  *
8020  * Originally Released Under LGPL - original licence link has changed is not relivant.
8021  *
8022  * Fork - LGPL
8023  * <script type="text/javascript">
8024  */
8025
8026 /**
8027  * @class Roo.Resizable
8028  * @extends Roo.util.Observable
8029  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8030  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8031  * 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
8032  * the element will be wrapped for you automatically.</p>
8033  * <p>Here is the list of valid resize handles:</p>
8034  * <pre>
8035 Value   Description
8036 ------  -------------------
8037  'n'     north
8038  's'     south
8039  'e'     east
8040  'w'     west
8041  'nw'    northwest
8042  'sw'    southwest
8043  'se'    southeast
8044  'ne'    northeast
8045  'hd'    horizontal drag
8046  'all'   all
8047 </pre>
8048  * <p>Here's an example showing the creation of a typical Resizable:</p>
8049  * <pre><code>
8050 var resizer = new Roo.Resizable("element-id", {
8051     handles: 'all',
8052     minWidth: 200,
8053     minHeight: 100,
8054     maxWidth: 500,
8055     maxHeight: 400,
8056     pinned: true
8057 });
8058 resizer.on("resize", myHandler);
8059 </code></pre>
8060  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8061  * resizer.east.setDisplayed(false);</p>
8062  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8063  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8064  * resize operation's new size (defaults to [0, 0])
8065  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8066  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8067  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8068  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8069  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8070  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8071  * @cfg {Number} width The width of the element in pixels (defaults to null)
8072  * @cfg {Number} height The height of the element in pixels (defaults to null)
8073  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8074  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8075  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8076  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8077  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8078  * in favor of the handles config option (defaults to false)
8079  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8080  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8081  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8082  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8083  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8084  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8085  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8086  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8087  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8088  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8089  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8090  * @constructor
8091  * Create a new resizable component
8092  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8093  * @param {Object} config configuration options
8094   */
8095 Roo.Resizable = function(el, config)
8096 {
8097     this.el = Roo.get(el);
8098
8099     if(config && config.wrap){
8100         config.resizeChild = this.el;
8101         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8102         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8103         this.el.setStyle("overflow", "hidden");
8104         this.el.setPositioning(config.resizeChild.getPositioning());
8105         config.resizeChild.clearPositioning();
8106         if(!config.width || !config.height){
8107             var csize = config.resizeChild.getSize();
8108             this.el.setSize(csize.width, csize.height);
8109         }
8110         if(config.pinned && !config.adjustments){
8111             config.adjustments = "auto";
8112         }
8113     }
8114
8115     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8116     this.proxy.unselectable();
8117     this.proxy.enableDisplayMode('block');
8118
8119     Roo.apply(this, config);
8120
8121     if(this.pinned){
8122         this.disableTrackOver = true;
8123         this.el.addClass("x-resizable-pinned");
8124     }
8125     // if the element isn't positioned, make it relative
8126     var position = this.el.getStyle("position");
8127     if(position != "absolute" && position != "fixed"){
8128         this.el.setStyle("position", "relative");
8129     }
8130     if(!this.handles){ // no handles passed, must be legacy style
8131         this.handles = 's,e,se';
8132         if(this.multiDirectional){
8133             this.handles += ',n,w';
8134         }
8135     }
8136     if(this.handles == "all"){
8137         this.handles = "n s e w ne nw se sw";
8138     }
8139     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8140     var ps = Roo.Resizable.positions;
8141     for(var i = 0, len = hs.length; i < len; i++){
8142         if(hs[i] && ps[hs[i]]){
8143             var pos = ps[hs[i]];
8144             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8145         }
8146     }
8147     // legacy
8148     this.corner = this.southeast;
8149     
8150     // updateBox = the box can move..
8151     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8152         this.updateBox = true;
8153     }
8154
8155     this.activeHandle = null;
8156
8157     if(this.resizeChild){
8158         if(typeof this.resizeChild == "boolean"){
8159             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8160         }else{
8161             this.resizeChild = Roo.get(this.resizeChild, true);
8162         }
8163     }
8164     
8165     if(this.adjustments == "auto"){
8166         var rc = this.resizeChild;
8167         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8168         if(rc && (hw || hn)){
8169             rc.position("relative");
8170             rc.setLeft(hw ? hw.el.getWidth() : 0);
8171             rc.setTop(hn ? hn.el.getHeight() : 0);
8172         }
8173         this.adjustments = [
8174             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8175             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8176         ];
8177     }
8178
8179     if(this.draggable){
8180         this.dd = this.dynamic ?
8181             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8182         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8183     }
8184
8185     // public events
8186     this.addEvents({
8187         /**
8188          * @event beforeresize
8189          * Fired before resize is allowed. Set enabled to false to cancel resize.
8190          * @param {Roo.Resizable} this
8191          * @param {Roo.EventObject} e The mousedown event
8192          */
8193         "beforeresize" : true,
8194         /**
8195          * @event resizing
8196          * Fired a resizing.
8197          * @param {Roo.Resizable} this
8198          * @param {Number} x The new x position
8199          * @param {Number} y The new y position
8200          * @param {Number} w The new w width
8201          * @param {Number} h The new h hight
8202          * @param {Roo.EventObject} e The mouseup event
8203          */
8204         "resizing" : true,
8205         /**
8206          * @event resize
8207          * Fired after a resize.
8208          * @param {Roo.Resizable} this
8209          * @param {Number} width The new width
8210          * @param {Number} height The new height
8211          * @param {Roo.EventObject} e The mouseup event
8212          */
8213         "resize" : true
8214     });
8215
8216     if(this.width !== null && this.height !== null){
8217         this.resizeTo(this.width, this.height);
8218     }else{
8219         this.updateChildSize();
8220     }
8221     if(Roo.isIE){
8222         this.el.dom.style.zoom = 1;
8223     }
8224     Roo.Resizable.superclass.constructor.call(this);
8225 };
8226
8227 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8228         resizeChild : false,
8229         adjustments : [0, 0],
8230         minWidth : 5,
8231         minHeight : 5,
8232         maxWidth : 10000,
8233         maxHeight : 10000,
8234         enabled : true,
8235         animate : false,
8236         duration : .35,
8237         dynamic : false,
8238         handles : false,
8239         multiDirectional : false,
8240         disableTrackOver : false,
8241         easing : 'easeOutStrong',
8242         widthIncrement : 0,
8243         heightIncrement : 0,
8244         pinned : false,
8245         width : null,
8246         height : null,
8247         preserveRatio : false,
8248         transparent: false,
8249         minX: 0,
8250         minY: 0,
8251         draggable: false,
8252
8253         /**
8254          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8255          */
8256         constrainTo: undefined,
8257         /**
8258          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8259          */
8260         resizeRegion: undefined,
8261
8262
8263     /**
8264      * Perform a manual resize
8265      * @param {Number} width
8266      * @param {Number} height
8267      */
8268     resizeTo : function(width, height){
8269         this.el.setSize(width, height);
8270         this.updateChildSize();
8271         this.fireEvent("resize", this, width, height, null);
8272     },
8273
8274     // private
8275     startSizing : function(e, handle){
8276         this.fireEvent("beforeresize", this, e);
8277         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8278
8279             if(!this.overlay){
8280                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8281                 this.overlay.unselectable();
8282                 this.overlay.enableDisplayMode("block");
8283                 this.overlay.on("mousemove", this.onMouseMove, this);
8284                 this.overlay.on("mouseup", this.onMouseUp, this);
8285             }
8286             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8287
8288             this.resizing = true;
8289             this.startBox = this.el.getBox();
8290             this.startPoint = e.getXY();
8291             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8292                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8293
8294             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8295             this.overlay.show();
8296
8297             if(this.constrainTo) {
8298                 var ct = Roo.get(this.constrainTo);
8299                 this.resizeRegion = ct.getRegion().adjust(
8300                     ct.getFrameWidth('t'),
8301                     ct.getFrameWidth('l'),
8302                     -ct.getFrameWidth('b'),
8303                     -ct.getFrameWidth('r')
8304                 );
8305             }
8306
8307             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8308             this.proxy.show();
8309             this.proxy.setBox(this.startBox);
8310             if(!this.dynamic){
8311                 this.proxy.setStyle('visibility', 'visible');
8312             }
8313         }
8314     },
8315
8316     // private
8317     onMouseDown : function(handle, e){
8318         if(this.enabled){
8319             e.stopEvent();
8320             this.activeHandle = handle;
8321             this.startSizing(e, handle);
8322         }
8323     },
8324
8325     // private
8326     onMouseUp : function(e){
8327         var size = this.resizeElement();
8328         this.resizing = false;
8329         this.handleOut();
8330         this.overlay.hide();
8331         this.proxy.hide();
8332         this.fireEvent("resize", this, size.width, size.height, e);
8333     },
8334
8335     // private
8336     updateChildSize : function(){
8337         
8338         if(this.resizeChild){
8339             var el = this.el;
8340             var child = this.resizeChild;
8341             var adj = this.adjustments;
8342             if(el.dom.offsetWidth){
8343                 var b = el.getSize(true);
8344                 child.setSize(b.width+adj[0], b.height+adj[1]);
8345             }
8346             // Second call here for IE
8347             // The first call enables instant resizing and
8348             // the second call corrects scroll bars if they
8349             // exist
8350             if(Roo.isIE){
8351                 setTimeout(function(){
8352                     if(el.dom.offsetWidth){
8353                         var b = el.getSize(true);
8354                         child.setSize(b.width+adj[0], b.height+adj[1]);
8355                     }
8356                 }, 10);
8357             }
8358         }
8359     },
8360
8361     // private
8362     snap : function(value, inc, min){
8363         if(!inc || !value) {
8364             return value;
8365         }
8366         var newValue = value;
8367         var m = value % inc;
8368         if(m > 0){
8369             if(m > (inc/2)){
8370                 newValue = value + (inc-m);
8371             }else{
8372                 newValue = value - m;
8373             }
8374         }
8375         return Math.max(min, newValue);
8376     },
8377
8378     // private
8379     resizeElement : function(){
8380         var box = this.proxy.getBox();
8381         if(this.updateBox){
8382             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8383         }else{
8384             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8385         }
8386         this.updateChildSize();
8387         if(!this.dynamic){
8388             this.proxy.hide();
8389         }
8390         return box;
8391     },
8392
8393     // private
8394     constrain : function(v, diff, m, mx){
8395         if(v - diff < m){
8396             diff = v - m;
8397         }else if(v - diff > mx){
8398             diff = mx - v;
8399         }
8400         return diff;
8401     },
8402
8403     // private
8404     onMouseMove : function(e){
8405         
8406         if(this.enabled){
8407             try{// try catch so if something goes wrong the user doesn't get hung
8408
8409             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8410                 return;
8411             }
8412
8413             //var curXY = this.startPoint;
8414             var curSize = this.curSize || this.startBox;
8415             var x = this.startBox.x, y = this.startBox.y;
8416             var ox = x, oy = y;
8417             var w = curSize.width, h = curSize.height;
8418             var ow = w, oh = h;
8419             var mw = this.minWidth, mh = this.minHeight;
8420             var mxw = this.maxWidth, mxh = this.maxHeight;
8421             var wi = this.widthIncrement;
8422             var hi = this.heightIncrement;
8423
8424             var eventXY = e.getXY();
8425             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8426             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8427
8428             var pos = this.activeHandle.position;
8429
8430             switch(pos){
8431                 case "east":
8432                     w += diffX;
8433                     w = Math.min(Math.max(mw, w), mxw);
8434                     break;
8435              
8436                 case "south":
8437                     h += diffY;
8438                     h = Math.min(Math.max(mh, h), mxh);
8439                     break;
8440                 case "southeast":
8441                     w += diffX;
8442                     h += diffY;
8443                     w = Math.min(Math.max(mw, w), mxw);
8444                     h = Math.min(Math.max(mh, h), mxh);
8445                     break;
8446                 case "north":
8447                     diffY = this.constrain(h, diffY, mh, mxh);
8448                     y += diffY;
8449                     h -= diffY;
8450                     break;
8451                 case "hdrag":
8452                     
8453                     if (wi) {
8454                         var adiffX = Math.abs(diffX);
8455                         var sub = (adiffX % wi); // how much 
8456                         if (sub > (wi/2)) { // far enough to snap
8457                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8458                         } else {
8459                             // remove difference.. 
8460                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8461                         }
8462                     }
8463                     x += diffX;
8464                     x = Math.max(this.minX, x);
8465                     break;
8466                 case "west":
8467                     diffX = this.constrain(w, diffX, mw, mxw);
8468                     x += diffX;
8469                     w -= diffX;
8470                     break;
8471                 case "northeast":
8472                     w += diffX;
8473                     w = Math.min(Math.max(mw, w), mxw);
8474                     diffY = this.constrain(h, diffY, mh, mxh);
8475                     y += diffY;
8476                     h -= diffY;
8477                     break;
8478                 case "northwest":
8479                     diffX = this.constrain(w, diffX, mw, mxw);
8480                     diffY = this.constrain(h, diffY, mh, mxh);
8481                     y += diffY;
8482                     h -= diffY;
8483                     x += diffX;
8484                     w -= diffX;
8485                     break;
8486                case "southwest":
8487                     diffX = this.constrain(w, diffX, mw, mxw);
8488                     h += diffY;
8489                     h = Math.min(Math.max(mh, h), mxh);
8490                     x += diffX;
8491                     w -= diffX;
8492                     break;
8493             }
8494
8495             var sw = this.snap(w, wi, mw);
8496             var sh = this.snap(h, hi, mh);
8497             if(sw != w || sh != h){
8498                 switch(pos){
8499                     case "northeast":
8500                         y -= sh - h;
8501                     break;
8502                     case "north":
8503                         y -= sh - h;
8504                         break;
8505                     case "southwest":
8506                         x -= sw - w;
8507                     break;
8508                     case "west":
8509                         x -= sw - w;
8510                         break;
8511                     case "northwest":
8512                         x -= sw - w;
8513                         y -= sh - h;
8514                     break;
8515                 }
8516                 w = sw;
8517                 h = sh;
8518             }
8519
8520             if(this.preserveRatio){
8521                 switch(pos){
8522                     case "southeast":
8523                     case "east":
8524                         h = oh * (w/ow);
8525                         h = Math.min(Math.max(mh, h), mxh);
8526                         w = ow * (h/oh);
8527                        break;
8528                     case "south":
8529                         w = ow * (h/oh);
8530                         w = Math.min(Math.max(mw, w), mxw);
8531                         h = oh * (w/ow);
8532                         break;
8533                     case "northeast":
8534                         w = ow * (h/oh);
8535                         w = Math.min(Math.max(mw, w), mxw);
8536                         h = oh * (w/ow);
8537                     break;
8538                     case "north":
8539                         var tw = w;
8540                         w = ow * (h/oh);
8541                         w = Math.min(Math.max(mw, w), mxw);
8542                         h = oh * (w/ow);
8543                         x += (tw - w) / 2;
8544                         break;
8545                     case "southwest":
8546                         h = oh * (w/ow);
8547                         h = Math.min(Math.max(mh, h), mxh);
8548                         var tw = w;
8549                         w = ow * (h/oh);
8550                         x += tw - w;
8551                         break;
8552                     case "west":
8553                         var th = h;
8554                         h = oh * (w/ow);
8555                         h = Math.min(Math.max(mh, h), mxh);
8556                         y += (th - h) / 2;
8557                         var tw = w;
8558                         w = ow * (h/oh);
8559                         x += tw - w;
8560                        break;
8561                     case "northwest":
8562                         var tw = w;
8563                         var th = h;
8564                         h = oh * (w/ow);
8565                         h = Math.min(Math.max(mh, h), mxh);
8566                         w = ow * (h/oh);
8567                         y += th - h;
8568                         x += tw - w;
8569                        break;
8570
8571                 }
8572             }
8573             if (pos == 'hdrag') {
8574                 w = ow;
8575             }
8576             this.proxy.setBounds(x, y, w, h);
8577             if(this.dynamic){
8578                 this.resizeElement();
8579             }
8580             }catch(e){}
8581         }
8582         this.fireEvent("resizing", this, x, y, w, h, e);
8583     },
8584
8585     // private
8586     handleOver : function(){
8587         if(this.enabled){
8588             this.el.addClass("x-resizable-over");
8589         }
8590     },
8591
8592     // private
8593     handleOut : function(){
8594         if(!this.resizing){
8595             this.el.removeClass("x-resizable-over");
8596         }
8597     },
8598
8599     /**
8600      * Returns the element this component is bound to.
8601      * @return {Roo.Element}
8602      */
8603     getEl : function(){
8604         return this.el;
8605     },
8606
8607     /**
8608      * Returns the resizeChild element (or null).
8609      * @return {Roo.Element}
8610      */
8611     getResizeChild : function(){
8612         return this.resizeChild;
8613     },
8614     groupHandler : function()
8615     {
8616         
8617     },
8618     /**
8619      * Destroys this resizable. If the element was wrapped and
8620      * removeEl is not true then the element remains.
8621      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8622      */
8623     destroy : function(removeEl){
8624         this.proxy.remove();
8625         if(this.overlay){
8626             this.overlay.removeAllListeners();
8627             this.overlay.remove();
8628         }
8629         var ps = Roo.Resizable.positions;
8630         for(var k in ps){
8631             if(typeof ps[k] != "function" && this[ps[k]]){
8632                 var h = this[ps[k]];
8633                 h.el.removeAllListeners();
8634                 h.el.remove();
8635             }
8636         }
8637         if(removeEl){
8638             this.el.update("");
8639             this.el.remove();
8640         }
8641     }
8642 });
8643
8644 // private
8645 // hash to map config positions to true positions
8646 Roo.Resizable.positions = {
8647     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8648     hd: "hdrag"
8649 };
8650
8651 // private
8652 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8653     if(!this.tpl){
8654         // only initialize the template if resizable is used
8655         var tpl = Roo.DomHelper.createTemplate(
8656             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8657         );
8658         tpl.compile();
8659         Roo.Resizable.Handle.prototype.tpl = tpl;
8660     }
8661     this.position = pos;
8662     this.rz = rz;
8663     // show north drag fro topdra
8664     var handlepos = pos == 'hdrag' ? 'north' : pos;
8665     
8666     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8667     if (pos == 'hdrag') {
8668         this.el.setStyle('cursor', 'pointer');
8669     }
8670     this.el.unselectable();
8671     if(transparent){
8672         this.el.setOpacity(0);
8673     }
8674     this.el.on("mousedown", this.onMouseDown, this);
8675     if(!disableTrackOver){
8676         this.el.on("mouseover", this.onMouseOver, this);
8677         this.el.on("mouseout", this.onMouseOut, this);
8678     }
8679 };
8680
8681 // private
8682 Roo.Resizable.Handle.prototype = {
8683     afterResize : function(rz){
8684         Roo.log('after?');
8685         // do nothing
8686     },
8687     // private
8688     onMouseDown : function(e){
8689         this.rz.onMouseDown(this, e);
8690     },
8691     // private
8692     onMouseOver : function(e){
8693         this.rz.handleOver(this, e);
8694     },
8695     // private
8696     onMouseOut : function(e){
8697         this.rz.handleOut(this, e);
8698     }
8699 };/*
8700  * Based on:
8701  * Ext JS Library 1.1.1
8702  * Copyright(c) 2006-2007, Ext JS, LLC.
8703  *
8704  * Originally Released Under LGPL - original licence link has changed is not relivant.
8705  *
8706  * Fork - LGPL
8707  * <script type="text/javascript">
8708  */
8709
8710 /**
8711  * @class Roo.Editor
8712  * @extends Roo.Component
8713  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8714  * @constructor
8715  * Create a new Editor
8716  * @param {Roo.form.Field} field The Field object (or descendant)
8717  * @param {Object} config The config object
8718  */
8719 Roo.Editor = function(field, config){
8720     Roo.Editor.superclass.constructor.call(this, config);
8721     this.field = field;
8722     this.addEvents({
8723         /**
8724              * @event beforestartedit
8725              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8726              * false from the handler of this event.
8727              * @param {Editor} this
8728              * @param {Roo.Element} boundEl The underlying element bound to this editor
8729              * @param {Mixed} value The field value being set
8730              */
8731         "beforestartedit" : true,
8732         /**
8733              * @event startedit
8734              * Fires when this editor is displayed
8735              * @param {Roo.Element} boundEl The underlying element bound to this editor
8736              * @param {Mixed} value The starting field value
8737              */
8738         "startedit" : true,
8739         /**
8740              * @event beforecomplete
8741              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8742              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8743              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8744              * event will not fire since no edit actually occurred.
8745              * @param {Editor} this
8746              * @param {Mixed} value The current field value
8747              * @param {Mixed} startValue The original field value
8748              */
8749         "beforecomplete" : true,
8750         /**
8751              * @event complete
8752              * Fires after editing is complete and any changed value has been written to the underlying field.
8753              * @param {Editor} this
8754              * @param {Mixed} value The current field value
8755              * @param {Mixed} startValue The original field value
8756              */
8757         "complete" : true,
8758         /**
8759          * @event specialkey
8760          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8761          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8762          * @param {Roo.form.Field} this
8763          * @param {Roo.EventObject} e The event object
8764          */
8765         "specialkey" : true
8766     });
8767 };
8768
8769 Roo.extend(Roo.Editor, Roo.Component, {
8770     /**
8771      * @cfg {Boolean/String} autosize
8772      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8773      * or "height" to adopt the height only (defaults to false)
8774      */
8775     /**
8776      * @cfg {Boolean} revertInvalid
8777      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8778      * validation fails (defaults to true)
8779      */
8780     /**
8781      * @cfg {Boolean} ignoreNoChange
8782      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8783      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8784      * will never be ignored.
8785      */
8786     /**
8787      * @cfg {Boolean} hideEl
8788      * False to keep the bound element visible while the editor is displayed (defaults to true)
8789      */
8790     /**
8791      * @cfg {Mixed} value
8792      * The data value of the underlying field (defaults to "")
8793      */
8794     value : "",
8795     /**
8796      * @cfg {String} alignment
8797      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8798      */
8799     alignment: "c-c?",
8800     /**
8801      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8802      * for bottom-right shadow (defaults to "frame")
8803      */
8804     shadow : "frame",
8805     /**
8806      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8807      */
8808     constrain : false,
8809     /**
8810      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8811      */
8812     completeOnEnter : false,
8813     /**
8814      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8815      */
8816     cancelOnEsc : false,
8817     /**
8818      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8819      */
8820     updateEl : false,
8821
8822     // private
8823     onRender : function(ct, position){
8824         this.el = new Roo.Layer({
8825             shadow: this.shadow,
8826             cls: "x-editor",
8827             parentEl : ct,
8828             shim : this.shim,
8829             shadowOffset:4,
8830             id: this.id,
8831             constrain: this.constrain
8832         });
8833         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8834         if(this.field.msgTarget != 'title'){
8835             this.field.msgTarget = 'qtip';
8836         }
8837         this.field.render(this.el);
8838         if(Roo.isGecko){
8839             this.field.el.dom.setAttribute('autocomplete', 'off');
8840         }
8841         this.field.on("specialkey", this.onSpecialKey, this);
8842         if(this.swallowKeys){
8843             this.field.el.swallowEvent(['keydown','keypress']);
8844         }
8845         this.field.show();
8846         this.field.on("blur", this.onBlur, this);
8847         if(this.field.grow){
8848             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8849         }
8850     },
8851
8852     onSpecialKey : function(field, e)
8853     {
8854         //Roo.log('editor onSpecialKey');
8855         if(this.completeOnEnter && e.getKey() == e.ENTER){
8856             e.stopEvent();
8857             this.completeEdit();
8858             return;
8859         }
8860         // do not fire special key otherwise it might hide close the editor...
8861         if(e.getKey() == e.ENTER){    
8862             return;
8863         }
8864         if(this.cancelOnEsc && e.getKey() == e.ESC){
8865             this.cancelEdit();
8866             return;
8867         } 
8868         this.fireEvent('specialkey', field, e);
8869     
8870     },
8871
8872     /**
8873      * Starts the editing process and shows the editor.
8874      * @param {String/HTMLElement/Element} el The element to edit
8875      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8876       * to the innerHTML of el.
8877      */
8878     startEdit : function(el, value){
8879         if(this.editing){
8880             this.completeEdit();
8881         }
8882         this.boundEl = Roo.get(el);
8883         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8884         if(!this.rendered){
8885             this.render(this.parentEl || document.body);
8886         }
8887         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8888             return;
8889         }
8890         this.startValue = v;
8891         this.field.setValue(v);
8892         if(this.autoSize){
8893             var sz = this.boundEl.getSize();
8894             switch(this.autoSize){
8895                 case "width":
8896                 this.setSize(sz.width,  "");
8897                 break;
8898                 case "height":
8899                 this.setSize("",  sz.height);
8900                 break;
8901                 default:
8902                 this.setSize(sz.width,  sz.height);
8903             }
8904         }
8905         this.el.alignTo(this.boundEl, this.alignment);
8906         this.editing = true;
8907         if(Roo.QuickTips){
8908             Roo.QuickTips.disable();
8909         }
8910         this.show();
8911     },
8912
8913     /**
8914      * Sets the height and width of this editor.
8915      * @param {Number} width The new width
8916      * @param {Number} height The new height
8917      */
8918     setSize : function(w, h){
8919         this.field.setSize(w, h);
8920         if(this.el){
8921             this.el.sync();
8922         }
8923     },
8924
8925     /**
8926      * Realigns the editor to the bound field based on the current alignment config value.
8927      */
8928     realign : function(){
8929         this.el.alignTo(this.boundEl, this.alignment);
8930     },
8931
8932     /**
8933      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8934      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8935      */
8936     completeEdit : function(remainVisible){
8937         if(!this.editing){
8938             return;
8939         }
8940         var v = this.getValue();
8941         if(this.revertInvalid !== false && !this.field.isValid()){
8942             v = this.startValue;
8943             this.cancelEdit(true);
8944         }
8945         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8946             this.editing = false;
8947             this.hide();
8948             return;
8949         }
8950         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8951             this.editing = false;
8952             if(this.updateEl && this.boundEl){
8953                 this.boundEl.update(v);
8954             }
8955             if(remainVisible !== true){
8956                 this.hide();
8957             }
8958             this.fireEvent("complete", this, v, this.startValue);
8959         }
8960     },
8961
8962     // private
8963     onShow : function(){
8964         this.el.show();
8965         if(this.hideEl !== false){
8966             this.boundEl.hide();
8967         }
8968         this.field.show();
8969         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8970             this.fixIEFocus = true;
8971             this.deferredFocus.defer(50, this);
8972         }else{
8973             this.field.focus();
8974         }
8975         this.fireEvent("startedit", this.boundEl, this.startValue);
8976     },
8977
8978     deferredFocus : function(){
8979         if(this.editing){
8980             this.field.focus();
8981         }
8982     },
8983
8984     /**
8985      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8986      * reverted to the original starting value.
8987      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8988      * cancel (defaults to false)
8989      */
8990     cancelEdit : function(remainVisible){
8991         if(this.editing){
8992             this.setValue(this.startValue);
8993             if(remainVisible !== true){
8994                 this.hide();
8995             }
8996         }
8997     },
8998
8999     // private
9000     onBlur : function(){
9001         if(this.allowBlur !== true && this.editing){
9002             this.completeEdit();
9003         }
9004     },
9005
9006     // private
9007     onHide : function(){
9008         if(this.editing){
9009             this.completeEdit();
9010             return;
9011         }
9012         this.field.blur();
9013         if(this.field.collapse){
9014             this.field.collapse();
9015         }
9016         this.el.hide();
9017         if(this.hideEl !== false){
9018             this.boundEl.show();
9019         }
9020         if(Roo.QuickTips){
9021             Roo.QuickTips.enable();
9022         }
9023     },
9024
9025     /**
9026      * Sets the data value of the editor
9027      * @param {Mixed} value Any valid value supported by the underlying field
9028      */
9029     setValue : function(v){
9030         this.field.setValue(v);
9031     },
9032
9033     /**
9034      * Gets the data value of the editor
9035      * @return {Mixed} The data value
9036      */
9037     getValue : function(){
9038         return this.field.getValue();
9039     }
9040 });/*
9041  * Based on:
9042  * Ext JS Library 1.1.1
9043  * Copyright(c) 2006-2007, Ext JS, LLC.
9044  *
9045  * Originally Released Under LGPL - original licence link has changed is not relivant.
9046  *
9047  * Fork - LGPL
9048  * <script type="text/javascript">
9049  */
9050  
9051 /**
9052  * @class Roo.BasicDialog
9053  * @extends Roo.util.Observable
9054  * @parent none builder
9055  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9056  * <pre><code>
9057 var dlg = new Roo.BasicDialog("my-dlg", {
9058     height: 200,
9059     width: 300,
9060     minHeight: 100,
9061     minWidth: 150,
9062     modal: true,
9063     proxyDrag: true,
9064     shadow: true
9065 });
9066 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9067 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9068 dlg.addButton('Cancel', dlg.hide, dlg);
9069 dlg.show();
9070 </code></pre>
9071   <b>A Dialog should always be a direct child of the body element.</b>
9072  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9073  * @cfg {String} title Default text to display in the title bar (defaults to null)
9074  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9075  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9076  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9077  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9078  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9079  * (defaults to null with no animation)
9080  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9081  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9082  * property for valid values (defaults to 'all')
9083  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9084  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9085  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9086  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9087  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9088  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9089  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9090  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9091  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9092  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9093  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9094  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9095  * draggable = true (defaults to false)
9096  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9097  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9098  * shadow (defaults to false)
9099  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9100  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9101  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9102  * @cfg {Array} buttons Array of buttons
9103  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9104  * @constructor
9105  * Create a new BasicDialog.
9106  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9107  * @param {Object} config Configuration options
9108  */
9109 Roo.BasicDialog = function(el, config){
9110     this.el = Roo.get(el);
9111     var dh = Roo.DomHelper;
9112     if(!this.el && config && config.autoCreate){
9113         if(typeof config.autoCreate == "object"){
9114             if(!config.autoCreate.id){
9115                 config.autoCreate.id = el;
9116             }
9117             this.el = dh.append(document.body,
9118                         config.autoCreate, true);
9119         }else{
9120             this.el = dh.append(document.body,
9121                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9122         }
9123     }
9124     el = this.el;
9125     el.setDisplayed(true);
9126     el.hide = this.hideAction;
9127     this.id = el.id;
9128     el.addClass("x-dlg");
9129
9130     Roo.apply(this, config);
9131
9132     this.proxy = el.createProxy("x-dlg-proxy");
9133     this.proxy.hide = this.hideAction;
9134     this.proxy.setOpacity(.5);
9135     this.proxy.hide();
9136
9137     if(config.width){
9138         el.setWidth(config.width);
9139     }
9140     if(config.height){
9141         el.setHeight(config.height);
9142     }
9143     this.size = el.getSize();
9144     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9145         this.xy = [config.x,config.y];
9146     }else{
9147         this.xy = el.getCenterXY(true);
9148     }
9149     /** The header element @type Roo.Element */
9150     this.header = el.child("> .x-dlg-hd");
9151     /** The body element @type Roo.Element */
9152     this.body = el.child("> .x-dlg-bd");
9153     /** The footer element @type Roo.Element */
9154     this.footer = el.child("> .x-dlg-ft");
9155
9156     if(!this.header){
9157         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9158     }
9159     if(!this.body){
9160         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9161     }
9162
9163     this.header.unselectable();
9164     if(this.title){
9165         this.header.update(this.title);
9166     }
9167     // this element allows the dialog to be focused for keyboard event
9168     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9169     this.focusEl.swallowEvent("click", true);
9170
9171     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9172
9173     // wrap the body and footer for special rendering
9174     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9175     if(this.footer){
9176         this.bwrap.dom.appendChild(this.footer.dom);
9177     }
9178
9179     this.bg = this.el.createChild({
9180         tag: "div", cls:"x-dlg-bg",
9181         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9182     });
9183     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9184
9185
9186     if(this.autoScroll !== false && !this.autoTabs){
9187         this.body.setStyle("overflow", "auto");
9188     }
9189
9190     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9191
9192     if(this.closable !== false){
9193         this.el.addClass("x-dlg-closable");
9194         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9195         this.close.on("click", this.closeClick, this);
9196         this.close.addClassOnOver("x-dlg-close-over");
9197     }
9198     if(this.collapsible !== false){
9199         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9200         this.collapseBtn.on("click", this.collapseClick, this);
9201         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9202         this.header.on("dblclick", this.collapseClick, this);
9203     }
9204     if(this.resizable !== false){
9205         this.el.addClass("x-dlg-resizable");
9206         this.resizer = new Roo.Resizable(el, {
9207             minWidth: this.minWidth || 80,
9208             minHeight:this.minHeight || 80,
9209             handles: this.resizeHandles || "all",
9210             pinned: true
9211         });
9212         this.resizer.on("beforeresize", this.beforeResize, this);
9213         this.resizer.on("resize", this.onResize, this);
9214     }
9215     if(this.draggable !== false){
9216         el.addClass("x-dlg-draggable");
9217         if (!this.proxyDrag) {
9218             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9219         }
9220         else {
9221             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9222         }
9223         dd.setHandleElId(this.header.id);
9224         dd.endDrag = this.endMove.createDelegate(this);
9225         dd.startDrag = this.startMove.createDelegate(this);
9226         dd.onDrag = this.onDrag.createDelegate(this);
9227         dd.scroll = false;
9228         this.dd = dd;
9229     }
9230     if(this.modal){
9231         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9232         this.mask.enableDisplayMode("block");
9233         this.mask.hide();
9234         this.el.addClass("x-dlg-modal");
9235     }
9236     if(this.shadow){
9237         this.shadow = new Roo.Shadow({
9238             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9239             offset : this.shadowOffset
9240         });
9241     }else{
9242         this.shadowOffset = 0;
9243     }
9244     if(Roo.useShims && this.shim !== false){
9245         this.shim = this.el.createShim();
9246         this.shim.hide = this.hideAction;
9247         this.shim.hide();
9248     }else{
9249         this.shim = false;
9250     }
9251     if(this.autoTabs){
9252         this.initTabs();
9253     }
9254     if (this.buttons) { 
9255         var bts= this.buttons;
9256         this.buttons = [];
9257         Roo.each(bts, function(b) {
9258             this.addButton(b);
9259         }, this);
9260     }
9261     
9262     
9263     this.addEvents({
9264         /**
9265          * @event keydown
9266          * Fires when a key is pressed
9267          * @param {Roo.BasicDialog} this
9268          * @param {Roo.EventObject} e
9269          */
9270         "keydown" : true,
9271         /**
9272          * @event move
9273          * Fires when this dialog is moved by the user.
9274          * @param {Roo.BasicDialog} this
9275          * @param {Number} x The new page X
9276          * @param {Number} y The new page Y
9277          */
9278         "move" : true,
9279         /**
9280          * @event resize
9281          * Fires when this dialog is resized by the user.
9282          * @param {Roo.BasicDialog} this
9283          * @param {Number} width The new width
9284          * @param {Number} height The new height
9285          */
9286         "resize" : true,
9287         /**
9288          * @event beforehide
9289          * Fires before this dialog is hidden.
9290          * @param {Roo.BasicDialog} this
9291          */
9292         "beforehide" : true,
9293         /**
9294          * @event hide
9295          * Fires when this dialog is hidden.
9296          * @param {Roo.BasicDialog} this
9297          */
9298         "hide" : true,
9299         /**
9300          * @event beforeshow
9301          * Fires before this dialog is shown.
9302          * @param {Roo.BasicDialog} this
9303          */
9304         "beforeshow" : true,
9305         /**
9306          * @event show
9307          * Fires when this dialog is shown.
9308          * @param {Roo.BasicDialog} this
9309          */
9310         "show" : true
9311     });
9312     el.on("keydown", this.onKeyDown, this);
9313     el.on("mousedown", this.toFront, this);
9314     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9315     this.el.hide();
9316     Roo.DialogManager.register(this);
9317     Roo.BasicDialog.superclass.constructor.call(this);
9318 };
9319
9320 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9321     shadowOffset: Roo.isIE ? 6 : 5,
9322     minHeight: 80,
9323     minWidth: 200,
9324     minButtonWidth: 75,
9325     defaultButton: null,
9326     buttonAlign: "right",
9327     tabTag: 'div',
9328     firstShow: true,
9329
9330     /**
9331      * Sets the dialog title text
9332      * @param {String} text The title text to display
9333      * @return {Roo.BasicDialog} this
9334      */
9335     setTitle : function(text){
9336         this.header.update(text);
9337         return this;
9338     },
9339
9340     // private
9341     closeClick : function(){
9342         this.hide();
9343     },
9344
9345     // private
9346     collapseClick : function(){
9347         this[this.collapsed ? "expand" : "collapse"]();
9348     },
9349
9350     /**
9351      * Collapses the dialog to its minimized state (only the title bar is visible).
9352      * Equivalent to the user clicking the collapse dialog button.
9353      */
9354     collapse : function(){
9355         if(!this.collapsed){
9356             this.collapsed = true;
9357             this.el.addClass("x-dlg-collapsed");
9358             this.restoreHeight = this.el.getHeight();
9359             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9360         }
9361     },
9362
9363     /**
9364      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9365      * clicking the expand dialog button.
9366      */
9367     expand : function(){
9368         if(this.collapsed){
9369             this.collapsed = false;
9370             this.el.removeClass("x-dlg-collapsed");
9371             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9372         }
9373     },
9374
9375     /**
9376      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9377      * @return {Roo.TabPanel} The tabs component
9378      */
9379     initTabs : function(){
9380         var tabs = this.getTabs();
9381         while(tabs.getTab(0)){
9382             tabs.removeTab(0);
9383         }
9384         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9385             var dom = el.dom;
9386             tabs.addTab(Roo.id(dom), dom.title);
9387             dom.title = "";
9388         });
9389         tabs.activate(0);
9390         return tabs;
9391     },
9392
9393     // private
9394     beforeResize : function(){
9395         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9396     },
9397
9398     // private
9399     onResize : function(){
9400         this.refreshSize();
9401         this.syncBodyHeight();
9402         this.adjustAssets();
9403         this.focus();
9404         this.fireEvent("resize", this, this.size.width, this.size.height);
9405     },
9406
9407     // private
9408     onKeyDown : function(e){
9409         if(this.isVisible()){
9410             this.fireEvent("keydown", this, e);
9411         }
9412     },
9413
9414     /**
9415      * Resizes the dialog.
9416      * @param {Number} width
9417      * @param {Number} height
9418      * @return {Roo.BasicDialog} this
9419      */
9420     resizeTo : function(width, height){
9421         this.el.setSize(width, height);
9422         this.size = {width: width, height: height};
9423         this.syncBodyHeight();
9424         if(this.fixedcenter){
9425             this.center();
9426         }
9427         if(this.isVisible()){
9428             this.constrainXY();
9429             this.adjustAssets();
9430         }
9431         this.fireEvent("resize", this, width, height);
9432         return this;
9433     },
9434
9435
9436     /**
9437      * Resizes the dialog to fit the specified content size.
9438      * @param {Number} width
9439      * @param {Number} height
9440      * @return {Roo.BasicDialog} this
9441      */
9442     setContentSize : function(w, h){
9443         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9444         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9445         //if(!this.el.isBorderBox()){
9446             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9447             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9448         //}
9449         if(this.tabs){
9450             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9451             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9452         }
9453         this.resizeTo(w, h);
9454         return this;
9455     },
9456
9457     /**
9458      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9459      * executed in response to a particular key being pressed while the dialog is active.
9460      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9461      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9462      * @param {Function} fn The function to call
9463      * @param {Object} scope (optional) The scope of the function
9464      * @return {Roo.BasicDialog} this
9465      */
9466     addKeyListener : function(key, fn, scope){
9467         var keyCode, shift, ctrl, alt;
9468         if(typeof key == "object" && !(key instanceof Array)){
9469             keyCode = key["key"];
9470             shift = key["shift"];
9471             ctrl = key["ctrl"];
9472             alt = key["alt"];
9473         }else{
9474             keyCode = key;
9475         }
9476         var handler = function(dlg, e){
9477             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9478                 var k = e.getKey();
9479                 if(keyCode instanceof Array){
9480                     for(var i = 0, len = keyCode.length; i < len; i++){
9481                         if(keyCode[i] == k){
9482                           fn.call(scope || window, dlg, k, e);
9483                           return;
9484                         }
9485                     }
9486                 }else{
9487                     if(k == keyCode){
9488                         fn.call(scope || window, dlg, k, e);
9489                     }
9490                 }
9491             }
9492         };
9493         this.on("keydown", handler);
9494         return this;
9495     },
9496
9497     /**
9498      * Returns the TabPanel component (creates it if it doesn't exist).
9499      * Note: If you wish to simply check for the existence of tabs without creating them,
9500      * check for a null 'tabs' property.
9501      * @return {Roo.TabPanel} The tabs component
9502      */
9503     getTabs : function(){
9504         if(!this.tabs){
9505             this.el.addClass("x-dlg-auto-tabs");
9506             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9507             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9508         }
9509         return this.tabs;
9510     },
9511
9512     /**
9513      * Adds a button to the footer section of the dialog.
9514      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9515      * object or a valid Roo.DomHelper element config
9516      * @param {Function} handler The function called when the button is clicked
9517      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9518      * @return {Roo.Button} The new button
9519      */
9520     addButton : function(config, handler, scope){
9521         var dh = Roo.DomHelper;
9522         if(!this.footer){
9523             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9524         }
9525         if(!this.btnContainer){
9526             var tb = this.footer.createChild({
9527
9528                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9529                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9530             }, null, true);
9531             this.btnContainer = tb.firstChild.firstChild.firstChild;
9532         }
9533         var bconfig = {
9534             handler: handler,
9535             scope: scope,
9536             minWidth: this.minButtonWidth,
9537             hideParent:true
9538         };
9539         if(typeof config == "string"){
9540             bconfig.text = config;
9541         }else{
9542             if(config.tag){
9543                 bconfig.dhconfig = config;
9544             }else{
9545                 Roo.apply(bconfig, config);
9546             }
9547         }
9548         var fc = false;
9549         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9550             bconfig.position = Math.max(0, bconfig.position);
9551             fc = this.btnContainer.childNodes[bconfig.position];
9552         }
9553          
9554         var btn = new Roo.Button(
9555             fc ? 
9556                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9557                 : this.btnContainer.appendChild(document.createElement("td")),
9558             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9559             bconfig
9560         );
9561         this.syncBodyHeight();
9562         if(!this.buttons){
9563             /**
9564              * Array of all the buttons that have been added to this dialog via addButton
9565              * @type Array
9566              */
9567             this.buttons = [];
9568         }
9569         this.buttons.push(btn);
9570         return btn;
9571     },
9572
9573     /**
9574      * Sets the default button to be focused when the dialog is displayed.
9575      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9576      * @return {Roo.BasicDialog} this
9577      */
9578     setDefaultButton : function(btn){
9579         this.defaultButton = btn;
9580         return this;
9581     },
9582
9583     // private
9584     getHeaderFooterHeight : function(safe){
9585         var height = 0;
9586         if(this.header){
9587            height += this.header.getHeight();
9588         }
9589         if(this.footer){
9590            var fm = this.footer.getMargins();
9591             height += (this.footer.getHeight()+fm.top+fm.bottom);
9592         }
9593         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9594         height += this.centerBg.getPadding("tb");
9595         return height;
9596     },
9597
9598     // private
9599     syncBodyHeight : function()
9600     {
9601         var bd = this.body, // the text
9602             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9603             bw = this.bwrap;
9604         var height = this.size.height - this.getHeaderFooterHeight(false);
9605         bd.setHeight(height-bd.getMargins("tb"));
9606         var hh = this.header.getHeight();
9607         var h = this.size.height-hh;
9608         cb.setHeight(h);
9609         
9610         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9611         bw.setHeight(h-cb.getPadding("tb"));
9612         
9613         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9614         bd.setWidth(bw.getWidth(true));
9615         if(this.tabs){
9616             this.tabs.syncHeight();
9617             if(Roo.isIE){
9618                 this.tabs.el.repaint();
9619             }
9620         }
9621     },
9622
9623     /**
9624      * Restores the previous state of the dialog if Roo.state is configured.
9625      * @return {Roo.BasicDialog} this
9626      */
9627     restoreState : function(){
9628         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9629         if(box && box.width){
9630             this.xy = [box.x, box.y];
9631             this.resizeTo(box.width, box.height);
9632         }
9633         return this;
9634     },
9635
9636     // private
9637     beforeShow : function(){
9638         this.expand();
9639         if(this.fixedcenter){
9640             this.xy = this.el.getCenterXY(true);
9641         }
9642         if(this.modal){
9643             Roo.get(document.body).addClass("x-body-masked");
9644             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9645             this.mask.show();
9646         }
9647         this.constrainXY();
9648     },
9649
9650     // private
9651     animShow : function(){
9652         var b = Roo.get(this.animateTarget).getBox();
9653         this.proxy.setSize(b.width, b.height);
9654         this.proxy.setLocation(b.x, b.y);
9655         this.proxy.show();
9656         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9657                     true, .35, this.showEl.createDelegate(this));
9658     },
9659
9660     /**
9661      * Shows the dialog.
9662      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9663      * @return {Roo.BasicDialog} this
9664      */
9665     show : function(animateTarget){
9666         if (this.fireEvent("beforeshow", this) === false){
9667             return;
9668         }
9669         if(this.syncHeightBeforeShow){
9670             this.syncBodyHeight();
9671         }else if(this.firstShow){
9672             this.firstShow = false;
9673             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9674         }
9675         this.animateTarget = animateTarget || this.animateTarget;
9676         if(!this.el.isVisible()){
9677             this.beforeShow();
9678             if(this.animateTarget && Roo.get(this.animateTarget)){
9679                 this.animShow();
9680             }else{
9681                 this.showEl();
9682             }
9683         }
9684         return this;
9685     },
9686
9687     // private
9688     showEl : function(){
9689         this.proxy.hide();
9690         this.el.setXY(this.xy);
9691         this.el.show();
9692         this.adjustAssets(true);
9693         this.toFront();
9694         this.focus();
9695         // IE peekaboo bug - fix found by Dave Fenwick
9696         if(Roo.isIE){
9697             this.el.repaint();
9698         }
9699         this.fireEvent("show", this);
9700     },
9701
9702     /**
9703      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9704      * dialog itself will receive focus.
9705      */
9706     focus : function(){
9707         if(this.defaultButton){
9708             this.defaultButton.focus();
9709         }else{
9710             this.focusEl.focus();
9711         }
9712     },
9713
9714     // private
9715     constrainXY : function(){
9716         if(this.constraintoviewport !== false){
9717             if(!this.viewSize){
9718                 if(this.container){
9719                     var s = this.container.getSize();
9720                     this.viewSize = [s.width, s.height];
9721                 }else{
9722                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9723                 }
9724             }
9725             var s = Roo.get(this.container||document).getScroll();
9726
9727             var x = this.xy[0], y = this.xy[1];
9728             var w = this.size.width, h = this.size.height;
9729             var vw = this.viewSize[0], vh = this.viewSize[1];
9730             // only move it if it needs it
9731             var moved = false;
9732             // first validate right/bottom
9733             if(x + w > vw+s.left){
9734                 x = vw - w;
9735                 moved = true;
9736             }
9737             if(y + h > vh+s.top){
9738                 y = vh - h;
9739                 moved = true;
9740             }
9741             // then make sure top/left isn't negative
9742             if(x < s.left){
9743                 x = s.left;
9744                 moved = true;
9745             }
9746             if(y < s.top){
9747                 y = s.top;
9748                 moved = true;
9749             }
9750             if(moved){
9751                 // cache xy
9752                 this.xy = [x, y];
9753                 if(this.isVisible()){
9754                     this.el.setLocation(x, y);
9755                     this.adjustAssets();
9756                 }
9757             }
9758         }
9759     },
9760
9761     // private
9762     onDrag : function(){
9763         if(!this.proxyDrag){
9764             this.xy = this.el.getXY();
9765             this.adjustAssets();
9766         }
9767     },
9768
9769     // private
9770     adjustAssets : function(doShow){
9771         var x = this.xy[0], y = this.xy[1];
9772         var w = this.size.width, h = this.size.height;
9773         if(doShow === true){
9774             if(this.shadow){
9775                 this.shadow.show(this.el);
9776             }
9777             if(this.shim){
9778                 this.shim.show();
9779             }
9780         }
9781         if(this.shadow && this.shadow.isVisible()){
9782             this.shadow.show(this.el);
9783         }
9784         if(this.shim && this.shim.isVisible()){
9785             this.shim.setBounds(x, y, w, h);
9786         }
9787     },
9788
9789     // private
9790     adjustViewport : function(w, h){
9791         if(!w || !h){
9792             w = Roo.lib.Dom.getViewWidth();
9793             h = Roo.lib.Dom.getViewHeight();
9794         }
9795         // cache the size
9796         this.viewSize = [w, h];
9797         if(this.modal && this.mask.isVisible()){
9798             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9799             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9800         }
9801         if(this.isVisible()){
9802             this.constrainXY();
9803         }
9804     },
9805
9806     /**
9807      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9808      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9810      */
9811     destroy : function(removeEl){
9812         if(this.isVisible()){
9813             this.animateTarget = null;
9814             this.hide();
9815         }
9816         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9817         if(this.tabs){
9818             this.tabs.destroy(removeEl);
9819         }
9820         Roo.destroy(
9821              this.shim,
9822              this.proxy,
9823              this.resizer,
9824              this.close,
9825              this.mask
9826         );
9827         if(this.dd){
9828             this.dd.unreg();
9829         }
9830         if(this.buttons){
9831            for(var i = 0, len = this.buttons.length; i < len; i++){
9832                this.buttons[i].destroy();
9833            }
9834         }
9835         this.el.removeAllListeners();
9836         if(removeEl === true){
9837             this.el.update("");
9838             this.el.remove();
9839         }
9840         Roo.DialogManager.unregister(this);
9841     },
9842
9843     // private
9844     startMove : function(){
9845         if(this.proxyDrag){
9846             this.proxy.show();
9847         }
9848         if(this.constraintoviewport !== false){
9849             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9850         }
9851     },
9852
9853     // private
9854     endMove : function(){
9855         if(!this.proxyDrag){
9856             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9857         }else{
9858             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9859             this.proxy.hide();
9860         }
9861         this.refreshSize();
9862         this.adjustAssets();
9863         this.focus();
9864         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9865     },
9866
9867     /**
9868      * Brings this dialog to the front of any other visible dialogs
9869      * @return {Roo.BasicDialog} this
9870      */
9871     toFront : function(){
9872         Roo.DialogManager.bringToFront(this);
9873         return this;
9874     },
9875
9876     /**
9877      * Sends this dialog to the back (under) of any other visible dialogs
9878      * @return {Roo.BasicDialog} this
9879      */
9880     toBack : function(){
9881         Roo.DialogManager.sendToBack(this);
9882         return this;
9883     },
9884
9885     /**
9886      * Centers this dialog in the viewport
9887      * @return {Roo.BasicDialog} this
9888      */
9889     center : function(){
9890         var xy = this.el.getCenterXY(true);
9891         this.moveTo(xy[0], xy[1]);
9892         return this;
9893     },
9894
9895     /**
9896      * Moves the dialog's top-left corner to the specified point
9897      * @param {Number} x
9898      * @param {Number} y
9899      * @return {Roo.BasicDialog} this
9900      */
9901     moveTo : function(x, y){
9902         this.xy = [x,y];
9903         if(this.isVisible()){
9904             this.el.setXY(this.xy);
9905             this.adjustAssets();
9906         }
9907         return this;
9908     },
9909
9910     /**
9911      * Aligns the dialog to the specified element
9912      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9913      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9914      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9915      * @return {Roo.BasicDialog} this
9916      */
9917     alignTo : function(element, position, offsets){
9918         this.xy = this.el.getAlignToXY(element, position, offsets);
9919         if(this.isVisible()){
9920             this.el.setXY(this.xy);
9921             this.adjustAssets();
9922         }
9923         return this;
9924     },
9925
9926     /**
9927      * Anchors an element to another element and realigns it when the window is resized.
9928      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9929      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9930      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9931      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9932      * is a number, it is used as the buffer delay (defaults to 50ms).
9933      * @return {Roo.BasicDialog} this
9934      */
9935     anchorTo : function(el, alignment, offsets, monitorScroll){
9936         var action = function(){
9937             this.alignTo(el, alignment, offsets);
9938         };
9939         Roo.EventManager.onWindowResize(action, this);
9940         var tm = typeof monitorScroll;
9941         if(tm != 'undefined'){
9942             Roo.EventManager.on(window, 'scroll', action, this,
9943                 {buffer: tm == 'number' ? monitorScroll : 50});
9944         }
9945         action.call(this);
9946         return this;
9947     },
9948
9949     /**
9950      * Returns true if the dialog is visible
9951      * @return {Boolean}
9952      */
9953     isVisible : function(){
9954         return this.el.isVisible();
9955     },
9956
9957     // private
9958     animHide : function(callback){
9959         var b = Roo.get(this.animateTarget).getBox();
9960         this.proxy.show();
9961         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9962         this.el.hide();
9963         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9964                     this.hideEl.createDelegate(this, [callback]));
9965     },
9966
9967     /**
9968      * Hides the dialog.
9969      * @param {Function} callback (optional) Function to call when the dialog is hidden
9970      * @return {Roo.BasicDialog} this
9971      */
9972     hide : function(callback){
9973         if (this.fireEvent("beforehide", this) === false){
9974             return;
9975         }
9976         if(this.shadow){
9977             this.shadow.hide();
9978         }
9979         if(this.shim) {
9980           this.shim.hide();
9981         }
9982         // sometimes animateTarget seems to get set.. causing problems...
9983         // this just double checks..
9984         if(this.animateTarget && Roo.get(this.animateTarget)) {
9985            this.animHide(callback);
9986         }else{
9987             this.el.hide();
9988             this.hideEl(callback);
9989         }
9990         return this;
9991     },
9992
9993     // private
9994     hideEl : function(callback){
9995         this.proxy.hide();
9996         if(this.modal){
9997             this.mask.hide();
9998             Roo.get(document.body).removeClass("x-body-masked");
9999         }
10000         this.fireEvent("hide", this);
10001         if(typeof callback == "function"){
10002             callback();
10003         }
10004     },
10005
10006     // private
10007     hideAction : function(){
10008         this.setLeft("-10000px");
10009         this.setTop("-10000px");
10010         this.setStyle("visibility", "hidden");
10011     },
10012
10013     // private
10014     refreshSize : function(){
10015         this.size = this.el.getSize();
10016         this.xy = this.el.getXY();
10017         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10018     },
10019
10020     // private
10021     // z-index is managed by the DialogManager and may be overwritten at any time
10022     setZIndex : function(index){
10023         if(this.modal){
10024             this.mask.setStyle("z-index", index);
10025         }
10026         if(this.shim){
10027             this.shim.setStyle("z-index", ++index);
10028         }
10029         if(this.shadow){
10030             this.shadow.setZIndex(++index);
10031         }
10032         this.el.setStyle("z-index", ++index);
10033         if(this.proxy){
10034             this.proxy.setStyle("z-index", ++index);
10035         }
10036         if(this.resizer){
10037             this.resizer.proxy.setStyle("z-index", ++index);
10038         }
10039
10040         this.lastZIndex = index;
10041     },
10042
10043     /**
10044      * Returns the element for this dialog
10045      * @return {Roo.Element} The underlying dialog Element
10046      */
10047     getEl : function(){
10048         return this.el;
10049     }
10050 });
10051
10052 /**
10053  * @class Roo.DialogManager
10054  * Provides global access to BasicDialogs that have been created and
10055  * support for z-indexing (layering) multiple open dialogs.
10056  */
10057 Roo.DialogManager = function(){
10058     var list = {};
10059     var accessList = [];
10060     var front = null;
10061
10062     // private
10063     var sortDialogs = function(d1, d2){
10064         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10065     };
10066
10067     // private
10068     var orderDialogs = function(){
10069         accessList.sort(sortDialogs);
10070         var seed = Roo.DialogManager.zseed;
10071         for(var i = 0, len = accessList.length; i < len; i++){
10072             var dlg = accessList[i];
10073             if(dlg){
10074                 dlg.setZIndex(seed + (i*10));
10075             }
10076         }
10077     };
10078
10079     return {
10080         /**
10081          * The starting z-index for BasicDialogs (defaults to 9000)
10082          * @type Number The z-index value
10083          */
10084         zseed : 9000,
10085
10086         // private
10087         register : function(dlg){
10088             list[dlg.id] = dlg;
10089             accessList.push(dlg);
10090         },
10091
10092         // private
10093         unregister : function(dlg){
10094             delete list[dlg.id];
10095             var i=0;
10096             var len=0;
10097             if(!accessList.indexOf){
10098                 for(  i = 0, len = accessList.length; i < len; i++){
10099                     if(accessList[i] == dlg){
10100                         accessList.splice(i, 1);
10101                         return;
10102                     }
10103                 }
10104             }else{
10105                  i = accessList.indexOf(dlg);
10106                 if(i != -1){
10107                     accessList.splice(i, 1);
10108                 }
10109             }
10110         },
10111
10112         /**
10113          * Gets a registered dialog by id
10114          * @param {String/Object} id The id of the dialog or a dialog
10115          * @return {Roo.BasicDialog} this
10116          */
10117         get : function(id){
10118             return typeof id == "object" ? id : list[id];
10119         },
10120
10121         /**
10122          * Brings the specified dialog to the front
10123          * @param {String/Object} dlg The id of the dialog or a dialog
10124          * @return {Roo.BasicDialog} this
10125          */
10126         bringToFront : function(dlg){
10127             dlg = this.get(dlg);
10128             if(dlg != front){
10129                 front = dlg;
10130                 dlg._lastAccess = new Date().getTime();
10131                 orderDialogs();
10132             }
10133             return dlg;
10134         },
10135
10136         /**
10137          * Sends the specified dialog to the back
10138          * @param {String/Object} dlg The id of the dialog or a dialog
10139          * @return {Roo.BasicDialog} this
10140          */
10141         sendToBack : function(dlg){
10142             dlg = this.get(dlg);
10143             dlg._lastAccess = -(new Date().getTime());
10144             orderDialogs();
10145             return dlg;
10146         },
10147
10148         /**
10149          * Hides all dialogs
10150          */
10151         hideAll : function(){
10152             for(var id in list){
10153                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10154                     list[id].hide();
10155                 }
10156             }
10157         }
10158     };
10159 }();
10160
10161 /**
10162  * @class Roo.LayoutDialog
10163  * @extends Roo.BasicDialog
10164  * @children Roo.ContentPanel
10165  * @parent builder none
10166  * Dialog which provides adjustments for working with a layout in a Dialog.
10167  * Add your necessary layout config options to the dialog's config.<br>
10168  * Example usage (including a nested layout):
10169  * <pre><code>
10170 if(!dialog){
10171     dialog = new Roo.LayoutDialog("download-dlg", {
10172         modal: true,
10173         width:600,
10174         height:450,
10175         shadow:true,
10176         minWidth:500,
10177         minHeight:350,
10178         autoTabs:true,
10179         proxyDrag:true,
10180         // layout config merges with the dialog config
10181         center:{
10182             tabPosition: "top",
10183             alwaysShowTabs: true
10184         }
10185     });
10186     dialog.addKeyListener(27, dialog.hide, dialog);
10187     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10188     dialog.addButton("Build It!", this.getDownload, this);
10189
10190     // we can even add nested layouts
10191     var innerLayout = new Roo.BorderLayout("dl-inner", {
10192         east: {
10193             initialSize: 200,
10194             autoScroll:true,
10195             split:true
10196         },
10197         center: {
10198             autoScroll:true
10199         }
10200     });
10201     innerLayout.beginUpdate();
10202     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10203     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10204     innerLayout.endUpdate(true);
10205
10206     var layout = dialog.getLayout();
10207     layout.beginUpdate();
10208     layout.add("center", new Roo.ContentPanel("standard-panel",
10209                         {title: "Download the Source", fitToFrame:true}));
10210     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10211                {title: "Build your own roo.js"}));
10212     layout.getRegion("center").showPanel(sp);
10213     layout.endUpdate();
10214 }
10215 </code></pre>
10216     * @constructor
10217     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10218     * @param {Object} config configuration options
10219   */
10220 Roo.LayoutDialog = function(el, cfg){
10221     
10222     var config=  cfg;
10223     if (typeof(cfg) == 'undefined') {
10224         config = Roo.apply({}, el);
10225         // not sure why we use documentElement here.. - it should always be body.
10226         // IE7 borks horribly if we use documentElement.
10227         // webkit also does not like documentElement - it creates a body element...
10228         el = Roo.get( document.body || document.documentElement ).createChild();
10229         //config.autoCreate = true;
10230     }
10231     
10232     
10233     config.autoTabs = false;
10234     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10235     this.body.setStyle({overflow:"hidden", position:"relative"});
10236     this.layout = new Roo.BorderLayout(this.body.dom, config);
10237     this.layout.monitorWindowResize = false;
10238     this.el.addClass("x-dlg-auto-layout");
10239     // fix case when center region overwrites center function
10240     this.center = Roo.BasicDialog.prototype.center;
10241     this.on("show", this.layout.layout, this.layout, true);
10242     if (config.items) {
10243         var xitems = config.items;
10244         delete config.items;
10245         Roo.each(xitems, this.addxtype, this);
10246     }
10247     
10248     
10249 };
10250 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10251     
10252     
10253     /**
10254      * @cfg {Roo.LayoutRegion} east  
10255      */
10256     /**
10257      * @cfg {Roo.LayoutRegion} west
10258      */
10259     /**
10260      * @cfg {Roo.LayoutRegion} south
10261      */
10262     /**
10263      * @cfg {Roo.LayoutRegion} north
10264      */
10265     /**
10266      * @cfg {Roo.LayoutRegion} center
10267      */
10268     /**
10269      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10270      */
10271     
10272     
10273     /**
10274      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10275      * @deprecated
10276      */
10277     endUpdate : function(){
10278         this.layout.endUpdate();
10279     },
10280
10281     /**
10282      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10283      *  @deprecated
10284      */
10285     beginUpdate : function(){
10286         this.layout.beginUpdate();
10287     },
10288
10289     /**
10290      * Get the BorderLayout for this dialog
10291      * @return {Roo.BorderLayout}
10292      */
10293     getLayout : function(){
10294         return this.layout;
10295     },
10296
10297     showEl : function(){
10298         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10299         if(Roo.isIE7){
10300             this.layout.layout();
10301         }
10302     },
10303
10304     // private
10305     // Use the syncHeightBeforeShow config option to control this automatically
10306     syncBodyHeight : function(){
10307         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10308         if(this.layout){this.layout.layout();}
10309     },
10310     
10311       /**
10312      * Add an xtype element (actually adds to the layout.)
10313      * @return {Object} xdata xtype object data.
10314      */
10315     
10316     addxtype : function(c) {
10317         return this.layout.addxtype(c);
10318     }
10319 });/*
10320  * Based on:
10321  * Ext JS Library 1.1.1
10322  * Copyright(c) 2006-2007, Ext JS, LLC.
10323  *
10324  * Originally Released Under LGPL - original licence link has changed is not relivant.
10325  *
10326  * Fork - LGPL
10327  * <script type="text/javascript">
10328  */
10329  
10330 /**
10331  * @class Roo.MessageBox
10332  * @static
10333  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10334  * Example usage:
10335  *<pre><code>
10336 // Basic alert:
10337 Roo.Msg.alert('Status', 'Changes saved successfully.');
10338
10339 // Prompt for user data:
10340 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10341     if (btn == 'ok'){
10342         // process text value...
10343     }
10344 });
10345
10346 // Show a dialog using config options:
10347 Roo.Msg.show({
10348    title:'Save Changes?',
10349    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10350    buttons: Roo.Msg.YESNOCANCEL,
10351    fn: processResult,
10352    animEl: 'elId'
10353 });
10354 </code></pre>
10355  * @static
10356  */
10357 Roo.MessageBox = function(){
10358     var dlg, opt, mask, waitTimer;
10359     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10360     var buttons, activeTextEl, bwidth;
10361
10362     // private
10363     var handleButton = function(button){
10364         dlg.hide();
10365         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10366     };
10367
10368     // private
10369     var handleHide = function(){
10370         if(opt && opt.cls){
10371             dlg.el.removeClass(opt.cls);
10372         }
10373         if(waitTimer){
10374             Roo.TaskMgr.stop(waitTimer);
10375             waitTimer = null;
10376         }
10377     };
10378
10379     // private
10380     var updateButtons = function(b){
10381         var width = 0;
10382         if(!b){
10383             buttons["ok"].hide();
10384             buttons["cancel"].hide();
10385             buttons["yes"].hide();
10386             buttons["no"].hide();
10387             dlg.footer.dom.style.display = 'none';
10388             return width;
10389         }
10390         dlg.footer.dom.style.display = '';
10391         for(var k in buttons){
10392             if(typeof buttons[k] != "function"){
10393                 if(b[k]){
10394                     buttons[k].show();
10395                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10396                     width += buttons[k].el.getWidth()+15;
10397                 }else{
10398                     buttons[k].hide();
10399                 }
10400             }
10401         }
10402         return width;
10403     };
10404
10405     // private
10406     var handleEsc = function(d, k, e){
10407         if(opt && opt.closable !== false){
10408             dlg.hide();
10409         }
10410         if(e){
10411             e.stopEvent();
10412         }
10413     };
10414
10415     return {
10416         /**
10417          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10418          * @return {Roo.BasicDialog} The BasicDialog element
10419          */
10420         getDialog : function(){
10421            if(!dlg){
10422                 dlg = new Roo.BasicDialog("x-msg-box", {
10423                     autoCreate : true,
10424                     shadow: true,
10425                     draggable: true,
10426                     resizable:false,
10427                     constraintoviewport:false,
10428                     fixedcenter:true,
10429                     collapsible : false,
10430                     shim:true,
10431                     modal: true,
10432                     width:400, height:100,
10433                     buttonAlign:"center",
10434                     closeClick : function(){
10435                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10436                             handleButton("no");
10437                         }else{
10438                             handleButton("cancel");
10439                         }
10440                     }
10441                 });
10442               
10443                 dlg.on("hide", handleHide);
10444                 mask = dlg.mask;
10445                 dlg.addKeyListener(27, handleEsc);
10446                 buttons = {};
10447                 var bt = this.buttonText;
10448                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10449                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10450                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10451                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10452                 bodyEl = dlg.body.createChild({
10453
10454                     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>'
10455                 });
10456                 msgEl = bodyEl.dom.firstChild;
10457                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10458                 textboxEl.enableDisplayMode();
10459                 textboxEl.addKeyListener([10,13], function(){
10460                     if(dlg.isVisible() && opt && opt.buttons){
10461                         if(opt.buttons.ok){
10462                             handleButton("ok");
10463                         }else if(opt.buttons.yes){
10464                             handleButton("yes");
10465                         }
10466                     }
10467                 });
10468                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10469                 textareaEl.enableDisplayMode();
10470                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10471                 progressEl.enableDisplayMode();
10472                 var pf = progressEl.dom.firstChild;
10473                 if (pf) {
10474                     pp = Roo.get(pf.firstChild);
10475                     pp.setHeight(pf.offsetHeight);
10476                 }
10477                 
10478             }
10479             return dlg;
10480         },
10481
10482         /**
10483          * Updates the message box body text
10484          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10485          * the XHTML-compliant non-breaking space character '&amp;#160;')
10486          * @return {Roo.MessageBox} This message box
10487          */
10488         updateText : function(text){
10489             if(!dlg.isVisible() && !opt.width){
10490                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10491             }
10492             msgEl.innerHTML = text || '&#160;';
10493       
10494             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10495             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10496             var w = Math.max(
10497                     Math.min(opt.width || cw , this.maxWidth), 
10498                     Math.max(opt.minWidth || this.minWidth, bwidth)
10499             );
10500             if(opt.prompt){
10501                 activeTextEl.setWidth(w);
10502             }
10503             if(dlg.isVisible()){
10504                 dlg.fixedcenter = false;
10505             }
10506             // to big, make it scroll. = But as usual stupid IE does not support
10507             // !important..
10508             
10509             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10510                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10511                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10512             } else {
10513                 bodyEl.dom.style.height = '';
10514                 bodyEl.dom.style.overflowY = '';
10515             }
10516             if (cw > w) {
10517                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10518             } else {
10519                 bodyEl.dom.style.overflowX = '';
10520             }
10521             
10522             dlg.setContentSize(w, bodyEl.getHeight());
10523             if(dlg.isVisible()){
10524                 dlg.fixedcenter = true;
10525             }
10526             return this;
10527         },
10528
10529         /**
10530          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10531          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10532          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10533          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10534          * @return {Roo.MessageBox} This message box
10535          */
10536         updateProgress : function(value, text){
10537             if(text){
10538                 this.updateText(text);
10539             }
10540             if (pp) { // weird bug on my firefox - for some reason this is not defined
10541                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10542             }
10543             return this;
10544         },        
10545
10546         /**
10547          * Returns true if the message box is currently displayed
10548          * @return {Boolean} True if the message box is visible, else false
10549          */
10550         isVisible : function(){
10551             return dlg && dlg.isVisible();  
10552         },
10553
10554         /**
10555          * Hides the message box if it is displayed
10556          */
10557         hide : function(){
10558             if(this.isVisible()){
10559                 dlg.hide();
10560             }  
10561         },
10562
10563         /**
10564          * Displays a new message box, or reinitializes an existing message box, based on the config options
10565          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10566          * The following config object properties are supported:
10567          * <pre>
10568 Property    Type             Description
10569 ----------  ---------------  ------------------------------------------------------------------------------------
10570 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10571                                    closes (defaults to undefined)
10572 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10573                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10574 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10575                                    progress and wait dialogs will ignore this property and always hide the
10576                                    close button as they can only be closed programmatically.
10577 cls               String           A custom CSS class to apply to the message box element
10578 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10579                                    displayed (defaults to 75)
10580 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10581                                    function will be btn (the name of the button that was clicked, if applicable,
10582                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10583                                    Progress and wait dialogs will ignore this option since they do not respond to
10584                                    user actions and can only be closed programmatically, so any required function
10585                                    should be called by the same code after it closes the dialog.
10586 icon              String           A CSS class that provides a background image to be used as an icon for
10587                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10588 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10589 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10590 modal             Boolean          False to allow user interaction with the page while the message box is
10591                                    displayed (defaults to true)
10592 msg               String           A string that will replace the existing message box body text (defaults
10593                                    to the XHTML-compliant non-breaking space character '&#160;')
10594 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10595 progress          Boolean          True to display a progress bar (defaults to false)
10596 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10597 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10598 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10599 title             String           The title text
10600 value             String           The string value to set into the active textbox element if displayed
10601 wait              Boolean          True to display a progress bar (defaults to false)
10602 width             Number           The width of the dialog in pixels
10603 </pre>
10604          *
10605          * Example usage:
10606          * <pre><code>
10607 Roo.Msg.show({
10608    title: 'Address',
10609    msg: 'Please enter your address:',
10610    width: 300,
10611    buttons: Roo.MessageBox.OKCANCEL,
10612    multiline: true,
10613    fn: saveAddress,
10614    animEl: 'addAddressBtn'
10615 });
10616 </code></pre>
10617          * @param {Object} config Configuration options
10618          * @return {Roo.MessageBox} This message box
10619          */
10620         show : function(options)
10621         {
10622             
10623             // this causes nightmares if you show one dialog after another
10624             // especially on callbacks..
10625              
10626             if(this.isVisible()){
10627                 
10628                 this.hide();
10629                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10630                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10631                 Roo.log("New Dialog Message:" +  options.msg )
10632                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10633                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10634                 
10635             }
10636             var d = this.getDialog();
10637             opt = options;
10638             d.setTitle(opt.title || "&#160;");
10639             d.close.setDisplayed(opt.closable !== false);
10640             activeTextEl = textboxEl;
10641             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10642             if(opt.prompt){
10643                 if(opt.multiline){
10644                     textboxEl.hide();
10645                     textareaEl.show();
10646                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10647                         opt.multiline : this.defaultTextHeight);
10648                     activeTextEl = textareaEl;
10649                 }else{
10650                     textboxEl.show();
10651                     textareaEl.hide();
10652                 }
10653             }else{
10654                 textboxEl.hide();
10655                 textareaEl.hide();
10656             }
10657             progressEl.setDisplayed(opt.progress === true);
10658             this.updateProgress(0);
10659             activeTextEl.dom.value = opt.value || "";
10660             if(opt.prompt){
10661                 dlg.setDefaultButton(activeTextEl);
10662             }else{
10663                 var bs = opt.buttons;
10664                 var db = null;
10665                 if(bs && bs.ok){
10666                     db = buttons["ok"];
10667                 }else if(bs && bs.yes){
10668                     db = buttons["yes"];
10669                 }
10670                 dlg.setDefaultButton(db);
10671             }
10672             bwidth = updateButtons(opt.buttons);
10673             this.updateText(opt.msg);
10674             if(opt.cls){
10675                 d.el.addClass(opt.cls);
10676             }
10677             d.proxyDrag = opt.proxyDrag === true;
10678             d.modal = opt.modal !== false;
10679             d.mask = opt.modal !== false ? mask : false;
10680             if(!d.isVisible()){
10681                 // force it to the end of the z-index stack so it gets a cursor in FF
10682                 document.body.appendChild(dlg.el.dom);
10683                 d.animateTarget = null;
10684                 d.show(options.animEl);
10685             }
10686             dlg.toFront();
10687             return this;
10688         },
10689
10690         /**
10691          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10692          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10693          * and closing the message box when the process is complete.
10694          * @param {String} title The title bar text
10695          * @param {String} msg The message box body text
10696          * @return {Roo.MessageBox} This message box
10697          */
10698         progress : function(title, msg){
10699             this.show({
10700                 title : title,
10701                 msg : msg,
10702                 buttons: false,
10703                 progress:true,
10704                 closable:false,
10705                 minWidth: this.minProgressWidth,
10706                 modal : true
10707             });
10708             return this;
10709         },
10710
10711         /**
10712          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10713          * If a callback function is passed it will be called after the user clicks the button, and the
10714          * id of the button that was clicked will be passed as the only parameter to the callback
10715          * (could also be the top-right close button).
10716          * @param {String} title The title bar text
10717          * @param {String} msg The message box body text
10718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10719          * @param {Object} scope (optional) The scope of the callback function
10720          * @return {Roo.MessageBox} This message box
10721          */
10722         alert : function(title, msg, fn, scope){
10723             this.show({
10724                 title : title,
10725                 msg : msg,
10726                 buttons: this.OK,
10727                 fn: fn,
10728                 scope : scope,
10729                 modal : true
10730             });
10731             return this;
10732         },
10733
10734         /**
10735          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10736          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10737          * You are responsible for closing the message box when the process is complete.
10738          * @param {String} msg The message box body text
10739          * @param {String} title (optional) The title bar text
10740          * @return {Roo.MessageBox} This message box
10741          */
10742         wait : function(msg, title){
10743             this.show({
10744                 title : title,
10745                 msg : msg,
10746                 buttons: false,
10747                 closable:false,
10748                 progress:true,
10749                 modal:true,
10750                 width:300,
10751                 wait:true
10752             });
10753             waitTimer = Roo.TaskMgr.start({
10754                 run: function(i){
10755                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10756                 },
10757                 interval: 1000
10758             });
10759             return this;
10760         },
10761
10762         /**
10763          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10764          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10765          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10766          * @param {String} title The title bar text
10767          * @param {String} msg The message box body text
10768          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10769          * @param {Object} scope (optional) The scope of the callback function
10770          * @return {Roo.MessageBox} This message box
10771          */
10772         confirm : function(title, msg, fn, scope){
10773             this.show({
10774                 title : title,
10775                 msg : msg,
10776                 buttons: this.YESNO,
10777                 fn: fn,
10778                 scope : scope,
10779                 modal : true
10780             });
10781             return this;
10782         },
10783
10784         /**
10785          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10786          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10787          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10788          * (could also be the top-right close button) and the text that was entered will be passed as the two
10789          * parameters to the callback.
10790          * @param {String} title The title bar text
10791          * @param {String} msg The message box body text
10792          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10793          * @param {Object} scope (optional) The scope of the callback function
10794          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10795          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10796          * @return {Roo.MessageBox} This message box
10797          */
10798         prompt : function(title, msg, fn, scope, multiline){
10799             this.show({
10800                 title : title,
10801                 msg : msg,
10802                 buttons: this.OKCANCEL,
10803                 fn: fn,
10804                 minWidth:250,
10805                 scope : scope,
10806                 prompt:true,
10807                 multiline: multiline,
10808                 modal : true
10809             });
10810             return this;
10811         },
10812
10813         /**
10814          * Button config that displays a single OK button
10815          * @type Object
10816          */
10817         OK : {ok:true},
10818         /**
10819          * Button config that displays Yes and No buttons
10820          * @type Object
10821          */
10822         YESNO : {yes:true, no:true},
10823         /**
10824          * Button config that displays OK and Cancel buttons
10825          * @type Object
10826          */
10827         OKCANCEL : {ok:true, cancel:true},
10828         /**
10829          * Button config that displays Yes, No and Cancel buttons
10830          * @type Object
10831          */
10832         YESNOCANCEL : {yes:true, no:true, cancel:true},
10833
10834         /**
10835          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10836          * @type Number
10837          */
10838         defaultTextHeight : 75,
10839         /**
10840          * The maximum width in pixels of the message box (defaults to 600)
10841          * @type Number
10842          */
10843         maxWidth : 600,
10844         /**
10845          * The minimum width in pixels of the message box (defaults to 100)
10846          * @type Number
10847          */
10848         minWidth : 100,
10849         /**
10850          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10851          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10852          * @type Number
10853          */
10854         minProgressWidth : 250,
10855         /**
10856          * An object containing the default button text strings that can be overriden for localized language support.
10857          * Supported properties are: ok, cancel, yes and no.
10858          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10859          * @type Object
10860          */
10861         buttonText : {
10862             ok : "OK",
10863             cancel : "Cancel",
10864             yes : "Yes",
10865             no : "No"
10866         }
10867     };
10868 }();
10869
10870 /**
10871  * Shorthand for {@link Roo.MessageBox}
10872  */
10873 Roo.Msg = Roo.MessageBox;/*
10874  * Based on:
10875  * Ext JS Library 1.1.1
10876  * Copyright(c) 2006-2007, Ext JS, LLC.
10877  *
10878  * Originally Released Under LGPL - original licence link has changed is not relivant.
10879  *
10880  * Fork - LGPL
10881  * <script type="text/javascript">
10882  */
10883 /**
10884  * @class Roo.QuickTips
10885  * Provides attractive and customizable tooltips for any element.
10886  * @static
10887  */
10888 Roo.QuickTips = function(){
10889     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10890     var ce, bd, xy, dd;
10891     var visible = false, disabled = true, inited = false;
10892     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10893     
10894     var onOver = function(e){
10895         if(disabled){
10896             return;
10897         }
10898         var t = e.getTarget();
10899         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10900             return;
10901         }
10902         if(ce && t == ce.el){
10903             clearTimeout(hideProc);
10904             return;
10905         }
10906         if(t && tagEls[t.id]){
10907             tagEls[t.id].el = t;
10908             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10909             return;
10910         }
10911         var ttp, et = Roo.fly(t);
10912         var ns = cfg.namespace;
10913         if(tm.interceptTitles && t.title){
10914             ttp = t.title;
10915             t.qtip = ttp;
10916             t.removeAttribute("title");
10917             e.preventDefault();
10918         }else{
10919             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10920         }
10921         if(ttp){
10922             showProc = show.defer(tm.showDelay, tm, [{
10923                 el: t, 
10924                 text: ttp.replace(/\\n/g,'<br/>'),
10925                 width: et.getAttributeNS(ns, cfg.width),
10926                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10927                 title: et.getAttributeNS(ns, cfg.title),
10928                     cls: et.getAttributeNS(ns, cfg.cls)
10929             }]);
10930         }
10931     };
10932     
10933     var onOut = function(e){
10934         clearTimeout(showProc);
10935         var t = e.getTarget();
10936         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10937             hideProc = setTimeout(hide, tm.hideDelay);
10938         }
10939     };
10940     
10941     var onMove = function(e){
10942         if(disabled){
10943             return;
10944         }
10945         xy = e.getXY();
10946         xy[1] += 18;
10947         if(tm.trackMouse && ce){
10948             el.setXY(xy);
10949         }
10950     };
10951     
10952     var onDown = function(e){
10953         clearTimeout(showProc);
10954         clearTimeout(hideProc);
10955         if(!e.within(el)){
10956             if(tm.hideOnClick){
10957                 hide();
10958                 tm.disable();
10959                 tm.enable.defer(100, tm);
10960             }
10961         }
10962     };
10963     
10964     var getPad = function(){
10965         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10966     };
10967
10968     var show = function(o){
10969         if(disabled){
10970             return;
10971         }
10972         clearTimeout(dismissProc);
10973         ce = o;
10974         if(removeCls){ // in case manually hidden
10975             el.removeClass(removeCls);
10976             removeCls = null;
10977         }
10978         if(ce.cls){
10979             el.addClass(ce.cls);
10980             removeCls = ce.cls;
10981         }
10982         if(ce.title){
10983             tipTitle.update(ce.title);
10984             tipTitle.show();
10985         }else{
10986             tipTitle.update('');
10987             tipTitle.hide();
10988         }
10989         el.dom.style.width  = tm.maxWidth+'px';
10990         //tipBody.dom.style.width = '';
10991         tipBodyText.update(o.text);
10992         var p = getPad(), w = ce.width;
10993         if(!w){
10994             var td = tipBodyText.dom;
10995             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10996             if(aw > tm.maxWidth){
10997                 w = tm.maxWidth;
10998             }else if(aw < tm.minWidth){
10999                 w = tm.minWidth;
11000             }else{
11001                 w = aw;
11002             }
11003         }
11004         //tipBody.setWidth(w);
11005         el.setWidth(parseInt(w, 10) + p);
11006         if(ce.autoHide === false){
11007             close.setDisplayed(true);
11008             if(dd){
11009                 dd.unlock();
11010             }
11011         }else{
11012             close.setDisplayed(false);
11013             if(dd){
11014                 dd.lock();
11015             }
11016         }
11017         if(xy){
11018             el.avoidY = xy[1]-18;
11019             el.setXY(xy);
11020         }
11021         if(tm.animate){
11022             el.setOpacity(.1);
11023             el.setStyle("visibility", "visible");
11024             el.fadeIn({callback: afterShow});
11025         }else{
11026             afterShow();
11027         }
11028     };
11029     
11030     var afterShow = function(){
11031         if(ce){
11032             el.show();
11033             esc.enable();
11034             if(tm.autoDismiss && ce.autoHide !== false){
11035                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11036             }
11037         }
11038     };
11039     
11040     var hide = function(noanim){
11041         clearTimeout(dismissProc);
11042         clearTimeout(hideProc);
11043         ce = null;
11044         if(el.isVisible()){
11045             esc.disable();
11046             if(noanim !== true && tm.animate){
11047                 el.fadeOut({callback: afterHide});
11048             }else{
11049                 afterHide();
11050             } 
11051         }
11052     };
11053     
11054     var afterHide = function(){
11055         el.hide();
11056         if(removeCls){
11057             el.removeClass(removeCls);
11058             removeCls = null;
11059         }
11060     };
11061     
11062     return {
11063         /**
11064         * @cfg {Number} minWidth
11065         * The minimum width of the quick tip (defaults to 40)
11066         */
11067        minWidth : 40,
11068         /**
11069         * @cfg {Number} maxWidth
11070         * The maximum width of the quick tip (defaults to 300)
11071         */
11072        maxWidth : 300,
11073         /**
11074         * @cfg {Boolean} interceptTitles
11075         * True to automatically use the element's DOM title value if available (defaults to false)
11076         */
11077        interceptTitles : false,
11078         /**
11079         * @cfg {Boolean} trackMouse
11080         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11081         */
11082        trackMouse : false,
11083         /**
11084         * @cfg {Boolean} hideOnClick
11085         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11086         */
11087        hideOnClick : true,
11088         /**
11089         * @cfg {Number} showDelay
11090         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11091         */
11092        showDelay : 500,
11093         /**
11094         * @cfg {Number} hideDelay
11095         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11096         */
11097        hideDelay : 200,
11098         /**
11099         * @cfg {Boolean} autoHide
11100         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11101         * Used in conjunction with hideDelay.
11102         */
11103        autoHide : true,
11104         /**
11105         * @cfg {Boolean}
11106         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11107         * (defaults to true).  Used in conjunction with autoDismissDelay.
11108         */
11109        autoDismiss : true,
11110         /**
11111         * @cfg {Number}
11112         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11113         */
11114        autoDismissDelay : 5000,
11115        /**
11116         * @cfg {Boolean} animate
11117         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11118         */
11119        animate : false,
11120
11121        /**
11122         * @cfg {String} title
11123         * Title text to display (defaults to '').  This can be any valid HTML markup.
11124         */
11125         title: '',
11126        /**
11127         * @cfg {String} text
11128         * Body text to display (defaults to '').  This can be any valid HTML markup.
11129         */
11130         text : '',
11131        /**
11132         * @cfg {String} cls
11133         * A CSS class to apply to the base quick tip element (defaults to '').
11134         */
11135         cls : '',
11136        /**
11137         * @cfg {Number} width
11138         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11139         * minWidth or maxWidth.
11140         */
11141         width : null,
11142
11143     /**
11144      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11145      * or display QuickTips in a page.
11146      */
11147        init : function(){
11148           tm = Roo.QuickTips;
11149           cfg = tm.tagConfig;
11150           if(!inited){
11151               if(!Roo.isReady){ // allow calling of init() before onReady
11152                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11153                   return;
11154               }
11155               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11156               el.fxDefaults = {stopFx: true};
11157               // maximum custom styling
11158               //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>');
11159               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>');              
11160               tipTitle = el.child('h3');
11161               tipTitle.enableDisplayMode("block");
11162               tipBody = el.child('div.x-tip-bd');
11163               tipBodyText = el.child('div.x-tip-bd-inner');
11164               //bdLeft = el.child('div.x-tip-bd-left');
11165               //bdRight = el.child('div.x-tip-bd-right');
11166               close = el.child('div.x-tip-close');
11167               close.enableDisplayMode("block");
11168               close.on("click", hide);
11169               var d = Roo.get(document);
11170               d.on("mousedown", onDown);
11171               d.on("mouseover", onOver);
11172               d.on("mouseout", onOut);
11173               d.on("mousemove", onMove);
11174               esc = d.addKeyListener(27, hide);
11175               esc.disable();
11176               if(Roo.dd.DD){
11177                   dd = el.initDD("default", null, {
11178                       onDrag : function(){
11179                           el.sync();  
11180                       }
11181                   });
11182                   dd.setHandleElId(tipTitle.id);
11183                   dd.lock();
11184               }
11185               inited = true;
11186           }
11187           this.enable(); 
11188        },
11189
11190     /**
11191      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11192      * are supported:
11193      * <pre>
11194 Property    Type                   Description
11195 ----------  ---------------------  ------------------------------------------------------------------------
11196 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11197      * </ul>
11198      * @param {Object} config The config object
11199      */
11200        register : function(config){
11201            var cs = config instanceof Array ? config : arguments;
11202            for(var i = 0, len = cs.length; i < len; i++) {
11203                var c = cs[i];
11204                var target = c.target;
11205                if(target){
11206                    if(target instanceof Array){
11207                        for(var j = 0, jlen = target.length; j < jlen; j++){
11208                            tagEls[target[j]] = c;
11209                        }
11210                    }else{
11211                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11212                    }
11213                }
11214            }
11215        },
11216
11217     /**
11218      * Removes this quick tip from its element and destroys it.
11219      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11220      */
11221        unregister : function(el){
11222            delete tagEls[Roo.id(el)];
11223        },
11224
11225     /**
11226      * Enable this quick tip.
11227      */
11228        enable : function(){
11229            if(inited && disabled){
11230                locks.pop();
11231                if(locks.length < 1){
11232                    disabled = false;
11233                }
11234            }
11235        },
11236
11237     /**
11238      * Disable this quick tip.
11239      */
11240        disable : function(){
11241           disabled = true;
11242           clearTimeout(showProc);
11243           clearTimeout(hideProc);
11244           clearTimeout(dismissProc);
11245           if(ce){
11246               hide(true);
11247           }
11248           locks.push(1);
11249        },
11250
11251     /**
11252      * Returns true if the quick tip is enabled, else false.
11253      */
11254        isEnabled : function(){
11255             return !disabled;
11256        },
11257
11258         // private
11259        tagConfig : {
11260            namespace : "roo", // was ext?? this may break..
11261            alt_namespace : "ext",
11262            attribute : "qtip",
11263            width : "width",
11264            target : "target",
11265            title : "qtitle",
11266            hide : "hide",
11267            cls : "qclass"
11268        }
11269    };
11270 }();
11271
11272 // backwards compat
11273 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11274  * Based on:
11275  * Ext JS Library 1.1.1
11276  * Copyright(c) 2006-2007, Ext JS, LLC.
11277  *
11278  * Originally Released Under LGPL - original licence link has changed is not relivant.
11279  *
11280  * Fork - LGPL
11281  * <script type="text/javascript">
11282  */
11283  
11284
11285 /**
11286  * @class Roo.tree.TreePanel
11287  * @extends Roo.data.Tree
11288  * @cfg {Roo.tree.TreeNode} root The root node
11289  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11290  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11291  * @cfg {Boolean} enableDD true to enable drag and drop
11292  * @cfg {Boolean} enableDrag true to enable just drag
11293  * @cfg {Boolean} enableDrop true to enable just drop
11294  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11295  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11296  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11297  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11298  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11299  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11300  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11301  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11302  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11303  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11304  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11305  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11306  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11307  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11308  * @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>
11309  * @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>
11310  * 
11311  * @constructor
11312  * @param {String/HTMLElement/Element} el The container element
11313  * @param {Object} config
11314  */
11315 Roo.tree.TreePanel = function(el, config){
11316     var root = false;
11317     var loader = false;
11318     if (config.root) {
11319         root = config.root;
11320         delete config.root;
11321     }
11322     if (config.loader) {
11323         loader = config.loader;
11324         delete config.loader;
11325     }
11326     
11327     Roo.apply(this, config);
11328     Roo.tree.TreePanel.superclass.constructor.call(this);
11329     this.el = Roo.get(el);
11330     this.el.addClass('x-tree');
11331     //console.log(root);
11332     if (root) {
11333         this.setRootNode( Roo.factory(root, Roo.tree));
11334     }
11335     if (loader) {
11336         this.loader = Roo.factory(loader, Roo.tree);
11337     }
11338    /**
11339     * Read-only. The id of the container element becomes this TreePanel's id.
11340     */
11341     this.id = this.el.id;
11342     this.addEvents({
11343         /**
11344         * @event beforeload
11345         * Fires before a node is loaded, return false to cancel
11346         * @param {Node} node The node being loaded
11347         */
11348         "beforeload" : true,
11349         /**
11350         * @event load
11351         * Fires when a node is loaded
11352         * @param {Node} node The node that was loaded
11353         */
11354         "load" : true,
11355         /**
11356         * @event textchange
11357         * Fires when the text for a node is changed
11358         * @param {Node} node The node
11359         * @param {String} text The new text
11360         * @param {String} oldText The old text
11361         */
11362         "textchange" : true,
11363         /**
11364         * @event beforeexpand
11365         * Fires before a node is expanded, return false to cancel.
11366         * @param {Node} node The node
11367         * @param {Boolean} deep
11368         * @param {Boolean} anim
11369         */
11370         "beforeexpand" : true,
11371         /**
11372         * @event beforecollapse
11373         * Fires before a node is collapsed, return false to cancel.
11374         * @param {Node} node The node
11375         * @param {Boolean} deep
11376         * @param {Boolean} anim
11377         */
11378         "beforecollapse" : true,
11379         /**
11380         * @event expand
11381         * Fires when a node is expanded
11382         * @param {Node} node The node
11383         */
11384         "expand" : true,
11385         /**
11386         * @event disabledchange
11387         * Fires when the disabled status of a node changes
11388         * @param {Node} node The node
11389         * @param {Boolean} disabled
11390         */
11391         "disabledchange" : true,
11392         /**
11393         * @event collapse
11394         * Fires when a node is collapsed
11395         * @param {Node} node The node
11396         */
11397         "collapse" : true,
11398         /**
11399         * @event beforeclick
11400         * Fires before click processing on a node. Return false to cancel the default action.
11401         * @param {Node} node The node
11402         * @param {Roo.EventObject} e The event object
11403         */
11404         "beforeclick":true,
11405         /**
11406         * @event checkchange
11407         * Fires when a node with a checkbox's checked property changes
11408         * @param {Node} this This node
11409         * @param {Boolean} checked
11410         */
11411         "checkchange":true,
11412         /**
11413         * @event click
11414         * Fires when a node is clicked
11415         * @param {Node} node The node
11416         * @param {Roo.EventObject} e The event object
11417         */
11418         "click":true,
11419         /**
11420         * @event dblclick
11421         * Fires when a node is double clicked
11422         * @param {Node} node The node
11423         * @param {Roo.EventObject} e The event object
11424         */
11425         "dblclick":true,
11426         /**
11427         * @event contextmenu
11428         * Fires when a node is right clicked
11429         * @param {Node} node The node
11430         * @param {Roo.EventObject} e The event object
11431         */
11432         "contextmenu":true,
11433         /**
11434         * @event beforechildrenrendered
11435         * Fires right before the child nodes for a node are rendered
11436         * @param {Node} node The node
11437         */
11438         "beforechildrenrendered":true,
11439         /**
11440         * @event startdrag
11441         * Fires when a node starts being dragged
11442         * @param {Roo.tree.TreePanel} this
11443         * @param {Roo.tree.TreeNode} node
11444         * @param {event} e The raw browser event
11445         */ 
11446        "startdrag" : true,
11447        /**
11448         * @event enddrag
11449         * Fires when a drag operation is complete
11450         * @param {Roo.tree.TreePanel} this
11451         * @param {Roo.tree.TreeNode} node
11452         * @param {event} e The raw browser event
11453         */
11454        "enddrag" : true,
11455        /**
11456         * @event dragdrop
11457         * Fires when a dragged node is dropped on a valid DD target
11458         * @param {Roo.tree.TreePanel} this
11459         * @param {Roo.tree.TreeNode} node
11460         * @param {DD} dd The dd it was dropped on
11461         * @param {event} e The raw browser event
11462         */
11463        "dragdrop" : true,
11464        /**
11465         * @event beforenodedrop
11466         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11467         * passed to handlers has the following properties:<br />
11468         * <ul style="padding:5px;padding-left:16px;">
11469         * <li>tree - The TreePanel</li>
11470         * <li>target - The node being targeted for the drop</li>
11471         * <li>data - The drag data from the drag source</li>
11472         * <li>point - The point of the drop - append, above or below</li>
11473         * <li>source - The drag source</li>
11474         * <li>rawEvent - Raw mouse event</li>
11475         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11476         * to be inserted by setting them on this object.</li>
11477         * <li>cancel - Set this to true to cancel the drop.</li>
11478         * </ul>
11479         * @param {Object} dropEvent
11480         */
11481        "beforenodedrop" : true,
11482        /**
11483         * @event nodedrop
11484         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11485         * passed to handlers has the following properties:<br />
11486         * <ul style="padding:5px;padding-left:16px;">
11487         * <li>tree - The TreePanel</li>
11488         * <li>target - The node being targeted for the drop</li>
11489         * <li>data - The drag data from the drag source</li>
11490         * <li>point - The point of the drop - append, above or below</li>
11491         * <li>source - The drag source</li>
11492         * <li>rawEvent - Raw mouse event</li>
11493         * <li>dropNode - Dropped node(s).</li>
11494         * </ul>
11495         * @param {Object} dropEvent
11496         */
11497        "nodedrop" : true,
11498         /**
11499         * @event nodedragover
11500         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11501         * passed to handlers has the following properties:<br />
11502         * <ul style="padding:5px;padding-left:16px;">
11503         * <li>tree - The TreePanel</li>
11504         * <li>target - The node being targeted for the drop</li>
11505         * <li>data - The drag data from the drag source</li>
11506         * <li>point - The point of the drop - append, above or below</li>
11507         * <li>source - The drag source</li>
11508         * <li>rawEvent - Raw mouse event</li>
11509         * <li>dropNode - Drop node(s) provided by the source.</li>
11510         * <li>cancel - Set this to true to signal drop not allowed.</li>
11511         * </ul>
11512         * @param {Object} dragOverEvent
11513         */
11514        "nodedragover" : true,
11515        /**
11516         * @event appendnode
11517         * Fires when append node to the tree
11518         * @param {Roo.tree.TreePanel} this
11519         * @param {Roo.tree.TreeNode} node
11520         * @param {Number} index The index of the newly appended node
11521         */
11522        "appendnode" : true
11523         
11524     });
11525     if(this.singleExpand){
11526        this.on("beforeexpand", this.restrictExpand, this);
11527     }
11528     if (this.editor) {
11529         this.editor.tree = this;
11530         this.editor = Roo.factory(this.editor, Roo.tree);
11531     }
11532     
11533     if (this.selModel) {
11534         this.selModel = Roo.factory(this.selModel, Roo.tree);
11535     }
11536    
11537 };
11538 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11539     rootVisible : true,
11540     animate: Roo.enableFx,
11541     lines : true,
11542     enableDD : false,
11543     hlDrop : Roo.enableFx,
11544   
11545     renderer: false,
11546     
11547     rendererTip: false,
11548     // private
11549     restrictExpand : function(node){
11550         var p = node.parentNode;
11551         if(p){
11552             if(p.expandedChild && p.expandedChild.parentNode == p){
11553                 p.expandedChild.collapse();
11554             }
11555             p.expandedChild = node;
11556         }
11557     },
11558
11559     // private override
11560     setRootNode : function(node){
11561         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11562         if(!this.rootVisible){
11563             node.ui = new Roo.tree.RootTreeNodeUI(node);
11564         }
11565         return node;
11566     },
11567
11568     /**
11569      * Returns the container element for this TreePanel
11570      */
11571     getEl : function(){
11572         return this.el;
11573     },
11574
11575     /**
11576      * Returns the default TreeLoader for this TreePanel
11577      */
11578     getLoader : function(){
11579         return this.loader;
11580     },
11581
11582     /**
11583      * Expand all nodes
11584      */
11585     expandAll : function(){
11586         this.root.expand(true);
11587     },
11588
11589     /**
11590      * Collapse all nodes
11591      */
11592     collapseAll : function(){
11593         this.root.collapse(true);
11594     },
11595
11596     /**
11597      * Returns the selection model used by this TreePanel
11598      */
11599     getSelectionModel : function(){
11600         if(!this.selModel){
11601             this.selModel = new Roo.tree.DefaultSelectionModel();
11602         }
11603         return this.selModel;
11604     },
11605
11606     /**
11607      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11608      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11609      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11610      * @return {Array}
11611      */
11612     getChecked : function(a, startNode){
11613         startNode = startNode || this.root;
11614         var r = [];
11615         var f = function(){
11616             if(this.attributes.checked){
11617                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11618             }
11619         }
11620         startNode.cascade(f);
11621         return r;
11622     },
11623
11624     /**
11625      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11626      * @param {String} path
11627      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11628      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11629      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11630      */
11631     expandPath : function(path, attr, callback){
11632         attr = attr || "id";
11633         var keys = path.split(this.pathSeparator);
11634         var curNode = this.root;
11635         if(curNode.attributes[attr] != keys[1]){ // invalid root
11636             if(callback){
11637                 callback(false, null);
11638             }
11639             return;
11640         }
11641         var index = 1;
11642         var f = function(){
11643             if(++index == keys.length){
11644                 if(callback){
11645                     callback(true, curNode);
11646                 }
11647                 return;
11648             }
11649             var c = curNode.findChild(attr, keys[index]);
11650             if(!c){
11651                 if(callback){
11652                     callback(false, curNode);
11653                 }
11654                 return;
11655             }
11656             curNode = c;
11657             c.expand(false, false, f);
11658         };
11659         curNode.expand(false, false, f);
11660     },
11661
11662     /**
11663      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11664      * @param {String} path
11665      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11666      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11667      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11668      */
11669     selectPath : function(path, attr, callback){
11670         attr = attr || "id";
11671         var keys = path.split(this.pathSeparator);
11672         var v = keys.pop();
11673         if(keys.length > 0){
11674             var f = function(success, node){
11675                 if(success && node){
11676                     var n = node.findChild(attr, v);
11677                     if(n){
11678                         n.select();
11679                         if(callback){
11680                             callback(true, n);
11681                         }
11682                     }else if(callback){
11683                         callback(false, n);
11684                     }
11685                 }else{
11686                     if(callback){
11687                         callback(false, n);
11688                     }
11689                 }
11690             };
11691             this.expandPath(keys.join(this.pathSeparator), attr, f);
11692         }else{
11693             this.root.select();
11694             if(callback){
11695                 callback(true, this.root);
11696             }
11697         }
11698     },
11699
11700     getTreeEl : function(){
11701         return this.el;
11702     },
11703
11704     /**
11705      * Trigger rendering of this TreePanel
11706      */
11707     render : function(){
11708         if (this.innerCt) {
11709             return this; // stop it rendering more than once!!
11710         }
11711         
11712         this.innerCt = this.el.createChild({tag:"ul",
11713                cls:"x-tree-root-ct " +
11714                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11715
11716         if(this.containerScroll){
11717             Roo.dd.ScrollManager.register(this.el);
11718         }
11719         if((this.enableDD || this.enableDrop) && !this.dropZone){
11720            /**
11721             * The dropZone used by this tree if drop is enabled
11722             * @type Roo.tree.TreeDropZone
11723             */
11724              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11725                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11726            });
11727         }
11728         if((this.enableDD || this.enableDrag) && !this.dragZone){
11729            /**
11730             * The dragZone used by this tree if drag is enabled
11731             * @type Roo.tree.TreeDragZone
11732             */
11733             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11734                ddGroup: this.ddGroup || "TreeDD",
11735                scroll: this.ddScroll
11736            });
11737         }
11738         this.getSelectionModel().init(this);
11739         if (!this.root) {
11740             Roo.log("ROOT not set in tree");
11741             return this;
11742         }
11743         this.root.render();
11744         if(!this.rootVisible){
11745             this.root.renderChildren();
11746         }
11747         return this;
11748     }
11749 });/*
11750  * Based on:
11751  * Ext JS Library 1.1.1
11752  * Copyright(c) 2006-2007, Ext JS, LLC.
11753  *
11754  * Originally Released Under LGPL - original licence link has changed is not relivant.
11755  *
11756  * Fork - LGPL
11757  * <script type="text/javascript">
11758  */
11759  
11760
11761 /**
11762  * @class Roo.tree.DefaultSelectionModel
11763  * @extends Roo.util.Observable
11764  * The default single selection for a TreePanel.
11765  * @param {Object} cfg Configuration
11766  */
11767 Roo.tree.DefaultSelectionModel = function(cfg){
11768    this.selNode = null;
11769    
11770    
11771    
11772    this.addEvents({
11773        /**
11774         * @event selectionchange
11775         * Fires when the selected node changes
11776         * @param {DefaultSelectionModel} this
11777         * @param {TreeNode} node the new selection
11778         */
11779        "selectionchange" : true,
11780
11781        /**
11782         * @event beforeselect
11783         * Fires before the selected node changes, return false to cancel the change
11784         * @param {DefaultSelectionModel} this
11785         * @param {TreeNode} node the new selection
11786         * @param {TreeNode} node the old selection
11787         */
11788        "beforeselect" : true
11789    });
11790    
11791     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11792 };
11793
11794 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11795     init : function(tree){
11796         this.tree = tree;
11797         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11798         tree.on("click", this.onNodeClick, this);
11799     },
11800     
11801     onNodeClick : function(node, e){
11802         if (e.ctrlKey && this.selNode == node)  {
11803             this.unselect(node);
11804             return;
11805         }
11806         this.select(node);
11807     },
11808     
11809     /**
11810      * Select a node.
11811      * @param {TreeNode} node The node to select
11812      * @return {TreeNode} The selected node
11813      */
11814     select : function(node){
11815         var last = this.selNode;
11816         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11817             if(last){
11818                 last.ui.onSelectedChange(false);
11819             }
11820             this.selNode = node;
11821             node.ui.onSelectedChange(true);
11822             this.fireEvent("selectionchange", this, node, last);
11823         }
11824         return node;
11825     },
11826     
11827     /**
11828      * Deselect a node.
11829      * @param {TreeNode} node The node to unselect
11830      */
11831     unselect : function(node){
11832         if(this.selNode == node){
11833             this.clearSelections();
11834         }    
11835     },
11836     
11837     /**
11838      * Clear all selections
11839      */
11840     clearSelections : function(){
11841         var n = this.selNode;
11842         if(n){
11843             n.ui.onSelectedChange(false);
11844             this.selNode = null;
11845             this.fireEvent("selectionchange", this, null);
11846         }
11847         return n;
11848     },
11849     
11850     /**
11851      * Get the selected node
11852      * @return {TreeNode} The selected node
11853      */
11854     getSelectedNode : function(){
11855         return this.selNode;    
11856     },
11857     
11858     /**
11859      * Returns true if the node is selected
11860      * @param {TreeNode} node The node to check
11861      * @return {Boolean}
11862      */
11863     isSelected : function(node){
11864         return this.selNode == node;  
11865     },
11866
11867     /**
11868      * Selects the node above the selected node in the tree, intelligently walking the nodes
11869      * @return TreeNode The new selection
11870      */
11871     selectPrevious : function(){
11872         var s = this.selNode || this.lastSelNode;
11873         if(!s){
11874             return null;
11875         }
11876         var ps = s.previousSibling;
11877         if(ps){
11878             if(!ps.isExpanded() || ps.childNodes.length < 1){
11879                 return this.select(ps);
11880             } else{
11881                 var lc = ps.lastChild;
11882                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11883                     lc = lc.lastChild;
11884                 }
11885                 return this.select(lc);
11886             }
11887         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11888             return this.select(s.parentNode);
11889         }
11890         return null;
11891     },
11892
11893     /**
11894      * Selects the node above the selected node in the tree, intelligently walking the nodes
11895      * @return TreeNode The new selection
11896      */
11897     selectNext : function(){
11898         var s = this.selNode || this.lastSelNode;
11899         if(!s){
11900             return null;
11901         }
11902         if(s.firstChild && s.isExpanded()){
11903              return this.select(s.firstChild);
11904          }else if(s.nextSibling){
11905              return this.select(s.nextSibling);
11906          }else if(s.parentNode){
11907             var newS = null;
11908             s.parentNode.bubble(function(){
11909                 if(this.nextSibling){
11910                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11911                     return false;
11912                 }
11913             });
11914             return newS;
11915          }
11916         return null;
11917     },
11918
11919     onKeyDown : function(e){
11920         var s = this.selNode || this.lastSelNode;
11921         // undesirable, but required
11922         var sm = this;
11923         if(!s){
11924             return;
11925         }
11926         var k = e.getKey();
11927         switch(k){
11928              case e.DOWN:
11929                  e.stopEvent();
11930                  this.selectNext();
11931              break;
11932              case e.UP:
11933                  e.stopEvent();
11934                  this.selectPrevious();
11935              break;
11936              case e.RIGHT:
11937                  e.preventDefault();
11938                  if(s.hasChildNodes()){
11939                      if(!s.isExpanded()){
11940                          s.expand();
11941                      }else if(s.firstChild){
11942                          this.select(s.firstChild, e);
11943                      }
11944                  }
11945              break;
11946              case e.LEFT:
11947                  e.preventDefault();
11948                  if(s.hasChildNodes() && s.isExpanded()){
11949                      s.collapse();
11950                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11951                      this.select(s.parentNode, e);
11952                  }
11953              break;
11954         };
11955     }
11956 });
11957
11958 /**
11959  * @class Roo.tree.MultiSelectionModel
11960  * @extends Roo.util.Observable
11961  * Multi selection for a TreePanel.
11962  * @param {Object} cfg Configuration
11963  */
11964 Roo.tree.MultiSelectionModel = function(){
11965    this.selNodes = [];
11966    this.selMap = {};
11967    this.addEvents({
11968        /**
11969         * @event selectionchange
11970         * Fires when the selected nodes change
11971         * @param {MultiSelectionModel} this
11972         * @param {Array} nodes Array of the selected nodes
11973         */
11974        "selectionchange" : true
11975    });
11976    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11977    
11978 };
11979
11980 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11981     init : function(tree){
11982         this.tree = tree;
11983         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11984         tree.on("click", this.onNodeClick, this);
11985     },
11986     
11987     onNodeClick : function(node, e){
11988         this.select(node, e, e.ctrlKey);
11989     },
11990     
11991     /**
11992      * Select a node.
11993      * @param {TreeNode} node The node to select
11994      * @param {EventObject} e (optional) An event associated with the selection
11995      * @param {Boolean} keepExisting True to retain existing selections
11996      * @return {TreeNode} The selected node
11997      */
11998     select : function(node, e, keepExisting){
11999         if(keepExisting !== true){
12000             this.clearSelections(true);
12001         }
12002         if(this.isSelected(node)){
12003             this.lastSelNode = node;
12004             return node;
12005         }
12006         this.selNodes.push(node);
12007         this.selMap[node.id] = node;
12008         this.lastSelNode = node;
12009         node.ui.onSelectedChange(true);
12010         this.fireEvent("selectionchange", this, this.selNodes);
12011         return node;
12012     },
12013     
12014     /**
12015      * Deselect a node.
12016      * @param {TreeNode} node The node to unselect
12017      */
12018     unselect : function(node){
12019         if(this.selMap[node.id]){
12020             node.ui.onSelectedChange(false);
12021             var sn = this.selNodes;
12022             var index = -1;
12023             if(sn.indexOf){
12024                 index = sn.indexOf(node);
12025             }else{
12026                 for(var i = 0, len = sn.length; i < len; i++){
12027                     if(sn[i] == node){
12028                         index = i;
12029                         break;
12030                     }
12031                 }
12032             }
12033             if(index != -1){
12034                 this.selNodes.splice(index, 1);
12035             }
12036             delete this.selMap[node.id];
12037             this.fireEvent("selectionchange", this, this.selNodes);
12038         }
12039     },
12040     
12041     /**
12042      * Clear all selections
12043      */
12044     clearSelections : function(suppressEvent){
12045         var sn = this.selNodes;
12046         if(sn.length > 0){
12047             for(var i = 0, len = sn.length; i < len; i++){
12048                 sn[i].ui.onSelectedChange(false);
12049             }
12050             this.selNodes = [];
12051             this.selMap = {};
12052             if(suppressEvent !== true){
12053                 this.fireEvent("selectionchange", this, this.selNodes);
12054             }
12055         }
12056     },
12057     
12058     /**
12059      * Returns true if the node is selected
12060      * @param {TreeNode} node The node to check
12061      * @return {Boolean}
12062      */
12063     isSelected : function(node){
12064         return this.selMap[node.id] ? true : false;  
12065     },
12066     
12067     /**
12068      * Returns an array of the selected nodes
12069      * @return {Array}
12070      */
12071     getSelectedNodes : function(){
12072         return this.selNodes;    
12073     },
12074
12075     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12076
12077     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12078
12079     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12080 });/*
12081  * Based on:
12082  * Ext JS Library 1.1.1
12083  * Copyright(c) 2006-2007, Ext JS, LLC.
12084  *
12085  * Originally Released Under LGPL - original licence link has changed is not relivant.
12086  *
12087  * Fork - LGPL
12088  * <script type="text/javascript">
12089  */
12090  
12091 /**
12092  * @class Roo.tree.TreeNode
12093  * @extends Roo.data.Node
12094  * @cfg {String} text The text for this node
12095  * @cfg {Boolean} expanded true to start the node expanded
12096  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12097  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12098  * @cfg {Boolean} disabled true to start the node disabled
12099  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12100  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12101  * @cfg {String} cls A css class to be added to the node
12102  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12103  * @cfg {String} href URL of the link used for the node (defaults to #)
12104  * @cfg {String} hrefTarget target frame for the link
12105  * @cfg {String} qtip An Ext QuickTip for the node
12106  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12107  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12108  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12109  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12110  * (defaults to undefined with no checkbox rendered)
12111  * @constructor
12112  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12113  */
12114 Roo.tree.TreeNode = function(attributes){
12115     attributes = attributes || {};
12116     if(typeof attributes == "string"){
12117         attributes = {text: attributes};
12118     }
12119     this.childrenRendered = false;
12120     this.rendered = false;
12121     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12122     this.expanded = attributes.expanded === true;
12123     this.isTarget = attributes.isTarget !== false;
12124     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12125     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12126
12127     /**
12128      * Read-only. The text for this node. To change it use setText().
12129      * @type String
12130      */
12131     this.text = attributes.text;
12132     /**
12133      * True if this node is disabled.
12134      * @type Boolean
12135      */
12136     this.disabled = attributes.disabled === true;
12137
12138     this.addEvents({
12139         /**
12140         * @event textchange
12141         * Fires when the text for this node is changed
12142         * @param {Node} this This node
12143         * @param {String} text The new text
12144         * @param {String} oldText The old text
12145         */
12146         "textchange" : true,
12147         /**
12148         * @event beforeexpand
12149         * Fires before this node is expanded, return false to cancel.
12150         * @param {Node} this This node
12151         * @param {Boolean} deep
12152         * @param {Boolean} anim
12153         */
12154         "beforeexpand" : true,
12155         /**
12156         * @event beforecollapse
12157         * Fires before this node is collapsed, return false to cancel.
12158         * @param {Node} this This node
12159         * @param {Boolean} deep
12160         * @param {Boolean} anim
12161         */
12162         "beforecollapse" : true,
12163         /**
12164         * @event expand
12165         * Fires when this node is expanded
12166         * @param {Node} this This node
12167         */
12168         "expand" : true,
12169         /**
12170         * @event disabledchange
12171         * Fires when the disabled status of this node changes
12172         * @param {Node} this This node
12173         * @param {Boolean} disabled
12174         */
12175         "disabledchange" : true,
12176         /**
12177         * @event collapse
12178         * Fires when this node is collapsed
12179         * @param {Node} this This node
12180         */
12181         "collapse" : true,
12182         /**
12183         * @event beforeclick
12184         * Fires before click processing. Return false to cancel the default action.
12185         * @param {Node} this This node
12186         * @param {Roo.EventObject} e The event object
12187         */
12188         "beforeclick":true,
12189         /**
12190         * @event checkchange
12191         * Fires when a node with a checkbox's checked property changes
12192         * @param {Node} this This node
12193         * @param {Boolean} checked
12194         */
12195         "checkchange":true,
12196         /**
12197         * @event click
12198         * Fires when this node is clicked
12199         * @param {Node} this This node
12200         * @param {Roo.EventObject} e The event object
12201         */
12202         "click":true,
12203         /**
12204         * @event dblclick
12205         * Fires when this node is double clicked
12206         * @param {Node} this This node
12207         * @param {Roo.EventObject} e The event object
12208         */
12209         "dblclick":true,
12210         /**
12211         * @event contextmenu
12212         * Fires when this node is right clicked
12213         * @param {Node} this This node
12214         * @param {Roo.EventObject} e The event object
12215         */
12216         "contextmenu":true,
12217         /**
12218         * @event beforechildrenrendered
12219         * Fires right before the child nodes for this node are rendered
12220         * @param {Node} this This node
12221         */
12222         "beforechildrenrendered":true
12223     });
12224
12225     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12226
12227     /**
12228      * Read-only. The UI for this node
12229      * @type TreeNodeUI
12230      */
12231     this.ui = new uiClass(this);
12232     
12233     // finally support items[]
12234     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12235         return;
12236     }
12237     
12238     
12239     Roo.each(this.attributes.items, function(c) {
12240         this.appendChild(Roo.factory(c,Roo.Tree));
12241     }, this);
12242     delete this.attributes.items;
12243     
12244     
12245     
12246 };
12247 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12248     preventHScroll: true,
12249     /**
12250      * Returns true if this node is expanded
12251      * @return {Boolean}
12252      */
12253     isExpanded : function(){
12254         return this.expanded;
12255     },
12256
12257     /**
12258      * Returns the UI object for this node
12259      * @return {TreeNodeUI}
12260      */
12261     getUI : function(){
12262         return this.ui;
12263     },
12264
12265     // private override
12266     setFirstChild : function(node){
12267         var of = this.firstChild;
12268         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12269         if(this.childrenRendered && of && node != of){
12270             of.renderIndent(true, true);
12271         }
12272         if(this.rendered){
12273             this.renderIndent(true, true);
12274         }
12275     },
12276
12277     // private override
12278     setLastChild : function(node){
12279         var ol = this.lastChild;
12280         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12281         if(this.childrenRendered && ol && node != ol){
12282             ol.renderIndent(true, true);
12283         }
12284         if(this.rendered){
12285             this.renderIndent(true, true);
12286         }
12287     },
12288
12289     // these methods are overridden to provide lazy rendering support
12290     // private override
12291     appendChild : function()
12292     {
12293         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12294         if(node && this.childrenRendered){
12295             node.render();
12296         }
12297         this.ui.updateExpandIcon();
12298         return node;
12299     },
12300
12301     // private override
12302     removeChild : function(node){
12303         this.ownerTree.getSelectionModel().unselect(node);
12304         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12305         // if it's been rendered remove dom node
12306         if(this.childrenRendered){
12307             node.ui.remove();
12308         }
12309         if(this.childNodes.length < 1){
12310             this.collapse(false, false);
12311         }else{
12312             this.ui.updateExpandIcon();
12313         }
12314         if(!this.firstChild) {
12315             this.childrenRendered = false;
12316         }
12317         return node;
12318     },
12319
12320     // private override
12321     insertBefore : function(node, refNode){
12322         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12323         if(newNode && refNode && this.childrenRendered){
12324             node.render();
12325         }
12326         this.ui.updateExpandIcon();
12327         return newNode;
12328     },
12329
12330     /**
12331      * Sets the text for this node
12332      * @param {String} text
12333      */
12334     setText : function(text){
12335         var oldText = this.text;
12336         this.text = text;
12337         this.attributes.text = text;
12338         if(this.rendered){ // event without subscribing
12339             this.ui.onTextChange(this, text, oldText);
12340         }
12341         this.fireEvent("textchange", this, text, oldText);
12342     },
12343
12344     /**
12345      * Triggers selection of this node
12346      */
12347     select : function(){
12348         this.getOwnerTree().getSelectionModel().select(this);
12349     },
12350
12351     /**
12352      * Triggers deselection of this node
12353      */
12354     unselect : function(){
12355         this.getOwnerTree().getSelectionModel().unselect(this);
12356     },
12357
12358     /**
12359      * Returns true if this node is selected
12360      * @return {Boolean}
12361      */
12362     isSelected : function(){
12363         return this.getOwnerTree().getSelectionModel().isSelected(this);
12364     },
12365
12366     /**
12367      * Expand this node.
12368      * @param {Boolean} deep (optional) True to expand all children as well
12369      * @param {Boolean} anim (optional) false to cancel the default animation
12370      * @param {Function} callback (optional) A callback to be called when
12371      * expanding this node completes (does not wait for deep expand to complete).
12372      * Called with 1 parameter, this node.
12373      */
12374     expand : function(deep, anim, callback){
12375         if(!this.expanded){
12376             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12377                 return;
12378             }
12379             if(!this.childrenRendered){
12380                 this.renderChildren();
12381             }
12382             this.expanded = true;
12383             
12384             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12385                 this.ui.animExpand(function(){
12386                     this.fireEvent("expand", this);
12387                     if(typeof callback == "function"){
12388                         callback(this);
12389                     }
12390                     if(deep === true){
12391                         this.expandChildNodes(true);
12392                     }
12393                 }.createDelegate(this));
12394                 return;
12395             }else{
12396                 this.ui.expand();
12397                 this.fireEvent("expand", this);
12398                 if(typeof callback == "function"){
12399                     callback(this);
12400                 }
12401             }
12402         }else{
12403            if(typeof callback == "function"){
12404                callback(this);
12405            }
12406         }
12407         if(deep === true){
12408             this.expandChildNodes(true);
12409         }
12410     },
12411
12412     isHiddenRoot : function(){
12413         return this.isRoot && !this.getOwnerTree().rootVisible;
12414     },
12415
12416     /**
12417      * Collapse this node.
12418      * @param {Boolean} deep (optional) True to collapse all children as well
12419      * @param {Boolean} anim (optional) false to cancel the default animation
12420      */
12421     collapse : function(deep, anim){
12422         if(this.expanded && !this.isHiddenRoot()){
12423             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12424                 return;
12425             }
12426             this.expanded = false;
12427             if((this.getOwnerTree().animate && anim !== false) || anim){
12428                 this.ui.animCollapse(function(){
12429                     this.fireEvent("collapse", this);
12430                     if(deep === true){
12431                         this.collapseChildNodes(true);
12432                     }
12433                 }.createDelegate(this));
12434                 return;
12435             }else{
12436                 this.ui.collapse();
12437                 this.fireEvent("collapse", this);
12438             }
12439         }
12440         if(deep === true){
12441             var cs = this.childNodes;
12442             for(var i = 0, len = cs.length; i < len; i++) {
12443                 cs[i].collapse(true, false);
12444             }
12445         }
12446     },
12447
12448     // private
12449     delayedExpand : function(delay){
12450         if(!this.expandProcId){
12451             this.expandProcId = this.expand.defer(delay, this);
12452         }
12453     },
12454
12455     // private
12456     cancelExpand : function(){
12457         if(this.expandProcId){
12458             clearTimeout(this.expandProcId);
12459         }
12460         this.expandProcId = false;
12461     },
12462
12463     /**
12464      * Toggles expanded/collapsed state of the node
12465      */
12466     toggle : function(){
12467         if(this.expanded){
12468             this.collapse();
12469         }else{
12470             this.expand();
12471         }
12472     },
12473
12474     /**
12475      * Ensures all parent nodes are expanded
12476      */
12477     ensureVisible : function(callback){
12478         var tree = this.getOwnerTree();
12479         tree.expandPath(this.parentNode.getPath(), false, function(){
12480             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12481             Roo.callback(callback);
12482         }.createDelegate(this));
12483     },
12484
12485     /**
12486      * Expand all child nodes
12487      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12488      */
12489     expandChildNodes : function(deep){
12490         var cs = this.childNodes;
12491         for(var i = 0, len = cs.length; i < len; i++) {
12492                 cs[i].expand(deep);
12493         }
12494     },
12495
12496     /**
12497      * Collapse all child nodes
12498      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12499      */
12500     collapseChildNodes : function(deep){
12501         var cs = this.childNodes;
12502         for(var i = 0, len = cs.length; i < len; i++) {
12503                 cs[i].collapse(deep);
12504         }
12505     },
12506
12507     /**
12508      * Disables this node
12509      */
12510     disable : function(){
12511         this.disabled = true;
12512         this.unselect();
12513         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12514             this.ui.onDisableChange(this, true);
12515         }
12516         this.fireEvent("disabledchange", this, true);
12517     },
12518
12519     /**
12520      * Enables this node
12521      */
12522     enable : function(){
12523         this.disabled = false;
12524         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12525             this.ui.onDisableChange(this, false);
12526         }
12527         this.fireEvent("disabledchange", this, false);
12528     },
12529
12530     // private
12531     renderChildren : function(suppressEvent){
12532         if(suppressEvent !== false){
12533             this.fireEvent("beforechildrenrendered", this);
12534         }
12535         var cs = this.childNodes;
12536         for(var i = 0, len = cs.length; i < len; i++){
12537             cs[i].render(true);
12538         }
12539         this.childrenRendered = true;
12540     },
12541
12542     // private
12543     sort : function(fn, scope){
12544         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12545         if(this.childrenRendered){
12546             var cs = this.childNodes;
12547             for(var i = 0, len = cs.length; i < len; i++){
12548                 cs[i].render(true);
12549             }
12550         }
12551     },
12552
12553     // private
12554     render : function(bulkRender){
12555         this.ui.render(bulkRender);
12556         if(!this.rendered){
12557             this.rendered = true;
12558             if(this.expanded){
12559                 this.expanded = false;
12560                 this.expand(false, false);
12561             }
12562         }
12563     },
12564
12565     // private
12566     renderIndent : function(deep, refresh){
12567         if(refresh){
12568             this.ui.childIndent = null;
12569         }
12570         this.ui.renderIndent();
12571         if(deep === true && this.childrenRendered){
12572             var cs = this.childNodes;
12573             for(var i = 0, len = cs.length; i < len; i++){
12574                 cs[i].renderIndent(true, refresh);
12575             }
12576         }
12577     }
12578 });/*
12579  * Based on:
12580  * Ext JS Library 1.1.1
12581  * Copyright(c) 2006-2007, Ext JS, LLC.
12582  *
12583  * Originally Released Under LGPL - original licence link has changed is not relivant.
12584  *
12585  * Fork - LGPL
12586  * <script type="text/javascript">
12587  */
12588  
12589 /**
12590  * @class Roo.tree.AsyncTreeNode
12591  * @extends Roo.tree.TreeNode
12592  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12593  * @constructor
12594  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12595  */
12596  Roo.tree.AsyncTreeNode = function(config){
12597     this.loaded = false;
12598     this.loading = false;
12599     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12600     /**
12601     * @event beforeload
12602     * Fires before this node is loaded, return false to cancel
12603     * @param {Node} this This node
12604     */
12605     this.addEvents({'beforeload':true, 'load': true});
12606     /**
12607     * @event load
12608     * Fires when this node is loaded
12609     * @param {Node} this This node
12610     */
12611     /**
12612      * The loader used by this node (defaults to using the tree's defined loader)
12613      * @type TreeLoader
12614      * @property loader
12615      */
12616 };
12617 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12618     expand : function(deep, anim, callback){
12619         if(this.loading){ // if an async load is already running, waiting til it's done
12620             var timer;
12621             var f = function(){
12622                 if(!this.loading){ // done loading
12623                     clearInterval(timer);
12624                     this.expand(deep, anim, callback);
12625                 }
12626             }.createDelegate(this);
12627             timer = setInterval(f, 200);
12628             return;
12629         }
12630         if(!this.loaded){
12631             if(this.fireEvent("beforeload", this) === false){
12632                 return;
12633             }
12634             this.loading = true;
12635             this.ui.beforeLoad(this);
12636             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12637             if(loader){
12638                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12639                 return;
12640             }
12641         }
12642         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12643     },
12644     
12645     /**
12646      * Returns true if this node is currently loading
12647      * @return {Boolean}
12648      */
12649     isLoading : function(){
12650         return this.loading;  
12651     },
12652     
12653     loadComplete : function(deep, anim, callback){
12654         this.loading = false;
12655         this.loaded = true;
12656         this.ui.afterLoad(this);
12657         this.fireEvent("load", this);
12658         this.expand(deep, anim, callback);
12659     },
12660     
12661     /**
12662      * Returns true if this node has been loaded
12663      * @return {Boolean}
12664      */
12665     isLoaded : function(){
12666         return this.loaded;
12667     },
12668     
12669     hasChildNodes : function(){
12670         if(!this.isLeaf() && !this.loaded){
12671             return true;
12672         }else{
12673             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12674         }
12675     },
12676
12677     /**
12678      * Trigger a reload for this node
12679      * @param {Function} callback
12680      */
12681     reload : function(callback){
12682         this.collapse(false, false);
12683         while(this.firstChild){
12684             this.removeChild(this.firstChild);
12685         }
12686         this.childrenRendered = false;
12687         this.loaded = false;
12688         if(this.isHiddenRoot()){
12689             this.expanded = false;
12690         }
12691         this.expand(false, false, callback);
12692     }
12693 });/*
12694  * Based on:
12695  * Ext JS Library 1.1.1
12696  * Copyright(c) 2006-2007, Ext JS, LLC.
12697  *
12698  * Originally Released Under LGPL - original licence link has changed is not relivant.
12699  *
12700  * Fork - LGPL
12701  * <script type="text/javascript">
12702  */
12703  
12704 /**
12705  * @class Roo.tree.TreeNodeUI
12706  * @constructor
12707  * @param {Object} node The node to render
12708  * The TreeNode UI implementation is separate from the
12709  * tree implementation. Unless you are customizing the tree UI,
12710  * you should never have to use this directly.
12711  */
12712 Roo.tree.TreeNodeUI = function(node){
12713     this.node = node;
12714     this.rendered = false;
12715     this.animating = false;
12716     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12717 };
12718
12719 Roo.tree.TreeNodeUI.prototype = {
12720     removeChild : function(node){
12721         if(this.rendered){
12722             this.ctNode.removeChild(node.ui.getEl());
12723         }
12724     },
12725
12726     beforeLoad : function(){
12727          this.addClass("x-tree-node-loading");
12728     },
12729
12730     afterLoad : function(){
12731          this.removeClass("x-tree-node-loading");
12732     },
12733
12734     onTextChange : function(node, text, oldText){
12735         if(this.rendered){
12736             this.textNode.innerHTML = text;
12737         }
12738     },
12739
12740     onDisableChange : function(node, state){
12741         this.disabled = state;
12742         if(state){
12743             this.addClass("x-tree-node-disabled");
12744         }else{
12745             this.removeClass("x-tree-node-disabled");
12746         }
12747     },
12748
12749     onSelectedChange : function(state){
12750         if(state){
12751             this.focus();
12752             this.addClass("x-tree-selected");
12753         }else{
12754             //this.blur();
12755             this.removeClass("x-tree-selected");
12756         }
12757     },
12758
12759     onMove : function(tree, node, oldParent, newParent, index, refNode){
12760         this.childIndent = null;
12761         if(this.rendered){
12762             var targetNode = newParent.ui.getContainer();
12763             if(!targetNode){//target not rendered
12764                 this.holder = document.createElement("div");
12765                 this.holder.appendChild(this.wrap);
12766                 return;
12767             }
12768             var insertBefore = refNode ? refNode.ui.getEl() : null;
12769             if(insertBefore){
12770                 targetNode.insertBefore(this.wrap, insertBefore);
12771             }else{
12772                 targetNode.appendChild(this.wrap);
12773             }
12774             this.node.renderIndent(true);
12775         }
12776     },
12777
12778     addClass : function(cls){
12779         if(this.elNode){
12780             Roo.fly(this.elNode).addClass(cls);
12781         }
12782     },
12783
12784     removeClass : function(cls){
12785         if(this.elNode){
12786             Roo.fly(this.elNode).removeClass(cls);
12787         }
12788     },
12789
12790     remove : function(){
12791         if(this.rendered){
12792             this.holder = document.createElement("div");
12793             this.holder.appendChild(this.wrap);
12794         }
12795     },
12796
12797     fireEvent : function(){
12798         return this.node.fireEvent.apply(this.node, arguments);
12799     },
12800
12801     initEvents : function(){
12802         this.node.on("move", this.onMove, this);
12803         var E = Roo.EventManager;
12804         var a = this.anchor;
12805
12806         var el = Roo.fly(a, '_treeui');
12807
12808         if(Roo.isOpera){ // opera render bug ignores the CSS
12809             el.setStyle("text-decoration", "none");
12810         }
12811
12812         el.on("click", this.onClick, this);
12813         el.on("dblclick", this.onDblClick, this);
12814
12815         if(this.checkbox){
12816             Roo.EventManager.on(this.checkbox,
12817                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12818         }
12819
12820         el.on("contextmenu", this.onContextMenu, this);
12821
12822         var icon = Roo.fly(this.iconNode);
12823         icon.on("click", this.onClick, this);
12824         icon.on("dblclick", this.onDblClick, this);
12825         icon.on("contextmenu", this.onContextMenu, this);
12826         E.on(this.ecNode, "click", this.ecClick, this, true);
12827
12828         if(this.node.disabled){
12829             this.addClass("x-tree-node-disabled");
12830         }
12831         if(this.node.hidden){
12832             this.addClass("x-tree-node-disabled");
12833         }
12834         var ot = this.node.getOwnerTree();
12835         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12836         if(dd && (!this.node.isRoot || ot.rootVisible)){
12837             Roo.dd.Registry.register(this.elNode, {
12838                 node: this.node,
12839                 handles: this.getDDHandles(),
12840                 isHandle: false
12841             });
12842         }
12843     },
12844
12845     getDDHandles : function(){
12846         return [this.iconNode, this.textNode];
12847     },
12848
12849     hide : function(){
12850         if(this.rendered){
12851             this.wrap.style.display = "none";
12852         }
12853     },
12854
12855     show : function(){
12856         if(this.rendered){
12857             this.wrap.style.display = "";
12858         }
12859     },
12860
12861     onContextMenu : function(e){
12862         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12863             e.preventDefault();
12864             this.focus();
12865             this.fireEvent("contextmenu", this.node, e);
12866         }
12867     },
12868
12869     onClick : function(e){
12870         if(this.dropping){
12871             e.stopEvent();
12872             return;
12873         }
12874         if(this.fireEvent("beforeclick", this.node, e) !== false){
12875             if(!this.disabled && this.node.attributes.href){
12876                 this.fireEvent("click", this.node, e);
12877                 return;
12878             }
12879             e.preventDefault();
12880             if(this.disabled){
12881                 return;
12882             }
12883
12884             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12885                 this.node.toggle();
12886             }
12887
12888             this.fireEvent("click", this.node, e);
12889         }else{
12890             e.stopEvent();
12891         }
12892     },
12893
12894     onDblClick : function(e){
12895         e.preventDefault();
12896         if(this.disabled){
12897             return;
12898         }
12899         if(this.checkbox){
12900             this.toggleCheck();
12901         }
12902         if(!this.animating && this.node.hasChildNodes()){
12903             this.node.toggle();
12904         }
12905         this.fireEvent("dblclick", this.node, e);
12906     },
12907
12908     onCheckChange : function(){
12909         var checked = this.checkbox.checked;
12910         this.node.attributes.checked = checked;
12911         this.fireEvent('checkchange', this.node, checked);
12912     },
12913
12914     ecClick : function(e){
12915         if(!this.animating && this.node.hasChildNodes()){
12916             this.node.toggle();
12917         }
12918     },
12919
12920     startDrop : function(){
12921         this.dropping = true;
12922     },
12923
12924     // delayed drop so the click event doesn't get fired on a drop
12925     endDrop : function(){
12926        setTimeout(function(){
12927            this.dropping = false;
12928        }.createDelegate(this), 50);
12929     },
12930
12931     expand : function(){
12932         this.updateExpandIcon();
12933         this.ctNode.style.display = "";
12934     },
12935
12936     focus : function(){
12937         if(!this.node.preventHScroll){
12938             try{this.anchor.focus();
12939             }catch(e){}
12940         }else if(!Roo.isIE){
12941             try{
12942                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12943                 var l = noscroll.scrollLeft;
12944                 this.anchor.focus();
12945                 noscroll.scrollLeft = l;
12946             }catch(e){}
12947         }
12948     },
12949
12950     toggleCheck : function(value){
12951         var cb = this.checkbox;
12952         if(cb){
12953             cb.checked = (value === undefined ? !cb.checked : value);
12954         }
12955     },
12956
12957     blur : function(){
12958         try{
12959             this.anchor.blur();
12960         }catch(e){}
12961     },
12962
12963     animExpand : function(callback){
12964         var ct = Roo.get(this.ctNode);
12965         ct.stopFx();
12966         if(!this.node.hasChildNodes()){
12967             this.updateExpandIcon();
12968             this.ctNode.style.display = "";
12969             Roo.callback(callback);
12970             return;
12971         }
12972         this.animating = true;
12973         this.updateExpandIcon();
12974
12975         ct.slideIn('t', {
12976            callback : function(){
12977                this.animating = false;
12978                Roo.callback(callback);
12979             },
12980             scope: this,
12981             duration: this.node.ownerTree.duration || .25
12982         });
12983     },
12984
12985     highlight : function(){
12986         var tree = this.node.getOwnerTree();
12987         Roo.fly(this.wrap).highlight(
12988             tree.hlColor || "C3DAF9",
12989             {endColor: tree.hlBaseColor}
12990         );
12991     },
12992
12993     collapse : function(){
12994         this.updateExpandIcon();
12995         this.ctNode.style.display = "none";
12996     },
12997
12998     animCollapse : function(callback){
12999         var ct = Roo.get(this.ctNode);
13000         ct.enableDisplayMode('block');
13001         ct.stopFx();
13002
13003         this.animating = true;
13004         this.updateExpandIcon();
13005
13006         ct.slideOut('t', {
13007             callback : function(){
13008                this.animating = false;
13009                Roo.callback(callback);
13010             },
13011             scope: this,
13012             duration: this.node.ownerTree.duration || .25
13013         });
13014     },
13015
13016     getContainer : function(){
13017         return this.ctNode;
13018     },
13019
13020     getEl : function(){
13021         return this.wrap;
13022     },
13023
13024     appendDDGhost : function(ghostNode){
13025         ghostNode.appendChild(this.elNode.cloneNode(true));
13026     },
13027
13028     getDDRepairXY : function(){
13029         return Roo.lib.Dom.getXY(this.iconNode);
13030     },
13031
13032     onRender : function(){
13033         this.render();
13034     },
13035
13036     render : function(bulkRender){
13037         var n = this.node, a = n.attributes;
13038         var targetNode = n.parentNode ?
13039               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13040
13041         if(!this.rendered){
13042             this.rendered = true;
13043
13044             this.renderElements(n, a, targetNode, bulkRender);
13045
13046             if(a.qtip){
13047                if(this.textNode.setAttributeNS){
13048                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13049                    if(a.qtipTitle){
13050                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13051                    }
13052                }else{
13053                    this.textNode.setAttribute("ext:qtip", a.qtip);
13054                    if(a.qtipTitle){
13055                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13056                    }
13057                }
13058             }else if(a.qtipCfg){
13059                 a.qtipCfg.target = Roo.id(this.textNode);
13060                 Roo.QuickTips.register(a.qtipCfg);
13061             }
13062             this.initEvents();
13063             if(!this.node.expanded){
13064                 this.updateExpandIcon();
13065             }
13066         }else{
13067             if(bulkRender === true) {
13068                 targetNode.appendChild(this.wrap);
13069             }
13070         }
13071     },
13072
13073     renderElements : function(n, a, targetNode, bulkRender)
13074     {
13075         // add some indent caching, this helps performance when rendering a large tree
13076         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13077         var t = n.getOwnerTree();
13078         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13079         if (typeof(n.attributes.html) != 'undefined') {
13080             txt = n.attributes.html;
13081         }
13082         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13083         var cb = typeof a.checked == 'boolean';
13084         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13085         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13086             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13087             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13088             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13089             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13090             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13091              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13092                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13093             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13094             "</li>"];
13095
13096         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13097             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13098                                 n.nextSibling.ui.getEl(), buf.join(""));
13099         }else{
13100             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13101         }
13102
13103         this.elNode = this.wrap.childNodes[0];
13104         this.ctNode = this.wrap.childNodes[1];
13105         var cs = this.elNode.childNodes;
13106         this.indentNode = cs[0];
13107         this.ecNode = cs[1];
13108         this.iconNode = cs[2];
13109         var index = 3;
13110         if(cb){
13111             this.checkbox = cs[3];
13112             index++;
13113         }
13114         this.anchor = cs[index];
13115         this.textNode = cs[index].firstChild;
13116     },
13117
13118     getAnchor : function(){
13119         return this.anchor;
13120     },
13121
13122     getTextEl : function(){
13123         return this.textNode;
13124     },
13125
13126     getIconEl : function(){
13127         return this.iconNode;
13128     },
13129
13130     isChecked : function(){
13131         return this.checkbox ? this.checkbox.checked : false;
13132     },
13133
13134     updateExpandIcon : function(){
13135         if(this.rendered){
13136             var n = this.node, c1, c2;
13137             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13138             var hasChild = n.hasChildNodes();
13139             if(hasChild){
13140                 if(n.expanded){
13141                     cls += "-minus";
13142                     c1 = "x-tree-node-collapsed";
13143                     c2 = "x-tree-node-expanded";
13144                 }else{
13145                     cls += "-plus";
13146                     c1 = "x-tree-node-expanded";
13147                     c2 = "x-tree-node-collapsed";
13148                 }
13149                 if(this.wasLeaf){
13150                     this.removeClass("x-tree-node-leaf");
13151                     this.wasLeaf = false;
13152                 }
13153                 if(this.c1 != c1 || this.c2 != c2){
13154                     Roo.fly(this.elNode).replaceClass(c1, c2);
13155                     this.c1 = c1; this.c2 = c2;
13156                 }
13157             }else{
13158                 // this changes non-leafs into leafs if they have no children.
13159                 // it's not very rational behaviour..
13160                 
13161                 if(!this.wasLeaf && this.node.leaf){
13162                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13163                     delete this.c1;
13164                     delete this.c2;
13165                     this.wasLeaf = true;
13166                 }
13167             }
13168             var ecc = "x-tree-ec-icon "+cls;
13169             if(this.ecc != ecc){
13170                 this.ecNode.className = ecc;
13171                 this.ecc = ecc;
13172             }
13173         }
13174     },
13175
13176     getChildIndent : function(){
13177         if(!this.childIndent){
13178             var buf = [];
13179             var p = this.node;
13180             while(p){
13181                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13182                     if(!p.isLast()) {
13183                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13184                     } else {
13185                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13186                     }
13187                 }
13188                 p = p.parentNode;
13189             }
13190             this.childIndent = buf.join("");
13191         }
13192         return this.childIndent;
13193     },
13194
13195     renderIndent : function(){
13196         if(this.rendered){
13197             var indent = "";
13198             var p = this.node.parentNode;
13199             if(p){
13200                 indent = p.ui.getChildIndent();
13201             }
13202             if(this.indentMarkup != indent){ // don't rerender if not required
13203                 this.indentNode.innerHTML = indent;
13204                 this.indentMarkup = indent;
13205             }
13206             this.updateExpandIcon();
13207         }
13208     }
13209 };
13210
13211 Roo.tree.RootTreeNodeUI = function(){
13212     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13213 };
13214 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13215     render : function(){
13216         if(!this.rendered){
13217             var targetNode = this.node.ownerTree.innerCt.dom;
13218             this.node.expanded = true;
13219             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13220             this.wrap = this.ctNode = targetNode.firstChild;
13221         }
13222     },
13223     collapse : function(){
13224     },
13225     expand : function(){
13226     }
13227 });/*
13228  * Based on:
13229  * Ext JS Library 1.1.1
13230  * Copyright(c) 2006-2007, Ext JS, LLC.
13231  *
13232  * Originally Released Under LGPL - original licence link has changed is not relivant.
13233  *
13234  * Fork - LGPL
13235  * <script type="text/javascript">
13236  */
13237 /**
13238  * @class Roo.tree.TreeLoader
13239  * @extends Roo.util.Observable
13240  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13241  * nodes from a specified URL. The response must be a javascript Array definition
13242  * who's elements are node definition objects. eg:
13243  * <pre><code>
13244 {  success : true,
13245    data :      [
13246    
13247     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13248     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13249     ]
13250 }
13251
13252
13253 </code></pre>
13254  * <br><br>
13255  * The old style respose with just an array is still supported, but not recommended.
13256  * <br><br>
13257  *
13258  * A server request is sent, and child nodes are loaded only when a node is expanded.
13259  * The loading node's id is passed to the server under the parameter name "node" to
13260  * enable the server to produce the correct child nodes.
13261  * <br><br>
13262  * To pass extra parameters, an event handler may be attached to the "beforeload"
13263  * event, and the parameters specified in the TreeLoader's baseParams property:
13264  * <pre><code>
13265     myTreeLoader.on("beforeload", function(treeLoader, node) {
13266         this.baseParams.category = node.attributes.category;
13267     }, this);
13268     
13269 </code></pre>
13270  *
13271  * This would pass an HTTP parameter called "category" to the server containing
13272  * the value of the Node's "category" attribute.
13273  * @constructor
13274  * Creates a new Treeloader.
13275  * @param {Object} config A config object containing config properties.
13276  */
13277 Roo.tree.TreeLoader = function(config){
13278     this.baseParams = {};
13279     this.requestMethod = "POST";
13280     Roo.apply(this, config);
13281
13282     this.addEvents({
13283     
13284         /**
13285          * @event beforeload
13286          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13287          * @param {Object} This TreeLoader object.
13288          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13289          * @param {Object} callback The callback function specified in the {@link #load} call.
13290          */
13291         beforeload : true,
13292         /**
13293          * @event load
13294          * Fires when the node has been successfuly loaded.
13295          * @param {Object} This TreeLoader object.
13296          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13297          * @param {Object} response The response object containing the data from the server.
13298          */
13299         load : true,
13300         /**
13301          * @event loadexception
13302          * Fires if the network request failed.
13303          * @param {Object} This TreeLoader object.
13304          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13305          * @param {Object} response The response object containing the data from the server.
13306          */
13307         loadexception : true,
13308         /**
13309          * @event create
13310          * Fires before a node is created, enabling you to return custom Node types 
13311          * @param {Object} This TreeLoader object.
13312          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13313          */
13314         create : true
13315     });
13316
13317     Roo.tree.TreeLoader.superclass.constructor.call(this);
13318 };
13319
13320 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13321     /**
13322     * @cfg {String} dataUrl The URL from which to request a Json string which
13323     * specifies an array of node definition object representing the child nodes
13324     * to be loaded.
13325     */
13326     /**
13327     * @cfg {String} requestMethod either GET or POST
13328     * defaults to POST (due to BC)
13329     * to be loaded.
13330     */
13331     /**
13332     * @cfg {Object} baseParams (optional) An object containing properties which
13333     * specify HTTP parameters to be passed to each request for child nodes.
13334     */
13335     /**
13336     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13337     * created by this loader. If the attributes sent by the server have an attribute in this object,
13338     * they take priority.
13339     */
13340     /**
13341     * @cfg {Object} uiProviders (optional) An object containing properties which
13342     * 
13343     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13344     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13345     * <i>uiProvider</i> attribute of a returned child node is a string rather
13346     * than a reference to a TreeNodeUI implementation, this that string value
13347     * is used as a property name in the uiProviders object. You can define the provider named
13348     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13349     */
13350     uiProviders : {},
13351
13352     /**
13353     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13354     * child nodes before loading.
13355     */
13356     clearOnLoad : true,
13357
13358     /**
13359     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13360     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13361     * Grid query { data : [ .....] }
13362     */
13363     
13364     root : false,
13365      /**
13366     * @cfg {String} queryParam (optional) 
13367     * Name of the query as it will be passed on the querystring (defaults to 'node')
13368     * eg. the request will be ?node=[id]
13369     */
13370     
13371     
13372     queryParam: false,
13373     
13374     /**
13375      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13376      * This is called automatically when a node is expanded, but may be used to reload
13377      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13378      * @param {Roo.tree.TreeNode} node
13379      * @param {Function} callback
13380      */
13381     load : function(node, callback){
13382         if(this.clearOnLoad){
13383             while(node.firstChild){
13384                 node.removeChild(node.firstChild);
13385             }
13386         }
13387         if(node.attributes.children){ // preloaded json children
13388             var cs = node.attributes.children;
13389             for(var i = 0, len = cs.length; i < len; i++){
13390                 node.appendChild(this.createNode(cs[i]));
13391             }
13392             if(typeof callback == "function"){
13393                 callback();
13394             }
13395         }else if(this.dataUrl){
13396             this.requestData(node, callback);
13397         }
13398     },
13399
13400     getParams: function(node){
13401         var buf = [], bp = this.baseParams;
13402         for(var key in bp){
13403             if(typeof bp[key] != "function"){
13404                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13405             }
13406         }
13407         var n = this.queryParam === false ? 'node' : this.queryParam;
13408         buf.push(n + "=", encodeURIComponent(node.id));
13409         return buf.join("");
13410     },
13411
13412     requestData : function(node, callback){
13413         if(this.fireEvent("beforeload", this, node, callback) !== false){
13414             this.transId = Roo.Ajax.request({
13415                 method:this.requestMethod,
13416                 url: this.dataUrl||this.url,
13417                 success: this.handleResponse,
13418                 failure: this.handleFailure,
13419                 scope: this,
13420                 argument: {callback: callback, node: node},
13421                 params: this.getParams(node)
13422             });
13423         }else{
13424             // if the load is cancelled, make sure we notify
13425             // the node that we are done
13426             if(typeof callback == "function"){
13427                 callback();
13428             }
13429         }
13430     },
13431
13432     isLoading : function(){
13433         return this.transId ? true : false;
13434     },
13435
13436     abort : function(){
13437         if(this.isLoading()){
13438             Roo.Ajax.abort(this.transId);
13439         }
13440     },
13441
13442     // private
13443     createNode : function(attr)
13444     {
13445         // apply baseAttrs, nice idea Corey!
13446         if(this.baseAttrs){
13447             Roo.applyIf(attr, this.baseAttrs);
13448         }
13449         if(this.applyLoader !== false){
13450             attr.loader = this;
13451         }
13452         // uiProvider = depreciated..
13453         
13454         if(typeof(attr.uiProvider) == 'string'){
13455            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13456                 /**  eval:var:attr */ eval(attr.uiProvider);
13457         }
13458         if(typeof(this.uiProviders['default']) != 'undefined') {
13459             attr.uiProvider = this.uiProviders['default'];
13460         }
13461         
13462         this.fireEvent('create', this, attr);
13463         
13464         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13465         return(attr.leaf ?
13466                         new Roo.tree.TreeNode(attr) :
13467                         new Roo.tree.AsyncTreeNode(attr));
13468     },
13469
13470     processResponse : function(response, node, callback)
13471     {
13472         var json = response.responseText;
13473         try {
13474             
13475             var o = Roo.decode(json);
13476             
13477             if (this.root === false && typeof(o.success) != undefined) {
13478                 this.root = 'data'; // the default behaviour for list like data..
13479                 }
13480                 
13481             if (this.root !== false &&  !o.success) {
13482                 // it's a failure condition.
13483                 var a = response.argument;
13484                 this.fireEvent("loadexception", this, a.node, response);
13485                 Roo.log("Load failed - should have a handler really");
13486                 return;
13487             }
13488             
13489             
13490             
13491             if (this.root !== false) {
13492                  o = o[this.root];
13493             }
13494             
13495             for(var i = 0, len = o.length; i < len; i++){
13496                 var n = this.createNode(o[i]);
13497                 if(n){
13498                     node.appendChild(n);
13499                 }
13500             }
13501             if(typeof callback == "function"){
13502                 callback(this, node);
13503             }
13504         }catch(e){
13505             this.handleFailure(response);
13506         }
13507     },
13508
13509     handleResponse : function(response){
13510         this.transId = false;
13511         var a = response.argument;
13512         this.processResponse(response, a.node, a.callback);
13513         this.fireEvent("load", this, a.node, response);
13514     },
13515
13516     handleFailure : function(response)
13517     {
13518         // should handle failure better..
13519         this.transId = false;
13520         var a = response.argument;
13521         this.fireEvent("loadexception", this, a.node, response);
13522         if(typeof a.callback == "function"){
13523             a.callback(this, a.node);
13524         }
13525     }
13526 });/*
13527  * Based on:
13528  * Ext JS Library 1.1.1
13529  * Copyright(c) 2006-2007, Ext JS, LLC.
13530  *
13531  * Originally Released Under LGPL - original licence link has changed is not relivant.
13532  *
13533  * Fork - LGPL
13534  * <script type="text/javascript">
13535  */
13536
13537 /**
13538 * @class Roo.tree.TreeFilter
13539 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13540 * @param {TreePanel} tree
13541 * @param {Object} config (optional)
13542  */
13543 Roo.tree.TreeFilter = function(tree, config){
13544     this.tree = tree;
13545     this.filtered = {};
13546     Roo.apply(this, config);
13547 };
13548
13549 Roo.tree.TreeFilter.prototype = {
13550     clearBlank:false,
13551     reverse:false,
13552     autoClear:false,
13553     remove:false,
13554
13555      /**
13556      * Filter the data by a specific attribute.
13557      * @param {String/RegExp} value Either string that the attribute value
13558      * should start with or a RegExp to test against the attribute
13559      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13560      * @param {TreeNode} startNode (optional) The node to start the filter at.
13561      */
13562     filter : function(value, attr, startNode){
13563         attr = attr || "text";
13564         var f;
13565         if(typeof value == "string"){
13566             var vlen = value.length;
13567             // auto clear empty filter
13568             if(vlen == 0 && this.clearBlank){
13569                 this.clear();
13570                 return;
13571             }
13572             value = value.toLowerCase();
13573             f = function(n){
13574                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13575             };
13576         }else if(value.exec){ // regex?
13577             f = function(n){
13578                 return value.test(n.attributes[attr]);
13579             };
13580         }else{
13581             throw 'Illegal filter type, must be string or regex';
13582         }
13583         this.filterBy(f, null, startNode);
13584         },
13585
13586     /**
13587      * Filter by a function. The passed function will be called with each
13588      * node in the tree (or from the startNode). If the function returns true, the node is kept
13589      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13590      * @param {Function} fn The filter function
13591      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13592      */
13593     filterBy : function(fn, scope, startNode){
13594         startNode = startNode || this.tree.root;
13595         if(this.autoClear){
13596             this.clear();
13597         }
13598         var af = this.filtered, rv = this.reverse;
13599         var f = function(n){
13600             if(n == startNode){
13601                 return true;
13602             }
13603             if(af[n.id]){
13604                 return false;
13605             }
13606             var m = fn.call(scope || n, n);
13607             if(!m || rv){
13608                 af[n.id] = n;
13609                 n.ui.hide();
13610                 return false;
13611             }
13612             return true;
13613         };
13614         startNode.cascade(f);
13615         if(this.remove){
13616            for(var id in af){
13617                if(typeof id != "function"){
13618                    var n = af[id];
13619                    if(n && n.parentNode){
13620                        n.parentNode.removeChild(n);
13621                    }
13622                }
13623            }
13624         }
13625     },
13626
13627     /**
13628      * Clears the current filter. Note: with the "remove" option
13629      * set a filter cannot be cleared.
13630      */
13631     clear : function(){
13632         var t = this.tree;
13633         var af = this.filtered;
13634         for(var id in af){
13635             if(typeof id != "function"){
13636                 var n = af[id];
13637                 if(n){
13638                     n.ui.show();
13639                 }
13640             }
13641         }
13642         this.filtered = {};
13643     }
13644 };
13645 /*
13646  * Based on:
13647  * Ext JS Library 1.1.1
13648  * Copyright(c) 2006-2007, Ext JS, LLC.
13649  *
13650  * Originally Released Under LGPL - original licence link has changed is not relivant.
13651  *
13652  * Fork - LGPL
13653  * <script type="text/javascript">
13654  */
13655  
13656
13657 /**
13658  * @class Roo.tree.TreeSorter
13659  * Provides sorting of nodes in a TreePanel
13660  * 
13661  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13662  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13663  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13664  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13665  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13666  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13667  * @constructor
13668  * @param {TreePanel} tree
13669  * @param {Object} config
13670  */
13671 Roo.tree.TreeSorter = function(tree, config){
13672     Roo.apply(this, config);
13673     tree.on("beforechildrenrendered", this.doSort, this);
13674     tree.on("append", this.updateSort, this);
13675     tree.on("insert", this.updateSort, this);
13676     
13677     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13678     var p = this.property || "text";
13679     var sortType = this.sortType;
13680     var fs = this.folderSort;
13681     var cs = this.caseSensitive === true;
13682     var leafAttr = this.leafAttr || 'leaf';
13683
13684     this.sortFn = function(n1, n2){
13685         if(fs){
13686             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13687                 return 1;
13688             }
13689             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13690                 return -1;
13691             }
13692         }
13693         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13694         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13695         if(v1 < v2){
13696                         return dsc ? +1 : -1;
13697                 }else if(v1 > v2){
13698                         return dsc ? -1 : +1;
13699         }else{
13700                 return 0;
13701         }
13702     };
13703 };
13704
13705 Roo.tree.TreeSorter.prototype = {
13706     doSort : function(node){
13707         node.sort(this.sortFn);
13708     },
13709     
13710     compareNodes : function(n1, n2){
13711         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13712     },
13713     
13714     updateSort : function(tree, node){
13715         if(node.childrenRendered){
13716             this.doSort.defer(1, this, [node]);
13717         }
13718     }
13719 };/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729
13730 if(Roo.dd.DropZone){
13731     
13732 Roo.tree.TreeDropZone = function(tree, config){
13733     this.allowParentInsert = false;
13734     this.allowContainerDrop = false;
13735     this.appendOnly = false;
13736     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13737     this.tree = tree;
13738     this.lastInsertClass = "x-tree-no-status";
13739     this.dragOverData = {};
13740 };
13741
13742 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13743     ddGroup : "TreeDD",
13744     scroll:  true,
13745     
13746     expandDelay : 1000,
13747     
13748     expandNode : function(node){
13749         if(node.hasChildNodes() && !node.isExpanded()){
13750             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13751         }
13752     },
13753     
13754     queueExpand : function(node){
13755         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13756     },
13757     
13758     cancelExpand : function(){
13759         if(this.expandProcId){
13760             clearTimeout(this.expandProcId);
13761             this.expandProcId = false;
13762         }
13763     },
13764     
13765     isValidDropPoint : function(n, pt, dd, e, data){
13766         if(!n || !data){ return false; }
13767         var targetNode = n.node;
13768         var dropNode = data.node;
13769         // default drop rules
13770         if(!(targetNode && targetNode.isTarget && pt)){
13771             return false;
13772         }
13773         if(pt == "append" && targetNode.allowChildren === false){
13774             return false;
13775         }
13776         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13777             return false;
13778         }
13779         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13780             return false;
13781         }
13782         // reuse the object
13783         var overEvent = this.dragOverData;
13784         overEvent.tree = this.tree;
13785         overEvent.target = targetNode;
13786         overEvent.data = data;
13787         overEvent.point = pt;
13788         overEvent.source = dd;
13789         overEvent.rawEvent = e;
13790         overEvent.dropNode = dropNode;
13791         overEvent.cancel = false;  
13792         var result = this.tree.fireEvent("nodedragover", overEvent);
13793         return overEvent.cancel === false && result !== false;
13794     },
13795     
13796     getDropPoint : function(e, n, dd)
13797     {
13798         var tn = n.node;
13799         if(tn.isRoot){
13800             return tn.allowChildren !== false ? "append" : false; // always append for root
13801         }
13802         var dragEl = n.ddel;
13803         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13804         var y = Roo.lib.Event.getPageY(e);
13805         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13806         
13807         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13808         var noAppend = tn.allowChildren === false;
13809         if(this.appendOnly || tn.parentNode.allowChildren === false){
13810             return noAppend ? false : "append";
13811         }
13812         var noBelow = false;
13813         if(!this.allowParentInsert){
13814             noBelow = tn.hasChildNodes() && tn.isExpanded();
13815         }
13816         var q = (b - t) / (noAppend ? 2 : 3);
13817         if(y >= t && y < (t + q)){
13818             return "above";
13819         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13820             return "below";
13821         }else{
13822             return "append";
13823         }
13824     },
13825     
13826     onNodeEnter : function(n, dd, e, data)
13827     {
13828         this.cancelExpand();
13829     },
13830     
13831     onNodeOver : function(n, dd, e, data)
13832     {
13833        
13834         var pt = this.getDropPoint(e, n, dd);
13835         var node = n.node;
13836         
13837         // auto node expand check
13838         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13839             this.queueExpand(node);
13840         }else if(pt != "append"){
13841             this.cancelExpand();
13842         }
13843         
13844         // set the insert point style on the target node
13845         var returnCls = this.dropNotAllowed;
13846         if(this.isValidDropPoint(n, pt, dd, e, data)){
13847            if(pt){
13848                var el = n.ddel;
13849                var cls;
13850                if(pt == "above"){
13851                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13852                    cls = "x-tree-drag-insert-above";
13853                }else if(pt == "below"){
13854                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13855                    cls = "x-tree-drag-insert-below";
13856                }else{
13857                    returnCls = "x-tree-drop-ok-append";
13858                    cls = "x-tree-drag-append";
13859                }
13860                if(this.lastInsertClass != cls){
13861                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13862                    this.lastInsertClass = cls;
13863                }
13864            }
13865        }
13866        return returnCls;
13867     },
13868     
13869     onNodeOut : function(n, dd, e, data){
13870         
13871         this.cancelExpand();
13872         this.removeDropIndicators(n);
13873     },
13874     
13875     onNodeDrop : function(n, dd, e, data){
13876         var point = this.getDropPoint(e, n, dd);
13877         var targetNode = n.node;
13878         targetNode.ui.startDrop();
13879         if(!this.isValidDropPoint(n, point, dd, e, data)){
13880             targetNode.ui.endDrop();
13881             return false;
13882         }
13883         // first try to find the drop node
13884         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13885         var dropEvent = {
13886             tree : this.tree,
13887             target: targetNode,
13888             data: data,
13889             point: point,
13890             source: dd,
13891             rawEvent: e,
13892             dropNode: dropNode,
13893             cancel: !dropNode   
13894         };
13895         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13896         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13897             targetNode.ui.endDrop();
13898             return false;
13899         }
13900         // allow target changing
13901         targetNode = dropEvent.target;
13902         if(point == "append" && !targetNode.isExpanded()){
13903             targetNode.expand(false, null, function(){
13904                 this.completeDrop(dropEvent);
13905             }.createDelegate(this));
13906         }else{
13907             this.completeDrop(dropEvent);
13908         }
13909         return true;
13910     },
13911     
13912     completeDrop : function(de){
13913         var ns = de.dropNode, p = de.point, t = de.target;
13914         if(!(ns instanceof Array)){
13915             ns = [ns];
13916         }
13917         var n;
13918         for(var i = 0, len = ns.length; i < len; i++){
13919             n = ns[i];
13920             if(p == "above"){
13921                 t.parentNode.insertBefore(n, t);
13922             }else if(p == "below"){
13923                 t.parentNode.insertBefore(n, t.nextSibling);
13924             }else{
13925                 t.appendChild(n);
13926             }
13927         }
13928         n.ui.focus();
13929         if(this.tree.hlDrop){
13930             n.ui.highlight();
13931         }
13932         t.ui.endDrop();
13933         this.tree.fireEvent("nodedrop", de);
13934     },
13935     
13936     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13937         if(this.tree.hlDrop){
13938             dropNode.ui.focus();
13939             dropNode.ui.highlight();
13940         }
13941         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13942     },
13943     
13944     getTree : function(){
13945         return this.tree;
13946     },
13947     
13948     removeDropIndicators : function(n){
13949         if(n && n.ddel){
13950             var el = n.ddel;
13951             Roo.fly(el).removeClass([
13952                     "x-tree-drag-insert-above",
13953                     "x-tree-drag-insert-below",
13954                     "x-tree-drag-append"]);
13955             this.lastInsertClass = "_noclass";
13956         }
13957     },
13958     
13959     beforeDragDrop : function(target, e, id){
13960         this.cancelExpand();
13961         return true;
13962     },
13963     
13964     afterRepair : function(data){
13965         if(data && Roo.enableFx){
13966             data.node.ui.highlight();
13967         }
13968         this.hideProxy();
13969     } 
13970     
13971 });
13972
13973 }
13974 /*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984  
13985
13986 if(Roo.dd.DragZone){
13987 Roo.tree.TreeDragZone = function(tree, config){
13988     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13989     this.tree = tree;
13990 };
13991
13992 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13993     ddGroup : "TreeDD",
13994    
13995     onBeforeDrag : function(data, e){
13996         var n = data.node;
13997         return n && n.draggable && !n.disabled;
13998     },
13999      
14000     
14001     onInitDrag : function(e){
14002         var data = this.dragData;
14003         this.tree.getSelectionModel().select(data.node);
14004         this.proxy.update("");
14005         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14006         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14007     },
14008     
14009     getRepairXY : function(e, data){
14010         return data.node.ui.getDDRepairXY();
14011     },
14012     
14013     onEndDrag : function(data, e){
14014         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14015         
14016         
14017     },
14018     
14019     onValidDrop : function(dd, e, id){
14020         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14021         this.hideProxy();
14022     },
14023     
14024     beforeInvalidDrop : function(e, id){
14025         // this scrolls the original position back into view
14026         var sm = this.tree.getSelectionModel();
14027         sm.clearSelections();
14028         sm.select(this.dragData.node);
14029     }
14030 });
14031 }/*
14032  * Based on:
14033  * Ext JS Library 1.1.1
14034  * Copyright(c) 2006-2007, Ext JS, LLC.
14035  *
14036  * Originally Released Under LGPL - original licence link has changed is not relivant.
14037  *
14038  * Fork - LGPL
14039  * <script type="text/javascript">
14040  */
14041 /**
14042  * @class Roo.tree.TreeEditor
14043  * @extends Roo.Editor
14044  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14045  * as the editor field.
14046  * @constructor
14047  * @param {Object} config (used to be the tree panel.)
14048  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14049  * 
14050  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14051  * @cfg {Roo.form.TextField} field [required] The field configuration
14052  *
14053  * 
14054  */
14055 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14056     var tree = config;
14057     var field;
14058     if (oldconfig) { // old style..
14059         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14060     } else {
14061         // new style..
14062         tree = config.tree;
14063         config.field = config.field  || {};
14064         config.field.xtype = 'TextField';
14065         field = Roo.factory(config.field, Roo.form);
14066     }
14067     config = config || {};
14068     
14069     
14070     this.addEvents({
14071         /**
14072          * @event beforenodeedit
14073          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14074          * false from the handler of this event.
14075          * @param {Editor} this
14076          * @param {Roo.tree.Node} node 
14077          */
14078         "beforenodeedit" : true
14079     });
14080     
14081     //Roo.log(config);
14082     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14083
14084     this.tree = tree;
14085
14086     tree.on('beforeclick', this.beforeNodeClick, this);
14087     tree.getTreeEl().on('mousedown', this.hide, this);
14088     this.on('complete', this.updateNode, this);
14089     this.on('beforestartedit', this.fitToTree, this);
14090     this.on('startedit', this.bindScroll, this, {delay:10});
14091     this.on('specialkey', this.onSpecialKey, this);
14092 };
14093
14094 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14095     /**
14096      * @cfg {String} alignment
14097      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14098      */
14099     alignment: "l-l",
14100     // inherit
14101     autoSize: false,
14102     /**
14103      * @cfg {Boolean} hideEl
14104      * True to hide the bound element while the editor is displayed (defaults to false)
14105      */
14106     hideEl : false,
14107     /**
14108      * @cfg {String} cls
14109      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14110      */
14111     cls: "x-small-editor x-tree-editor",
14112     /**
14113      * @cfg {Boolean} shim
14114      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14115      */
14116     shim:false,
14117     // inherit
14118     shadow:"frame",
14119     /**
14120      * @cfg {Number} maxWidth
14121      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14122      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14123      * scroll and client offsets into account prior to each edit.
14124      */
14125     maxWidth: 250,
14126
14127     editDelay : 350,
14128
14129     // private
14130     fitToTree : function(ed, el){
14131         var td = this.tree.getTreeEl().dom, nd = el.dom;
14132         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14133             td.scrollLeft = nd.offsetLeft;
14134         }
14135         var w = Math.min(
14136                 this.maxWidth,
14137                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14138         this.setSize(w, '');
14139         
14140         return this.fireEvent('beforenodeedit', this, this.editNode);
14141         
14142     },
14143
14144     // private
14145     triggerEdit : function(node){
14146         this.completeEdit();
14147         this.editNode = node;
14148         this.startEdit(node.ui.textNode, node.text);
14149     },
14150
14151     // private
14152     bindScroll : function(){
14153         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14154     },
14155
14156     // private
14157     beforeNodeClick : function(node, e){
14158         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14159         this.lastClick = new Date();
14160         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14161             e.stopEvent();
14162             this.triggerEdit(node);
14163             return false;
14164         }
14165         return true;
14166     },
14167
14168     // private
14169     updateNode : function(ed, value){
14170         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14171         this.editNode.setText(value);
14172     },
14173
14174     // private
14175     onHide : function(){
14176         Roo.tree.TreeEditor.superclass.onHide.call(this);
14177         if(this.editNode){
14178             this.editNode.ui.focus();
14179         }
14180     },
14181
14182     // private
14183     onSpecialKey : function(field, e){
14184         var k = e.getKey();
14185         if(k == e.ESC){
14186             e.stopEvent();
14187             this.cancelEdit();
14188         }else if(k == e.ENTER && !e.hasModifier()){
14189             e.stopEvent();
14190             this.completeEdit();
14191         }
14192     }
14193 });//<Script type="text/javascript">
14194 /*
14195  * Based on:
14196  * Ext JS Library 1.1.1
14197  * Copyright(c) 2006-2007, Ext JS, LLC.
14198  *
14199  * Originally Released Under LGPL - original licence link has changed is not relivant.
14200  *
14201  * Fork - LGPL
14202  * <script type="text/javascript">
14203  */
14204  
14205 /**
14206  * Not documented??? - probably should be...
14207  */
14208
14209 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14210     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14211     
14212     renderElements : function(n, a, targetNode, bulkRender){
14213         //consel.log("renderElements?");
14214         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14215
14216         var t = n.getOwnerTree();
14217         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14218         
14219         var cols = t.columns;
14220         var bw = t.borderWidth;
14221         var c = cols[0];
14222         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14223          var cb = typeof a.checked == "boolean";
14224         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14225         var colcls = 'x-t-' + tid + '-c0';
14226         var buf = [
14227             '<li class="x-tree-node">',
14228             
14229                 
14230                 '<div class="x-tree-node-el ', a.cls,'">',
14231                     // extran...
14232                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14233                 
14234                 
14235                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14236                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14237                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14238                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14239                            (a.iconCls ? ' '+a.iconCls : ''),
14240                            '" unselectable="on" />',
14241                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14242                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14243                              
14244                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14245                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14246                             '<span unselectable="on" qtip="' + tx + '">',
14247                              tx,
14248                              '</span></a>' ,
14249                     '</div>',
14250                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14251                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14252                  ];
14253         for(var i = 1, len = cols.length; i < len; i++){
14254             c = cols[i];
14255             colcls = 'x-t-' + tid + '-c' +i;
14256             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14257             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14258                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14259                       "</div>");
14260          }
14261          
14262          buf.push(
14263             '</a>',
14264             '<div class="x-clear"></div></div>',
14265             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14266             "</li>");
14267         
14268         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14269             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14270                                 n.nextSibling.ui.getEl(), buf.join(""));
14271         }else{
14272             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14273         }
14274         var el = this.wrap.firstChild;
14275         this.elRow = el;
14276         this.elNode = el.firstChild;
14277         this.ranchor = el.childNodes[1];
14278         this.ctNode = this.wrap.childNodes[1];
14279         var cs = el.firstChild.childNodes;
14280         this.indentNode = cs[0];
14281         this.ecNode = cs[1];
14282         this.iconNode = cs[2];
14283         var index = 3;
14284         if(cb){
14285             this.checkbox = cs[3];
14286             index++;
14287         }
14288         this.anchor = cs[index];
14289         
14290         this.textNode = cs[index].firstChild;
14291         
14292         //el.on("click", this.onClick, this);
14293         //el.on("dblclick", this.onDblClick, this);
14294         
14295         
14296        // console.log(this);
14297     },
14298     initEvents : function(){
14299         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14300         
14301             
14302         var a = this.ranchor;
14303
14304         var el = Roo.get(a);
14305
14306         if(Roo.isOpera){ // opera render bug ignores the CSS
14307             el.setStyle("text-decoration", "none");
14308         }
14309
14310         el.on("click", this.onClick, this);
14311         el.on("dblclick", this.onDblClick, this);
14312         el.on("contextmenu", this.onContextMenu, this);
14313         
14314     },
14315     
14316     /*onSelectedChange : function(state){
14317         if(state){
14318             this.focus();
14319             this.addClass("x-tree-selected");
14320         }else{
14321             //this.blur();
14322             this.removeClass("x-tree-selected");
14323         }
14324     },*/
14325     addClass : function(cls){
14326         if(this.elRow){
14327             Roo.fly(this.elRow).addClass(cls);
14328         }
14329         
14330     },
14331     
14332     
14333     removeClass : function(cls){
14334         if(this.elRow){
14335             Roo.fly(this.elRow).removeClass(cls);
14336         }
14337     }
14338
14339     
14340     
14341 });//<Script type="text/javascript">
14342
14343 /*
14344  * Based on:
14345  * Ext JS Library 1.1.1
14346  * Copyright(c) 2006-2007, Ext JS, LLC.
14347  *
14348  * Originally Released Under LGPL - original licence link has changed is not relivant.
14349  *
14350  * Fork - LGPL
14351  * <script type="text/javascript">
14352  */
14353  
14354
14355 /**
14356  * @class Roo.tree.ColumnTree
14357  * @extends Roo.tree.TreePanel
14358  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14359  * @cfg {int} borderWidth  compined right/left border allowance
14360  * @constructor
14361  * @param {String/HTMLElement/Element} el The container element
14362  * @param {Object} config
14363  */
14364 Roo.tree.ColumnTree =  function(el, config)
14365 {
14366    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14367    this.addEvents({
14368         /**
14369         * @event resize
14370         * Fire this event on a container when it resizes
14371         * @param {int} w Width
14372         * @param {int} h Height
14373         */
14374        "resize" : true
14375     });
14376     this.on('resize', this.onResize, this);
14377 };
14378
14379 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14380     //lines:false,
14381     
14382     
14383     borderWidth: Roo.isBorderBox ? 0 : 2, 
14384     headEls : false,
14385     
14386     render : function(){
14387         // add the header.....
14388        
14389         Roo.tree.ColumnTree.superclass.render.apply(this);
14390         
14391         this.el.addClass('x-column-tree');
14392         
14393         this.headers = this.el.createChild(
14394             {cls:'x-tree-headers'},this.innerCt.dom);
14395    
14396         var cols = this.columns, c;
14397         var totalWidth = 0;
14398         this.headEls = [];
14399         var  len = cols.length;
14400         for(var i = 0; i < len; i++){
14401              c = cols[i];
14402              totalWidth += c.width;
14403             this.headEls.push(this.headers.createChild({
14404                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14405                  cn: {
14406                      cls:'x-tree-hd-text',
14407                      html: c.header
14408                  },
14409                  style:'width:'+(c.width-this.borderWidth)+'px;'
14410              }));
14411         }
14412         this.headers.createChild({cls:'x-clear'});
14413         // prevent floats from wrapping when clipped
14414         this.headers.setWidth(totalWidth);
14415         //this.innerCt.setWidth(totalWidth);
14416         this.innerCt.setStyle({ overflow: 'auto' });
14417         this.onResize(this.width, this.height);
14418              
14419         
14420     },
14421     onResize : function(w,h)
14422     {
14423         this.height = h;
14424         this.width = w;
14425         // resize cols..
14426         this.innerCt.setWidth(this.width);
14427         this.innerCt.setHeight(this.height-20);
14428         
14429         // headers...
14430         var cols = this.columns, c;
14431         var totalWidth = 0;
14432         var expEl = false;
14433         var len = cols.length;
14434         for(var i = 0; i < len; i++){
14435             c = cols[i];
14436             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14437                 // it's the expander..
14438                 expEl  = this.headEls[i];
14439                 continue;
14440             }
14441             totalWidth += c.width;
14442             
14443         }
14444         if (expEl) {
14445             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14446         }
14447         this.headers.setWidth(w-20);
14448
14449         
14450         
14451         
14452     }
14453 });
14454 /*
14455  * Based on:
14456  * Ext JS Library 1.1.1
14457  * Copyright(c) 2006-2007, Ext JS, LLC.
14458  *
14459  * Originally Released Under LGPL - original licence link has changed is not relivant.
14460  *
14461  * Fork - LGPL
14462  * <script type="text/javascript">
14463  */
14464  
14465 /**
14466  * @class Roo.menu.Menu
14467  * @extends Roo.util.Observable
14468  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14469  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14470  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14471  * @constructor
14472  * Creates a new Menu
14473  * @param {Object} config Configuration options
14474  */
14475 Roo.menu.Menu = function(config){
14476     
14477     Roo.menu.Menu.superclass.constructor.call(this, config);
14478     
14479     this.id = this.id || Roo.id();
14480     this.addEvents({
14481         /**
14482          * @event beforeshow
14483          * Fires before this menu is displayed
14484          * @param {Roo.menu.Menu} this
14485          */
14486         beforeshow : true,
14487         /**
14488          * @event beforehide
14489          * Fires before this menu is hidden
14490          * @param {Roo.menu.Menu} this
14491          */
14492         beforehide : true,
14493         /**
14494          * @event show
14495          * Fires after this menu is displayed
14496          * @param {Roo.menu.Menu} this
14497          */
14498         show : true,
14499         /**
14500          * @event hide
14501          * Fires after this menu is hidden
14502          * @param {Roo.menu.Menu} this
14503          */
14504         hide : true,
14505         /**
14506          * @event click
14507          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14508          * @param {Roo.menu.Menu} this
14509          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14510          * @param {Roo.EventObject} e
14511          */
14512         click : true,
14513         /**
14514          * @event mouseover
14515          * Fires when the mouse is hovering over this menu
14516          * @param {Roo.menu.Menu} this
14517          * @param {Roo.EventObject} e
14518          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14519          */
14520         mouseover : true,
14521         /**
14522          * @event mouseout
14523          * Fires when the mouse exits this menu
14524          * @param {Roo.menu.Menu} this
14525          * @param {Roo.EventObject} e
14526          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14527          */
14528         mouseout : true,
14529         /**
14530          * @event itemclick
14531          * Fires when a menu item contained in this menu is clicked
14532          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14533          * @param {Roo.EventObject} e
14534          */
14535         itemclick: true
14536     });
14537     if (this.registerMenu) {
14538         Roo.menu.MenuMgr.register(this);
14539     }
14540     
14541     var mis = this.items;
14542     this.items = new Roo.util.MixedCollection();
14543     if(mis){
14544         this.add.apply(this, mis);
14545     }
14546 };
14547
14548 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14549     /**
14550      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14551      */
14552     minWidth : 120,
14553     /**
14554      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14555      * for bottom-right shadow (defaults to "sides")
14556      */
14557     shadow : "sides",
14558     /**
14559      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14560      * this menu (defaults to "tl-tr?")
14561      */
14562     subMenuAlign : "tl-tr?",
14563     /**
14564      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14565      * relative to its element of origin (defaults to "tl-bl?")
14566      */
14567     defaultAlign : "tl-bl?",
14568     /**
14569      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14570      */
14571     allowOtherMenus : false,
14572     /**
14573      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14574      */
14575     registerMenu : true,
14576
14577     hidden:true,
14578
14579     // private
14580     render : function(){
14581         if(this.el){
14582             return;
14583         }
14584         var el = this.el = new Roo.Layer({
14585             cls: "x-menu",
14586             shadow:this.shadow,
14587             constrain: false,
14588             parentEl: this.parentEl || document.body,
14589             zindex:15000
14590         });
14591
14592         this.keyNav = new Roo.menu.MenuNav(this);
14593
14594         if(this.plain){
14595             el.addClass("x-menu-plain");
14596         }
14597         if(this.cls){
14598             el.addClass(this.cls);
14599         }
14600         // generic focus element
14601         this.focusEl = el.createChild({
14602             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14603         });
14604         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14605         //disabling touch- as it's causing issues ..
14606         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14607         ul.on('click'   , this.onClick, this);
14608         
14609         
14610         ul.on("mouseover", this.onMouseOver, this);
14611         ul.on("mouseout", this.onMouseOut, this);
14612         this.items.each(function(item){
14613             if (item.hidden) {
14614                 return;
14615             }
14616             
14617             var li = document.createElement("li");
14618             li.className = "x-menu-list-item";
14619             ul.dom.appendChild(li);
14620             item.render(li, this);
14621         }, this);
14622         this.ul = ul;
14623         this.autoWidth();
14624     },
14625
14626     // private
14627     autoWidth : function(){
14628         var el = this.el, ul = this.ul;
14629         if(!el){
14630             return;
14631         }
14632         var w = this.width;
14633         if(w){
14634             el.setWidth(w);
14635         }else if(Roo.isIE){
14636             el.setWidth(this.minWidth);
14637             var t = el.dom.offsetWidth; // force recalc
14638             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14639         }
14640     },
14641
14642     // private
14643     delayAutoWidth : function(){
14644         if(this.rendered){
14645             if(!this.awTask){
14646                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14647             }
14648             this.awTask.delay(20);
14649         }
14650     },
14651
14652     // private
14653     findTargetItem : function(e){
14654         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14655         if(t && t.menuItemId){
14656             return this.items.get(t.menuItemId);
14657         }
14658     },
14659
14660     // private
14661     onClick : function(e){
14662         Roo.log("menu.onClick");
14663         var t = this.findTargetItem(e);
14664         if(!t){
14665             return;
14666         }
14667         Roo.log(e);
14668         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14669             if(t == this.activeItem && t.shouldDeactivate(e)){
14670                 this.activeItem.deactivate();
14671                 delete this.activeItem;
14672                 return;
14673             }
14674             if(t.canActivate){
14675                 this.setActiveItem(t, true);
14676             }
14677             return;
14678             
14679             
14680         }
14681         
14682         t.onClick(e);
14683         this.fireEvent("click", this, t, e);
14684     },
14685
14686     // private
14687     setActiveItem : function(item, autoExpand){
14688         if(item != this.activeItem){
14689             if(this.activeItem){
14690                 this.activeItem.deactivate();
14691             }
14692             this.activeItem = item;
14693             item.activate(autoExpand);
14694         }else if(autoExpand){
14695             item.expandMenu();
14696         }
14697     },
14698
14699     // private
14700     tryActivate : function(start, step){
14701         var items = this.items;
14702         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14703             var item = items.get(i);
14704             if(!item.disabled && item.canActivate){
14705                 this.setActiveItem(item, false);
14706                 return item;
14707             }
14708         }
14709         return false;
14710     },
14711
14712     // private
14713     onMouseOver : function(e){
14714         var t;
14715         if(t = this.findTargetItem(e)){
14716             if(t.canActivate && !t.disabled){
14717                 this.setActiveItem(t, true);
14718             }
14719         }
14720         this.fireEvent("mouseover", this, e, t);
14721     },
14722
14723     // private
14724     onMouseOut : function(e){
14725         var t;
14726         if(t = this.findTargetItem(e)){
14727             if(t == this.activeItem && t.shouldDeactivate(e)){
14728                 this.activeItem.deactivate();
14729                 delete this.activeItem;
14730             }
14731         }
14732         this.fireEvent("mouseout", this, e, t);
14733     },
14734
14735     /**
14736      * Read-only.  Returns true if the menu is currently displayed, else false.
14737      * @type Boolean
14738      */
14739     isVisible : function(){
14740         return this.el && !this.hidden;
14741     },
14742
14743     /**
14744      * Displays this menu relative to another element
14745      * @param {String/HTMLElement/Roo.Element} element The element to align to
14746      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14747      * the element (defaults to this.defaultAlign)
14748      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14749      */
14750     show : function(el, pos, parentMenu){
14751         this.parentMenu = parentMenu;
14752         if(!this.el){
14753             this.render();
14754         }
14755         this.fireEvent("beforeshow", this);
14756         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14757     },
14758
14759     /**
14760      * Displays this menu at a specific xy position
14761      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14762      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14763      */
14764     showAt : function(xy, parentMenu, /* private: */_e){
14765         this.parentMenu = parentMenu;
14766         if(!this.el){
14767             this.render();
14768         }
14769         if(_e !== false){
14770             this.fireEvent("beforeshow", this);
14771             xy = this.el.adjustForConstraints(xy);
14772         }
14773         this.el.setXY(xy);
14774         this.el.show();
14775         this.hidden = false;
14776         this.focus();
14777         this.fireEvent("show", this);
14778     },
14779
14780     focus : function(){
14781         if(!this.hidden){
14782             this.doFocus.defer(50, this);
14783         }
14784     },
14785
14786     doFocus : function(){
14787         if(!this.hidden){
14788             this.focusEl.focus();
14789         }
14790     },
14791
14792     /**
14793      * Hides this menu and optionally all parent menus
14794      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14795      */
14796     hide : function(deep){
14797         if(this.el && this.isVisible()){
14798             this.fireEvent("beforehide", this);
14799             if(this.activeItem){
14800                 this.activeItem.deactivate();
14801                 this.activeItem = null;
14802             }
14803             this.el.hide();
14804             this.hidden = true;
14805             this.fireEvent("hide", this);
14806         }
14807         if(deep === true && this.parentMenu){
14808             this.parentMenu.hide(true);
14809         }
14810     },
14811
14812     /**
14813      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14814      * Any of the following are valid:
14815      * <ul>
14816      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14817      * <li>An HTMLElement object which will be converted to a menu item</li>
14818      * <li>A menu item config object that will be created as a new menu item</li>
14819      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14820      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14821      * </ul>
14822      * Usage:
14823      * <pre><code>
14824 // Create the menu
14825 var menu = new Roo.menu.Menu();
14826
14827 // Create a menu item to add by reference
14828 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14829
14830 // Add a bunch of items at once using different methods.
14831 // Only the last item added will be returned.
14832 var item = menu.add(
14833     menuItem,                // add existing item by ref
14834     'Dynamic Item',          // new TextItem
14835     '-',                     // new separator
14836     { text: 'Config Item' }  // new item by config
14837 );
14838 </code></pre>
14839      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14840      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14841      */
14842     add : function(){
14843         var a = arguments, l = a.length, item;
14844         for(var i = 0; i < l; i++){
14845             var el = a[i];
14846             if ((typeof(el) == "object") && el.xtype && el.xns) {
14847                 el = Roo.factory(el, Roo.menu);
14848             }
14849             
14850             if(el.render){ // some kind of Item
14851                 item = this.addItem(el);
14852             }else if(typeof el == "string"){ // string
14853                 if(el == "separator" || el == "-"){
14854                     item = this.addSeparator();
14855                 }else{
14856                     item = this.addText(el);
14857                 }
14858             }else if(el.tagName || el.el){ // element
14859                 item = this.addElement(el);
14860             }else if(typeof el == "object"){ // must be menu item config?
14861                 item = this.addMenuItem(el);
14862             }
14863         }
14864         return item;
14865     },
14866
14867     /**
14868      * Returns this menu's underlying {@link Roo.Element} object
14869      * @return {Roo.Element} The element
14870      */
14871     getEl : function(){
14872         if(!this.el){
14873             this.render();
14874         }
14875         return this.el;
14876     },
14877
14878     /**
14879      * Adds a separator bar to the menu
14880      * @return {Roo.menu.Item} The menu item that was added
14881      */
14882     addSeparator : function(){
14883         return this.addItem(new Roo.menu.Separator());
14884     },
14885
14886     /**
14887      * Adds an {@link Roo.Element} object to the menu
14888      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14889      * @return {Roo.menu.Item} The menu item that was added
14890      */
14891     addElement : function(el){
14892         return this.addItem(new Roo.menu.BaseItem(el));
14893     },
14894
14895     /**
14896      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14897      * @param {Roo.menu.Item} item The menu item to add
14898      * @return {Roo.menu.Item} The menu item that was added
14899      */
14900     addItem : function(item){
14901         this.items.add(item);
14902         if(this.ul){
14903             var li = document.createElement("li");
14904             li.className = "x-menu-list-item";
14905             this.ul.dom.appendChild(li);
14906             item.render(li, this);
14907             this.delayAutoWidth();
14908         }
14909         return item;
14910     },
14911
14912     /**
14913      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14914      * @param {Object} config A MenuItem config object
14915      * @return {Roo.menu.Item} The menu item that was added
14916      */
14917     addMenuItem : function(config){
14918         if(!(config instanceof Roo.menu.Item)){
14919             if(typeof config.checked == "boolean"){ // must be check menu item config?
14920                 config = new Roo.menu.CheckItem(config);
14921             }else{
14922                 config = new Roo.menu.Item(config);
14923             }
14924         }
14925         return this.addItem(config);
14926     },
14927
14928     /**
14929      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14930      * @param {String} text The text to display in the menu item
14931      * @return {Roo.menu.Item} The menu item that was added
14932      */
14933     addText : function(text){
14934         return this.addItem(new Roo.menu.TextItem({ text : text }));
14935     },
14936
14937     /**
14938      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14939      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14940      * @param {Roo.menu.Item} item The menu item to add
14941      * @return {Roo.menu.Item} The menu item that was added
14942      */
14943     insert : function(index, item){
14944         this.items.insert(index, item);
14945         if(this.ul){
14946             var li = document.createElement("li");
14947             li.className = "x-menu-list-item";
14948             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14949             item.render(li, this);
14950             this.delayAutoWidth();
14951         }
14952         return item;
14953     },
14954
14955     /**
14956      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14957      * @param {Roo.menu.Item} item The menu item to remove
14958      */
14959     remove : function(item){
14960         this.items.removeKey(item.id);
14961         item.destroy();
14962     },
14963
14964     /**
14965      * Removes and destroys all items in the menu
14966      */
14967     removeAll : function(){
14968         var f;
14969         while(f = this.items.first()){
14970             this.remove(f);
14971         }
14972     }
14973 });
14974
14975 // MenuNav is a private utility class used internally by the Menu
14976 Roo.menu.MenuNav = function(menu){
14977     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14978     this.scope = this.menu = menu;
14979 };
14980
14981 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14982     doRelay : function(e, h){
14983         var k = e.getKey();
14984         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14985             this.menu.tryActivate(0, 1);
14986             return false;
14987         }
14988         return h.call(this.scope || this, e, this.menu);
14989     },
14990
14991     up : function(e, m){
14992         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14993             m.tryActivate(m.items.length-1, -1);
14994         }
14995     },
14996
14997     down : function(e, m){
14998         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14999             m.tryActivate(0, 1);
15000         }
15001     },
15002
15003     right : function(e, m){
15004         if(m.activeItem){
15005             m.activeItem.expandMenu(true);
15006         }
15007     },
15008
15009     left : function(e, m){
15010         m.hide();
15011         if(m.parentMenu && m.parentMenu.activeItem){
15012             m.parentMenu.activeItem.activate();
15013         }
15014     },
15015
15016     enter : function(e, m){
15017         if(m.activeItem){
15018             e.stopPropagation();
15019             m.activeItem.onClick(e);
15020             m.fireEvent("click", this, m.activeItem);
15021             return true;
15022         }
15023     }
15024 });/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034  
15035 /**
15036  * @class Roo.menu.MenuMgr
15037  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15038  * @static
15039  */
15040 Roo.menu.MenuMgr = function(){
15041    var menus, active, groups = {}, attached = false, lastShow = new Date();
15042
15043    // private - called when first menu is created
15044    function init(){
15045        menus = {};
15046        active = new Roo.util.MixedCollection();
15047        Roo.get(document).addKeyListener(27, function(){
15048            if(active.length > 0){
15049                hideAll();
15050            }
15051        });
15052    }
15053
15054    // private
15055    function hideAll(){
15056        if(active && active.length > 0){
15057            var c = active.clone();
15058            c.each(function(m){
15059                m.hide();
15060            });
15061        }
15062    }
15063
15064    // private
15065    function onHide(m){
15066        active.remove(m);
15067        if(active.length < 1){
15068            Roo.get(document).un("mousedown", onMouseDown);
15069            attached = false;
15070        }
15071    }
15072
15073    // private
15074    function onShow(m){
15075        var last = active.last();
15076        lastShow = new Date();
15077        active.add(m);
15078        if(!attached){
15079            Roo.get(document).on("mousedown", onMouseDown);
15080            attached = true;
15081        }
15082        if(m.parentMenu){
15083           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15084           m.parentMenu.activeChild = m;
15085        }else if(last && last.isVisible()){
15086           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15087        }
15088    }
15089
15090    // private
15091    function onBeforeHide(m){
15092        if(m.activeChild){
15093            m.activeChild.hide();
15094        }
15095        if(m.autoHideTimer){
15096            clearTimeout(m.autoHideTimer);
15097            delete m.autoHideTimer;
15098        }
15099    }
15100
15101    // private
15102    function onBeforeShow(m){
15103        var pm = m.parentMenu;
15104        if(!pm && !m.allowOtherMenus){
15105            hideAll();
15106        }else if(pm && pm.activeChild && active != m){
15107            pm.activeChild.hide();
15108        }
15109    }
15110
15111    // private
15112    function onMouseDown(e){
15113        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15114            hideAll();
15115        }
15116    }
15117
15118    // private
15119    function onBeforeCheck(mi, state){
15120        if(state){
15121            var g = groups[mi.group];
15122            for(var i = 0, l = g.length; i < l; i++){
15123                if(g[i] != mi){
15124                    g[i].setChecked(false);
15125                }
15126            }
15127        }
15128    }
15129
15130    return {
15131
15132        /**
15133         * Hides all menus that are currently visible
15134         */
15135        hideAll : function(){
15136             hideAll();  
15137        },
15138
15139        // private
15140        register : function(menu){
15141            if(!menus){
15142                init();
15143            }
15144            menus[menu.id] = menu;
15145            menu.on("beforehide", onBeforeHide);
15146            menu.on("hide", onHide);
15147            menu.on("beforeshow", onBeforeShow);
15148            menu.on("show", onShow);
15149            var g = menu.group;
15150            if(g && menu.events["checkchange"]){
15151                if(!groups[g]){
15152                    groups[g] = [];
15153                }
15154                groups[g].push(menu);
15155                menu.on("checkchange", onCheck);
15156            }
15157        },
15158
15159         /**
15160          * Returns a {@link Roo.menu.Menu} object
15161          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15162          * be used to generate and return a new Menu instance.
15163          */
15164        get : function(menu){
15165            if(typeof menu == "string"){ // menu id
15166                return menus[menu];
15167            }else if(menu.events){  // menu instance
15168                return menu;
15169            }else if(typeof menu.length == 'number'){ // array of menu items?
15170                return new Roo.menu.Menu({items:menu});
15171            }else{ // otherwise, must be a config
15172                return new Roo.menu.Menu(menu);
15173            }
15174        },
15175
15176        // private
15177        unregister : function(menu){
15178            delete menus[menu.id];
15179            menu.un("beforehide", onBeforeHide);
15180            menu.un("hide", onHide);
15181            menu.un("beforeshow", onBeforeShow);
15182            menu.un("show", onShow);
15183            var g = menu.group;
15184            if(g && menu.events["checkchange"]){
15185                groups[g].remove(menu);
15186                menu.un("checkchange", onCheck);
15187            }
15188        },
15189
15190        // private
15191        registerCheckable : function(menuItem){
15192            var g = menuItem.group;
15193            if(g){
15194                if(!groups[g]){
15195                    groups[g] = [];
15196                }
15197                groups[g].push(menuItem);
15198                menuItem.on("beforecheckchange", onBeforeCheck);
15199            }
15200        },
15201
15202        // private
15203        unregisterCheckable : function(menuItem){
15204            var g = menuItem.group;
15205            if(g){
15206                groups[g].remove(menuItem);
15207                menuItem.un("beforecheckchange", onBeforeCheck);
15208            }
15209        }
15210    };
15211 }();/*
15212  * Based on:
15213  * Ext JS Library 1.1.1
15214  * Copyright(c) 2006-2007, Ext JS, LLC.
15215  *
15216  * Originally Released Under LGPL - original licence link has changed is not relivant.
15217  *
15218  * Fork - LGPL
15219  * <script type="text/javascript">
15220  */
15221  
15222
15223 /**
15224  * @class Roo.menu.BaseItem
15225  * @extends Roo.Component
15226  * @abstract
15227  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15228  * management and base configuration options shared by all menu components.
15229  * @constructor
15230  * Creates a new BaseItem
15231  * @param {Object} config Configuration options
15232  */
15233 Roo.menu.BaseItem = function(config){
15234     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15235
15236     this.addEvents({
15237         /**
15238          * @event click
15239          * Fires when this item is clicked
15240          * @param {Roo.menu.BaseItem} this
15241          * @param {Roo.EventObject} e
15242          */
15243         click: true,
15244         /**
15245          * @event activate
15246          * Fires when this item is activated
15247          * @param {Roo.menu.BaseItem} this
15248          */
15249         activate : true,
15250         /**
15251          * @event deactivate
15252          * Fires when this item is deactivated
15253          * @param {Roo.menu.BaseItem} this
15254          */
15255         deactivate : true
15256     });
15257
15258     if(this.handler){
15259         this.on("click", this.handler, this.scope, true);
15260     }
15261 };
15262
15263 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15264     /**
15265      * @cfg {Function} handler
15266      * A function that will handle the click event of this menu item (defaults to undefined)
15267      */
15268     /**
15269      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15270      */
15271     canActivate : false,
15272     
15273      /**
15274      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15275      */
15276     hidden: false,
15277     
15278     /**
15279      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15280      */
15281     activeClass : "x-menu-item-active",
15282     /**
15283      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15284      */
15285     hideOnClick : true,
15286     /**
15287      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15288      */
15289     hideDelay : 100,
15290
15291     // private
15292     ctype: "Roo.menu.BaseItem",
15293
15294     // private
15295     actionMode : "container",
15296
15297     // private
15298     render : function(container, parentMenu){
15299         this.parentMenu = parentMenu;
15300         Roo.menu.BaseItem.superclass.render.call(this, container);
15301         this.container.menuItemId = this.id;
15302     },
15303
15304     // private
15305     onRender : function(container, position){
15306         this.el = Roo.get(this.el);
15307         container.dom.appendChild(this.el.dom);
15308     },
15309
15310     // private
15311     onClick : function(e){
15312         if(!this.disabled && this.fireEvent("click", this, e) !== false
15313                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15314             this.handleClick(e);
15315         }else{
15316             e.stopEvent();
15317         }
15318     },
15319
15320     // private
15321     activate : function(){
15322         if(this.disabled){
15323             return false;
15324         }
15325         var li = this.container;
15326         li.addClass(this.activeClass);
15327         this.region = li.getRegion().adjust(2, 2, -2, -2);
15328         this.fireEvent("activate", this);
15329         return true;
15330     },
15331
15332     // private
15333     deactivate : function(){
15334         this.container.removeClass(this.activeClass);
15335         this.fireEvent("deactivate", this);
15336     },
15337
15338     // private
15339     shouldDeactivate : function(e){
15340         return !this.region || !this.region.contains(e.getPoint());
15341     },
15342
15343     // private
15344     handleClick : function(e){
15345         if(this.hideOnClick){
15346             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15347         }
15348     },
15349
15350     // private
15351     expandMenu : function(autoActivate){
15352         // do nothing
15353     },
15354
15355     // private
15356     hideMenu : function(){
15357         // do nothing
15358     }
15359 });/*
15360  * Based on:
15361  * Ext JS Library 1.1.1
15362  * Copyright(c) 2006-2007, Ext JS, LLC.
15363  *
15364  * Originally Released Under LGPL - original licence link has changed is not relivant.
15365  *
15366  * Fork - LGPL
15367  * <script type="text/javascript">
15368  */
15369  
15370 /**
15371  * @class Roo.menu.Adapter
15372  * @extends Roo.menu.BaseItem
15373  * @abstract
15374  * 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.
15375  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15376  * @constructor
15377  * Creates a new Adapter
15378  * @param {Object} config Configuration options
15379  */
15380 Roo.menu.Adapter = function(component, config){
15381     Roo.menu.Adapter.superclass.constructor.call(this, config);
15382     this.component = component;
15383 };
15384 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15385     // private
15386     canActivate : true,
15387
15388     // private
15389     onRender : function(container, position){
15390         this.component.render(container);
15391         this.el = this.component.getEl();
15392     },
15393
15394     // private
15395     activate : function(){
15396         if(this.disabled){
15397             return false;
15398         }
15399         this.component.focus();
15400         this.fireEvent("activate", this);
15401         return true;
15402     },
15403
15404     // private
15405     deactivate : function(){
15406         this.fireEvent("deactivate", this);
15407     },
15408
15409     // private
15410     disable : function(){
15411         this.component.disable();
15412         Roo.menu.Adapter.superclass.disable.call(this);
15413     },
15414
15415     // private
15416     enable : function(){
15417         this.component.enable();
15418         Roo.menu.Adapter.superclass.enable.call(this);
15419     }
15420 });/*
15421  * Based on:
15422  * Ext JS Library 1.1.1
15423  * Copyright(c) 2006-2007, Ext JS, LLC.
15424  *
15425  * Originally Released Under LGPL - original licence link has changed is not relivant.
15426  *
15427  * Fork - LGPL
15428  * <script type="text/javascript">
15429  */
15430
15431 /**
15432  * @class Roo.menu.TextItem
15433  * @extends Roo.menu.BaseItem
15434  * Adds a static text string to a menu, usually used as either a heading or group separator.
15435  * Note: old style constructor with text is still supported.
15436  * 
15437  * @constructor
15438  * Creates a new TextItem
15439  * @param {Object} cfg Configuration
15440  */
15441 Roo.menu.TextItem = function(cfg){
15442     if (typeof(cfg) == 'string') {
15443         this.text = cfg;
15444     } else {
15445         Roo.apply(this,cfg);
15446     }
15447     
15448     Roo.menu.TextItem.superclass.constructor.call(this);
15449 };
15450
15451 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15452     /**
15453      * @cfg {String} text Text to show on item.
15454      */
15455     text : '',
15456     
15457     /**
15458      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15459      */
15460     hideOnClick : false,
15461     /**
15462      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15463      */
15464     itemCls : "x-menu-text",
15465
15466     // private
15467     onRender : function(){
15468         var s = document.createElement("span");
15469         s.className = this.itemCls;
15470         s.innerHTML = this.text;
15471         this.el = s;
15472         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15473     }
15474 });/*
15475  * Based on:
15476  * Ext JS Library 1.1.1
15477  * Copyright(c) 2006-2007, Ext JS, LLC.
15478  *
15479  * Originally Released Under LGPL - original licence link has changed is not relivant.
15480  *
15481  * Fork - LGPL
15482  * <script type="text/javascript">
15483  */
15484
15485 /**
15486  * @class Roo.menu.Separator
15487  * @extends Roo.menu.BaseItem
15488  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15489  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15490  * @constructor
15491  * @param {Object} config Configuration options
15492  */
15493 Roo.menu.Separator = function(config){
15494     Roo.menu.Separator.superclass.constructor.call(this, config);
15495 };
15496
15497 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15498     /**
15499      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15500      */
15501     itemCls : "x-menu-sep",
15502     /**
15503      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15504      */
15505     hideOnClick : false,
15506
15507     // private
15508     onRender : function(li){
15509         var s = document.createElement("span");
15510         s.className = this.itemCls;
15511         s.innerHTML = "&#160;";
15512         this.el = s;
15513         li.addClass("x-menu-sep-li");
15514         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15515     }
15516 });/*
15517  * Based on:
15518  * Ext JS Library 1.1.1
15519  * Copyright(c) 2006-2007, Ext JS, LLC.
15520  *
15521  * Originally Released Under LGPL - original licence link has changed is not relivant.
15522  *
15523  * Fork - LGPL
15524  * <script type="text/javascript">
15525  */
15526 /**
15527  * @class Roo.menu.Item
15528  * @extends Roo.menu.BaseItem
15529  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15530  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15531  * activation and click handling.
15532  * @constructor
15533  * Creates a new Item
15534  * @param {Object} config Configuration options
15535  */
15536 Roo.menu.Item = function(config){
15537     Roo.menu.Item.superclass.constructor.call(this, config);
15538     if(this.menu){
15539         this.menu = Roo.menu.MenuMgr.get(this.menu);
15540     }
15541 };
15542 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15543     /**
15544      * @cfg {Roo.menu.Menu} menu
15545      * A Sub menu
15546      */
15547     /**
15548      * @cfg {String} text
15549      * The text to show on the menu item.
15550      */
15551     text: '',
15552      /**
15553      * @cfg {String} html to render in menu
15554      * The text to show on the menu item (HTML version).
15555      */
15556     html: '',
15557     /**
15558      * @cfg {String} icon
15559      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15560      */
15561     icon: undefined,
15562     /**
15563      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15564      */
15565     itemCls : "x-menu-item",
15566     /**
15567      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15568      */
15569     canActivate : true,
15570     /**
15571      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15572      */
15573     showDelay: 200,
15574     // doc'd in BaseItem
15575     hideDelay: 200,
15576
15577     // private
15578     ctype: "Roo.menu.Item",
15579     
15580     // private
15581     onRender : function(container, position){
15582         var el = document.createElement("a");
15583         el.hideFocus = true;
15584         el.unselectable = "on";
15585         el.href = this.href || "#";
15586         if(this.hrefTarget){
15587             el.target = this.hrefTarget;
15588         }
15589         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15590         
15591         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15592         
15593         el.innerHTML = String.format(
15594                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15595                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15596         this.el = el;
15597         Roo.menu.Item.superclass.onRender.call(this, container, position);
15598     },
15599
15600     /**
15601      * Sets the text to display in this menu item
15602      * @param {String} text The text to display
15603      * @param {Boolean} isHTML true to indicate text is pure html.
15604      */
15605     setText : function(text, isHTML){
15606         if (isHTML) {
15607             this.html = text;
15608         } else {
15609             this.text = text;
15610             this.html = '';
15611         }
15612         if(this.rendered){
15613             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15614      
15615             this.el.update(String.format(
15616                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15617                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15618             this.parentMenu.autoWidth();
15619         }
15620     },
15621
15622     // private
15623     handleClick : function(e){
15624         if(!this.href){ // if no link defined, stop the event automatically
15625             e.stopEvent();
15626         }
15627         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15628     },
15629
15630     // private
15631     activate : function(autoExpand){
15632         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15633             this.focus();
15634             if(autoExpand){
15635                 this.expandMenu();
15636             }
15637         }
15638         return true;
15639     },
15640
15641     // private
15642     shouldDeactivate : function(e){
15643         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15644             if(this.menu && this.menu.isVisible()){
15645                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15646             }
15647             return true;
15648         }
15649         return false;
15650     },
15651
15652     // private
15653     deactivate : function(){
15654         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15655         this.hideMenu();
15656     },
15657
15658     // private
15659     expandMenu : function(autoActivate){
15660         if(!this.disabled && this.menu){
15661             clearTimeout(this.hideTimer);
15662             delete this.hideTimer;
15663             if(!this.menu.isVisible() && !this.showTimer){
15664                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15665             }else if (this.menu.isVisible() && autoActivate){
15666                 this.menu.tryActivate(0, 1);
15667             }
15668         }
15669     },
15670
15671     // private
15672     deferExpand : function(autoActivate){
15673         delete this.showTimer;
15674         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15675         if(autoActivate){
15676             this.menu.tryActivate(0, 1);
15677         }
15678     },
15679
15680     // private
15681     hideMenu : function(){
15682         clearTimeout(this.showTimer);
15683         delete this.showTimer;
15684         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15685             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15686         }
15687     },
15688
15689     // private
15690     deferHide : function(){
15691         delete this.hideTimer;
15692         this.menu.hide();
15693     }
15694 });/*
15695  * Based on:
15696  * Ext JS Library 1.1.1
15697  * Copyright(c) 2006-2007, Ext JS, LLC.
15698  *
15699  * Originally Released Under LGPL - original licence link has changed is not relivant.
15700  *
15701  * Fork - LGPL
15702  * <script type="text/javascript">
15703  */
15704  
15705 /**
15706  * @class Roo.menu.CheckItem
15707  * @extends Roo.menu.Item
15708  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15709  * @constructor
15710  * Creates a new CheckItem
15711  * @param {Object} config Configuration options
15712  */
15713 Roo.menu.CheckItem = function(config){
15714     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15715     this.addEvents({
15716         /**
15717          * @event beforecheckchange
15718          * Fires before the checked value is set, providing an opportunity to cancel if needed
15719          * @param {Roo.menu.CheckItem} this
15720          * @param {Boolean} checked The new checked value that will be set
15721          */
15722         "beforecheckchange" : true,
15723         /**
15724          * @event checkchange
15725          * Fires after the checked value has been set
15726          * @param {Roo.menu.CheckItem} this
15727          * @param {Boolean} checked The checked value that was set
15728          */
15729         "checkchange" : true
15730     });
15731     if(this.checkHandler){
15732         this.on('checkchange', this.checkHandler, this.scope);
15733     }
15734 };
15735 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15736     /**
15737      * @cfg {String} group
15738      * All check items with the same group name will automatically be grouped into a single-select
15739      * radio button group (defaults to '')
15740      */
15741     /**
15742      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15743      */
15744     itemCls : "x-menu-item x-menu-check-item",
15745     /**
15746      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15747      */
15748     groupClass : "x-menu-group-item",
15749
15750     /**
15751      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15752      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15753      * initialized with checked = true will be rendered as checked.
15754      */
15755     checked: false,
15756
15757     // private
15758     ctype: "Roo.menu.CheckItem",
15759
15760     // private
15761     onRender : function(c){
15762         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15763         if(this.group){
15764             this.el.addClass(this.groupClass);
15765         }
15766         Roo.menu.MenuMgr.registerCheckable(this);
15767         if(this.checked){
15768             this.checked = false;
15769             this.setChecked(true, true);
15770         }
15771     },
15772
15773     // private
15774     destroy : function(){
15775         if(this.rendered){
15776             Roo.menu.MenuMgr.unregisterCheckable(this);
15777         }
15778         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15779     },
15780
15781     /**
15782      * Set the checked state of this item
15783      * @param {Boolean} checked The new checked value
15784      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15785      */
15786     setChecked : function(state, suppressEvent){
15787         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15788             if(this.container){
15789                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15790             }
15791             this.checked = state;
15792             if(suppressEvent !== true){
15793                 this.fireEvent("checkchange", this, state);
15794             }
15795         }
15796     },
15797
15798     // private
15799     handleClick : function(e){
15800        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15801            this.setChecked(!this.checked);
15802        }
15803        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15804     }
15805 });/*
15806  * Based on:
15807  * Ext JS Library 1.1.1
15808  * Copyright(c) 2006-2007, Ext JS, LLC.
15809  *
15810  * Originally Released Under LGPL - original licence link has changed is not relivant.
15811  *
15812  * Fork - LGPL
15813  * <script type="text/javascript">
15814  */
15815  
15816 /**
15817  * @class Roo.menu.DateItem
15818  * @extends Roo.menu.Adapter
15819  * A menu item that wraps the {@link Roo.DatPicker} component.
15820  * @constructor
15821  * Creates a new DateItem
15822  * @param {Object} config Configuration options
15823  */
15824 Roo.menu.DateItem = function(config){
15825     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15826     /** The Roo.DatePicker object @type Roo.DatePicker */
15827     this.picker = this.component;
15828     this.addEvents({select: true});
15829     
15830     this.picker.on("render", function(picker){
15831         picker.getEl().swallowEvent("click");
15832         picker.container.addClass("x-menu-date-item");
15833     });
15834
15835     this.picker.on("select", this.onSelect, this);
15836 };
15837
15838 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15839     // private
15840     onSelect : function(picker, date){
15841         this.fireEvent("select", this, date, picker);
15842         Roo.menu.DateItem.superclass.handleClick.call(this);
15843     }
15844 });/*
15845  * Based on:
15846  * Ext JS Library 1.1.1
15847  * Copyright(c) 2006-2007, Ext JS, LLC.
15848  *
15849  * Originally Released Under LGPL - original licence link has changed is not relivant.
15850  *
15851  * Fork - LGPL
15852  * <script type="text/javascript">
15853  */
15854  
15855 /**
15856  * @class Roo.menu.ColorItem
15857  * @extends Roo.menu.Adapter
15858  * A menu item that wraps the {@link Roo.ColorPalette} component.
15859  * @constructor
15860  * Creates a new ColorItem
15861  * @param {Object} config Configuration options
15862  */
15863 Roo.menu.ColorItem = function(config){
15864     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15865     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15866     this.palette = this.component;
15867     this.relayEvents(this.palette, ["select"]);
15868     if(this.selectHandler){
15869         this.on('select', this.selectHandler, this.scope);
15870     }
15871 };
15872 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15873  * Based on:
15874  * Ext JS Library 1.1.1
15875  * Copyright(c) 2006-2007, Ext JS, LLC.
15876  *
15877  * Originally Released Under LGPL - original licence link has changed is not relivant.
15878  *
15879  * Fork - LGPL
15880  * <script type="text/javascript">
15881  */
15882  
15883
15884 /**
15885  * @class Roo.menu.DateMenu
15886  * @extends Roo.menu.Menu
15887  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15888  * @constructor
15889  * Creates a new DateMenu
15890  * @param {Object} config Configuration options
15891  */
15892 Roo.menu.DateMenu = function(config){
15893     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15894     this.plain = true;
15895     var di = new Roo.menu.DateItem(config);
15896     this.add(di);
15897     /**
15898      * The {@link Roo.DatePicker} instance for this DateMenu
15899      * @type DatePicker
15900      */
15901     this.picker = di.picker;
15902     /**
15903      * @event select
15904      * @param {DatePicker} picker
15905      * @param {Date} date
15906      */
15907     this.relayEvents(di, ["select"]);
15908     this.on('beforeshow', function(){
15909         if(this.picker){
15910             this.picker.hideMonthPicker(false);
15911         }
15912     }, this);
15913 };
15914 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15915     cls:'x-date-menu'
15916 });/*
15917  * Based on:
15918  * Ext JS Library 1.1.1
15919  * Copyright(c) 2006-2007, Ext JS, LLC.
15920  *
15921  * Originally Released Under LGPL - original licence link has changed is not relivant.
15922  *
15923  * Fork - LGPL
15924  * <script type="text/javascript">
15925  */
15926  
15927
15928 /**
15929  * @class Roo.menu.ColorMenu
15930  * @extends Roo.menu.Menu
15931  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15932  * @constructor
15933  * Creates a new ColorMenu
15934  * @param {Object} config Configuration options
15935  */
15936 Roo.menu.ColorMenu = function(config){
15937     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15938     this.plain = true;
15939     var ci = new Roo.menu.ColorItem(config);
15940     this.add(ci);
15941     /**
15942      * The {@link Roo.ColorPalette} instance for this ColorMenu
15943      * @type ColorPalette
15944      */
15945     this.palette = ci.palette;
15946     /**
15947      * @event select
15948      * @param {ColorPalette} palette
15949      * @param {String} color
15950      */
15951     this.relayEvents(ci, ["select"]);
15952 };
15953 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15954  * Based on:
15955  * Ext JS Library 1.1.1
15956  * Copyright(c) 2006-2007, Ext JS, LLC.
15957  *
15958  * Originally Released Under LGPL - original licence link has changed is not relivant.
15959  *
15960  * Fork - LGPL
15961  * <script type="text/javascript">
15962  */
15963  
15964 /**
15965  * @class Roo.form.TextItem
15966  * @extends Roo.BoxComponent
15967  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15968  * @constructor
15969  * Creates a new TextItem
15970  * @param {Object} config Configuration options
15971  */
15972 Roo.form.TextItem = function(config){
15973     Roo.form.TextItem.superclass.constructor.call(this, config);
15974 };
15975
15976 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15977     
15978     /**
15979      * @cfg {String} tag the tag for this item (default div)
15980      */
15981     tag : 'div',
15982     /**
15983      * @cfg {String} html the content for this item
15984      */
15985     html : '',
15986     
15987     getAutoCreate : function()
15988     {
15989         var cfg = {
15990             id: this.id,
15991             tag: this.tag,
15992             html: this.html,
15993             cls: 'x-form-item'
15994         };
15995         
15996         return cfg;
15997         
15998     },
15999     
16000     onRender : function(ct, position)
16001     {
16002         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16003         
16004         if(!this.el){
16005             var cfg = this.getAutoCreate();
16006             if(!cfg.name){
16007                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16008             }
16009             if (!cfg.name.length) {
16010                 delete cfg.name;
16011             }
16012             this.el = ct.createChild(cfg, position);
16013         }
16014     },
16015     /*
16016      * setHTML
16017      * @param {String} html update the Contents of the element.
16018      */
16019     setHTML : function(html)
16020     {
16021         this.fieldEl.dom.innerHTML = html;
16022     }
16023     
16024 });/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034  
16035 /**
16036  * @class Roo.form.Field
16037  * @extends Roo.BoxComponent
16038  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16039  * @constructor
16040  * Creates a new Field
16041  * @param {Object} config Configuration options
16042  */
16043 Roo.form.Field = function(config){
16044     Roo.form.Field.superclass.constructor.call(this, config);
16045 };
16046
16047 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16048     /**
16049      * @cfg {String} fieldLabel Label to use when rendering a form.
16050      */
16051        /**
16052      * @cfg {String} qtip Mouse over tip
16053      */
16054      
16055     /**
16056      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16057      */
16058     invalidClass : "x-form-invalid",
16059     /**
16060      * @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")
16061      */
16062     invalidText : "The value in this field is invalid",
16063     /**
16064      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16065      */
16066     focusClass : "x-form-focus",
16067     /**
16068      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16069       automatic validation (defaults to "keyup").
16070      */
16071     validationEvent : "keyup",
16072     /**
16073      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16074      */
16075     validateOnBlur : true,
16076     /**
16077      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16078      */
16079     validationDelay : 250,
16080     /**
16081      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16082      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16083      */
16084     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16085     /**
16086      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16087      */
16088     fieldClass : "x-form-field",
16089     /**
16090      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16091      *<pre>
16092 Value         Description
16093 -----------   ----------------------------------------------------------------------
16094 qtip          Display a quick tip when the user hovers over the field
16095 title         Display a default browser title attribute popup
16096 under         Add a block div beneath the field containing the error text
16097 side          Add an error icon to the right of the field with a popup on hover
16098 [element id]  Add the error text directly to the innerHTML of the specified element
16099 </pre>
16100      */
16101     msgTarget : 'qtip',
16102     /**
16103      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16104      */
16105     msgFx : 'normal',
16106
16107     /**
16108      * @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.
16109      */
16110     readOnly : false,
16111
16112     /**
16113      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16114      */
16115     disabled : false,
16116
16117     /**
16118      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16119      */
16120     inputType : undefined,
16121     
16122     /**
16123      * @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).
16124          */
16125         tabIndex : undefined,
16126         
16127     // private
16128     isFormField : true,
16129
16130     // private
16131     hasFocus : false,
16132     /**
16133      * @property {Roo.Element} fieldEl
16134      * Element Containing the rendered Field (with label etc.)
16135      */
16136     /**
16137      * @cfg {Mixed} value A value to initialize this field with.
16138      */
16139     value : undefined,
16140
16141     /**
16142      * @cfg {String} name The field's HTML name attribute.
16143      */
16144     /**
16145      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16146      */
16147     // private
16148     loadedValue : false,
16149      
16150      
16151         // private ??
16152         initComponent : function(){
16153         Roo.form.Field.superclass.initComponent.call(this);
16154         this.addEvents({
16155             /**
16156              * @event focus
16157              * Fires when this field receives input focus.
16158              * @param {Roo.form.Field} this
16159              */
16160             focus : true,
16161             /**
16162              * @event blur
16163              * Fires when this field loses input focus.
16164              * @param {Roo.form.Field} this
16165              */
16166             blur : true,
16167             /**
16168              * @event specialkey
16169              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16170              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16171              * @param {Roo.form.Field} this
16172              * @param {Roo.EventObject} e The event object
16173              */
16174             specialkey : true,
16175             /**
16176              * @event change
16177              * Fires just before the field blurs if the field value has changed.
16178              * @param {Roo.form.Field} this
16179              * @param {Mixed} newValue The new value
16180              * @param {Mixed} oldValue The original value
16181              */
16182             change : true,
16183             /**
16184              * @event invalid
16185              * Fires after the field has been marked as invalid.
16186              * @param {Roo.form.Field} this
16187              * @param {String} msg The validation message
16188              */
16189             invalid : true,
16190             /**
16191              * @event valid
16192              * Fires after the field has been validated with no errors.
16193              * @param {Roo.form.Field} this
16194              */
16195             valid : true,
16196              /**
16197              * @event keyup
16198              * Fires after the key up
16199              * @param {Roo.form.Field} this
16200              * @param {Roo.EventObject}  e The event Object
16201              */
16202             keyup : true
16203         });
16204     },
16205
16206     /**
16207      * Returns the name attribute of the field if available
16208      * @return {String} name The field name
16209      */
16210     getName: function(){
16211          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16212     },
16213
16214     // private
16215     onRender : function(ct, position){
16216         Roo.form.Field.superclass.onRender.call(this, ct, position);
16217         if(!this.el){
16218             var cfg = this.getAutoCreate();
16219             if(!cfg.name){
16220                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16221             }
16222             if (!cfg.name.length) {
16223                 delete cfg.name;
16224             }
16225             if(this.inputType){
16226                 cfg.type = this.inputType;
16227             }
16228             this.el = ct.createChild(cfg, position);
16229         }
16230         var type = this.el.dom.type;
16231         if(type){
16232             if(type == 'password'){
16233                 type = 'text';
16234             }
16235             this.el.addClass('x-form-'+type);
16236         }
16237         if(this.readOnly){
16238             this.el.dom.readOnly = true;
16239         }
16240         if(this.tabIndex !== undefined){
16241             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16242         }
16243
16244         this.el.addClass([this.fieldClass, this.cls]);
16245         this.initValue();
16246     },
16247
16248     /**
16249      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16250      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16251      * @return {Roo.form.Field} this
16252      */
16253     applyTo : function(target){
16254         this.allowDomMove = false;
16255         this.el = Roo.get(target);
16256         this.render(this.el.dom.parentNode);
16257         return this;
16258     },
16259
16260     // private
16261     initValue : function(){
16262         if(this.value !== undefined){
16263             this.setValue(this.value);
16264         }else if(this.el.dom.value.length > 0){
16265             this.setValue(this.el.dom.value);
16266         }
16267     },
16268
16269     /**
16270      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16271      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16272      */
16273     isDirty : function() {
16274         if(this.disabled) {
16275             return false;
16276         }
16277         return String(this.getValue()) !== String(this.originalValue);
16278     },
16279
16280     /**
16281      * stores the current value in loadedValue
16282      */
16283     resetHasChanged : function()
16284     {
16285         this.loadedValue = String(this.getValue());
16286     },
16287     /**
16288      * checks the current value against the 'loaded' value.
16289      * Note - will return false if 'resetHasChanged' has not been called first.
16290      */
16291     hasChanged : function()
16292     {
16293         if(this.disabled || this.readOnly) {
16294             return false;
16295         }
16296         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16297     },
16298     
16299     
16300     
16301     // private
16302     afterRender : function(){
16303         Roo.form.Field.superclass.afterRender.call(this);
16304         this.initEvents();
16305     },
16306
16307     // private
16308     fireKey : function(e){
16309         //Roo.log('field ' + e.getKey());
16310         if(e.isNavKeyPress()){
16311             this.fireEvent("specialkey", this, e);
16312         }
16313     },
16314
16315     /**
16316      * Resets the current field value to the originally loaded value and clears any validation messages
16317      */
16318     reset : function(){
16319         this.setValue(this.resetValue);
16320         this.originalValue = this.getValue();
16321         this.clearInvalid();
16322     },
16323
16324     // private
16325     initEvents : function(){
16326         // safari killled keypress - so keydown is now used..
16327         this.el.on("keydown" , this.fireKey,  this);
16328         this.el.on("focus", this.onFocus,  this);
16329         this.el.on("blur", this.onBlur,  this);
16330         this.el.relayEvent('keyup', this);
16331
16332         // reference to original value for reset
16333         this.originalValue = this.getValue();
16334         this.resetValue =  this.getValue();
16335     },
16336
16337     // private
16338     onFocus : function(){
16339         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16340             this.el.addClass(this.focusClass);
16341         }
16342         if(!this.hasFocus){
16343             this.hasFocus = true;
16344             this.startValue = this.getValue();
16345             this.fireEvent("focus", this);
16346         }
16347     },
16348
16349     beforeBlur : Roo.emptyFn,
16350
16351     // private
16352     onBlur : function(){
16353         this.beforeBlur();
16354         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16355             this.el.removeClass(this.focusClass);
16356         }
16357         this.hasFocus = false;
16358         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16359             this.validate();
16360         }
16361         var v = this.getValue();
16362         if(String(v) !== String(this.startValue)){
16363             this.fireEvent('change', this, v, this.startValue);
16364         }
16365         this.fireEvent("blur", this);
16366     },
16367
16368     /**
16369      * Returns whether or not the field value is currently valid
16370      * @param {Boolean} preventMark True to disable marking the field invalid
16371      * @return {Boolean} True if the value is valid, else false
16372      */
16373     isValid : function(preventMark){
16374         if(this.disabled){
16375             return true;
16376         }
16377         var restore = this.preventMark;
16378         this.preventMark = preventMark === true;
16379         var v = this.validateValue(this.processValue(this.getRawValue()));
16380         this.preventMark = restore;
16381         return v;
16382     },
16383
16384     /**
16385      * Validates the field value
16386      * @return {Boolean} True if the value is valid, else false
16387      */
16388     validate : function(){
16389         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16390             this.clearInvalid();
16391             return true;
16392         }
16393         return false;
16394     },
16395
16396     processValue : function(value){
16397         return value;
16398     },
16399
16400     // private
16401     // Subclasses should provide the validation implementation by overriding this
16402     validateValue : function(value){
16403         return true;
16404     },
16405
16406     /**
16407      * Mark this field as invalid
16408      * @param {String} msg The validation message
16409      */
16410     markInvalid : function(msg){
16411         if(!this.rendered || this.preventMark){ // not rendered
16412             return;
16413         }
16414         
16415         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16416         
16417         obj.el.addClass(this.invalidClass);
16418         msg = msg || this.invalidText;
16419         switch(this.msgTarget){
16420             case 'qtip':
16421                 obj.el.dom.qtip = msg;
16422                 obj.el.dom.qclass = 'x-form-invalid-tip';
16423                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16424                     Roo.QuickTips.enable();
16425                 }
16426                 break;
16427             case 'title':
16428                 this.el.dom.title = msg;
16429                 break;
16430             case 'under':
16431                 if(!this.errorEl){
16432                     var elp = this.el.findParent('.x-form-element', 5, true);
16433                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16434                     this.errorEl.setWidth(elp.getWidth(true)-20);
16435                 }
16436                 this.errorEl.update(msg);
16437                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16438                 break;
16439             case 'side':
16440                 if(!this.errorIcon){
16441                     var elp = this.el.findParent('.x-form-element', 5, true);
16442                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16443                 }
16444                 this.alignErrorIcon();
16445                 this.errorIcon.dom.qtip = msg;
16446                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16447                 this.errorIcon.show();
16448                 this.on('resize', this.alignErrorIcon, this);
16449                 break;
16450             default:
16451                 var t = Roo.getDom(this.msgTarget);
16452                 t.innerHTML = msg;
16453                 t.style.display = this.msgDisplay;
16454                 break;
16455         }
16456         this.fireEvent('invalid', this, msg);
16457     },
16458
16459     // private
16460     alignErrorIcon : function(){
16461         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16462     },
16463
16464     /**
16465      * Clear any invalid styles/messages for this field
16466      */
16467     clearInvalid : function(){
16468         if(!this.rendered || this.preventMark){ // not rendered
16469             return;
16470         }
16471         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16472         
16473         obj.el.removeClass(this.invalidClass);
16474         switch(this.msgTarget){
16475             case 'qtip':
16476                 obj.el.dom.qtip = '';
16477                 break;
16478             case 'title':
16479                 this.el.dom.title = '';
16480                 break;
16481             case 'under':
16482                 if(this.errorEl){
16483                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16484                 }
16485                 break;
16486             case 'side':
16487                 if(this.errorIcon){
16488                     this.errorIcon.dom.qtip = '';
16489                     this.errorIcon.hide();
16490                     this.un('resize', this.alignErrorIcon, this);
16491                 }
16492                 break;
16493             default:
16494                 var t = Roo.getDom(this.msgTarget);
16495                 t.innerHTML = '';
16496                 t.style.display = 'none';
16497                 break;
16498         }
16499         this.fireEvent('valid', this);
16500     },
16501
16502     /**
16503      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16504      * @return {Mixed} value The field value
16505      */
16506     getRawValue : function(){
16507         var v = this.el.getValue();
16508         
16509         return v;
16510     },
16511
16512     /**
16513      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16514      * @return {Mixed} value The field value
16515      */
16516     getValue : function(){
16517         var v = this.el.getValue();
16518          
16519         return v;
16520     },
16521
16522     /**
16523      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16524      * @param {Mixed} value The value to set
16525      */
16526     setRawValue : function(v){
16527         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16528     },
16529
16530     /**
16531      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16532      * @param {Mixed} value The value to set
16533      */
16534     setValue : function(v){
16535         this.value = v;
16536         if(this.rendered){
16537             this.el.dom.value = (v === null || v === undefined ? '' : v);
16538              this.validate();
16539         }
16540     },
16541
16542     adjustSize : function(w, h){
16543         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16544         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16545         return s;
16546     },
16547
16548     adjustWidth : function(tag, w){
16549         tag = tag.toLowerCase();
16550         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16551             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16552                 if(tag == 'input'){
16553                     return w + 2;
16554                 }
16555                 if(tag == 'textarea'){
16556                     return w-2;
16557                 }
16558             }else if(Roo.isOpera){
16559                 if(tag == 'input'){
16560                     return w + 2;
16561                 }
16562                 if(tag == 'textarea'){
16563                     return w-2;
16564                 }
16565             }
16566         }
16567         return w;
16568     }
16569 });
16570
16571
16572 // anything other than normal should be considered experimental
16573 Roo.form.Field.msgFx = {
16574     normal : {
16575         show: function(msgEl, f){
16576             msgEl.setDisplayed('block');
16577         },
16578
16579         hide : function(msgEl, f){
16580             msgEl.setDisplayed(false).update('');
16581         }
16582     },
16583
16584     slide : {
16585         show: function(msgEl, f){
16586             msgEl.slideIn('t', {stopFx:true});
16587         },
16588
16589         hide : function(msgEl, f){
16590             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16591         }
16592     },
16593
16594     slideRight : {
16595         show: function(msgEl, f){
16596             msgEl.fixDisplay();
16597             msgEl.alignTo(f.el, 'tl-tr');
16598             msgEl.slideIn('l', {stopFx:true});
16599         },
16600
16601         hide : function(msgEl, f){
16602             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16603         }
16604     }
16605 };/*
16606  * Based on:
16607  * Ext JS Library 1.1.1
16608  * Copyright(c) 2006-2007, Ext JS, LLC.
16609  *
16610  * Originally Released Under LGPL - original licence link has changed is not relivant.
16611  *
16612  * Fork - LGPL
16613  * <script type="text/javascript">
16614  */
16615  
16616
16617 /**
16618  * @class Roo.form.TextField
16619  * @extends Roo.form.Field
16620  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16621  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16622  * @constructor
16623  * Creates a new TextField
16624  * @param {Object} config Configuration options
16625  */
16626 Roo.form.TextField = function(config){
16627     Roo.form.TextField.superclass.constructor.call(this, config);
16628     this.addEvents({
16629         /**
16630          * @event autosize
16631          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16632          * according to the default logic, but this event provides a hook for the developer to apply additional
16633          * logic at runtime to resize the field if needed.
16634              * @param {Roo.form.Field} this This text field
16635              * @param {Number} width The new field width
16636              */
16637         autosize : true
16638     });
16639 };
16640
16641 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16642     /**
16643      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16644      */
16645     grow : false,
16646     /**
16647      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16648      */
16649     growMin : 30,
16650     /**
16651      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16652      */
16653     growMax : 800,
16654     /**
16655      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16656      */
16657     vtype : null,
16658     /**
16659      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16660      */
16661     maskRe : null,
16662     /**
16663      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16664      */
16665     disableKeyFilter : false,
16666     /**
16667      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16668      */
16669     allowBlank : true,
16670     /**
16671      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16672      */
16673     minLength : 0,
16674     /**
16675      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16676      */
16677     maxLength : Number.MAX_VALUE,
16678     /**
16679      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16680      */
16681     minLengthText : "The minimum length for this field is {0}",
16682     /**
16683      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16684      */
16685     maxLengthText : "The maximum length for this field is {0}",
16686     /**
16687      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16688      */
16689     selectOnFocus : false,
16690     /**
16691      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16692      */    
16693     allowLeadingSpace : false,
16694     /**
16695      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16696      */
16697     blankText : "This field is required",
16698     /**
16699      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16700      * If available, this function will be called only after the basic validators all return true, and will be passed the
16701      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16702      */
16703     validator : null,
16704     /**
16705      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16706      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16707      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16708      */
16709     regex : null,
16710     /**
16711      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16712      */
16713     regexText : "",
16714     /**
16715      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16716      */
16717     emptyText : null,
16718    
16719
16720     // private
16721     initEvents : function()
16722     {
16723         if (this.emptyText) {
16724             this.el.attr('placeholder', this.emptyText);
16725         }
16726         
16727         Roo.form.TextField.superclass.initEvents.call(this);
16728         if(this.validationEvent == 'keyup'){
16729             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16730             this.el.on('keyup', this.filterValidation, this);
16731         }
16732         else if(this.validationEvent !== false){
16733             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16734         }
16735         
16736         if(this.selectOnFocus){
16737             this.on("focus", this.preFocus, this);
16738         }
16739         if (!this.allowLeadingSpace) {
16740             this.on('blur', this.cleanLeadingSpace, this);
16741         }
16742         
16743         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16744             this.el.on("keypress", this.filterKeys, this);
16745         }
16746         if(this.grow){
16747             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16748             this.el.on("click", this.autoSize,  this);
16749         }
16750         if(this.el.is('input[type=password]') && Roo.isSafari){
16751             this.el.on('keydown', this.SafariOnKeyDown, this);
16752         }
16753     },
16754
16755     processValue : function(value){
16756         if(this.stripCharsRe){
16757             var newValue = value.replace(this.stripCharsRe, '');
16758             if(newValue !== value){
16759                 this.setRawValue(newValue);
16760                 return newValue;
16761             }
16762         }
16763         return value;
16764     },
16765
16766     filterValidation : function(e){
16767         if(!e.isNavKeyPress()){
16768             this.validationTask.delay(this.validationDelay);
16769         }
16770     },
16771
16772     // private
16773     onKeyUp : function(e){
16774         if(!e.isNavKeyPress()){
16775             this.autoSize();
16776         }
16777     },
16778     // private - clean the leading white space
16779     cleanLeadingSpace : function(e)
16780     {
16781         if ( this.inputType == 'file') {
16782             return;
16783         }
16784         
16785         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16786     },
16787     /**
16788      * Resets the current field value to the originally-loaded value and clears any validation messages.
16789      *  
16790      */
16791     reset : function(){
16792         Roo.form.TextField.superclass.reset.call(this);
16793        
16794     }, 
16795     // private
16796     preFocus : function(){
16797         
16798         if(this.selectOnFocus){
16799             this.el.dom.select();
16800         }
16801     },
16802
16803     
16804     // private
16805     filterKeys : function(e){
16806         var k = e.getKey();
16807         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16808             return;
16809         }
16810         var c = e.getCharCode(), cc = String.fromCharCode(c);
16811         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16812             return;
16813         }
16814         if(!this.maskRe.test(cc)){
16815             e.stopEvent();
16816         }
16817     },
16818
16819     setValue : function(v){
16820         
16821         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16822         
16823         this.autoSize();
16824     },
16825
16826     /**
16827      * Validates a value according to the field's validation rules and marks the field as invalid
16828      * if the validation fails
16829      * @param {Mixed} value The value to validate
16830      * @return {Boolean} True if the value is valid, else false
16831      */
16832     validateValue : function(value){
16833         if(value.length < 1)  { // if it's blank
16834              if(this.allowBlank){
16835                 this.clearInvalid();
16836                 return true;
16837              }else{
16838                 this.markInvalid(this.blankText);
16839                 return false;
16840              }
16841         }
16842         if(value.length < this.minLength){
16843             this.markInvalid(String.format(this.minLengthText, this.minLength));
16844             return false;
16845         }
16846         if(value.length > this.maxLength){
16847             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16848             return false;
16849         }
16850         if(this.vtype){
16851             var vt = Roo.form.VTypes;
16852             if(!vt[this.vtype](value, this)){
16853                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16854                 return false;
16855             }
16856         }
16857         if(typeof this.validator == "function"){
16858             var msg = this.validator(value);
16859             if(msg !== true){
16860                 this.markInvalid(msg);
16861                 return false;
16862             }
16863         }
16864         if(this.regex && !this.regex.test(value)){
16865             this.markInvalid(this.regexText);
16866             return false;
16867         }
16868         return true;
16869     },
16870
16871     /**
16872      * Selects text in this field
16873      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16874      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16875      */
16876     selectText : function(start, end){
16877         var v = this.getRawValue();
16878         if(v.length > 0){
16879             start = start === undefined ? 0 : start;
16880             end = end === undefined ? v.length : end;
16881             var d = this.el.dom;
16882             if(d.setSelectionRange){
16883                 d.setSelectionRange(start, end);
16884             }else if(d.createTextRange){
16885                 var range = d.createTextRange();
16886                 range.moveStart("character", start);
16887                 range.moveEnd("character", v.length-end);
16888                 range.select();
16889             }
16890         }
16891     },
16892
16893     /**
16894      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16895      * This only takes effect if grow = true, and fires the autosize event.
16896      */
16897     autoSize : function(){
16898         if(!this.grow || !this.rendered){
16899             return;
16900         }
16901         if(!this.metrics){
16902             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16903         }
16904         var el = this.el;
16905         var v = el.dom.value;
16906         var d = document.createElement('div');
16907         d.appendChild(document.createTextNode(v));
16908         v = d.innerHTML;
16909         d = null;
16910         v += "&#160;";
16911         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16912         this.el.setWidth(w);
16913         this.fireEvent("autosize", this, w);
16914     },
16915     
16916     // private
16917     SafariOnKeyDown : function(event)
16918     {
16919         // this is a workaround for a password hang bug on chrome/ webkit.
16920         
16921         var isSelectAll = false;
16922         
16923         if(this.el.dom.selectionEnd > 0){
16924             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16925         }
16926         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16927             event.preventDefault();
16928             this.setValue('');
16929             return;
16930         }
16931         
16932         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16933             
16934             event.preventDefault();
16935             // this is very hacky as keydown always get's upper case.
16936             
16937             var cc = String.fromCharCode(event.getCharCode());
16938             
16939             
16940             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16941             
16942         }
16943         
16944         
16945     }
16946 });/*
16947  * Based on:
16948  * Ext JS Library 1.1.1
16949  * Copyright(c) 2006-2007, Ext JS, LLC.
16950  *
16951  * Originally Released Under LGPL - original licence link has changed is not relivant.
16952  *
16953  * Fork - LGPL
16954  * <script type="text/javascript">
16955  */
16956  
16957 /**
16958  * @class Roo.form.Hidden
16959  * @extends Roo.form.TextField
16960  * Simple Hidden element used on forms 
16961  * 
16962  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16963  * 
16964  * @constructor
16965  * Creates a new Hidden form element.
16966  * @param {Object} config Configuration options
16967  */
16968
16969
16970
16971 // easy hidden field...
16972 Roo.form.Hidden = function(config){
16973     Roo.form.Hidden.superclass.constructor.call(this, config);
16974 };
16975   
16976 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16977     fieldLabel:      '',
16978     inputType:      'hidden',
16979     width:          50,
16980     allowBlank:     true,
16981     labelSeparator: '',
16982     hidden:         true,
16983     itemCls :       'x-form-item-display-none'
16984
16985
16986 });
16987
16988
16989 /*
16990  * Based on:
16991  * Ext JS Library 1.1.1
16992  * Copyright(c) 2006-2007, Ext JS, LLC.
16993  *
16994  * Originally Released Under LGPL - original licence link has changed is not relivant.
16995  *
16996  * Fork - LGPL
16997  * <script type="text/javascript">
16998  */
16999  
17000 /**
17001  * @class Roo.form.TriggerField
17002  * @extends Roo.form.TextField
17003  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17004  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17005  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17006  * for which you can provide a custom implementation.  For example:
17007  * <pre><code>
17008 var trigger = new Roo.form.TriggerField();
17009 trigger.onTriggerClick = myTriggerFn;
17010 trigger.applyTo('my-field');
17011 </code></pre>
17012  *
17013  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17014  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17015  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17016  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17017  * @constructor
17018  * Create a new TriggerField.
17019  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17020  * to the base TextField)
17021  */
17022 Roo.form.TriggerField = function(config){
17023     this.mimicing = false;
17024     Roo.form.TriggerField.superclass.constructor.call(this, config);
17025 };
17026
17027 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17028     /**
17029      * @cfg {String} triggerClass A CSS class to apply to the trigger
17030      */
17031     /**
17032      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17033      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17034      */
17035     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17036     /**
17037      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17038      */
17039     hideTrigger:false,
17040
17041     /** @cfg {Boolean} grow @hide */
17042     /** @cfg {Number} growMin @hide */
17043     /** @cfg {Number} growMax @hide */
17044
17045     /**
17046      * @hide 
17047      * @method
17048      */
17049     autoSize: Roo.emptyFn,
17050     // private
17051     monitorTab : true,
17052     // private
17053     deferHeight : true,
17054
17055     
17056     actionMode : 'wrap',
17057     // private
17058     onResize : function(w, h){
17059         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17060         if(typeof w == 'number'){
17061             var x = w - this.trigger.getWidth();
17062             this.el.setWidth(this.adjustWidth('input', x));
17063             this.trigger.setStyle('left', x+'px');
17064         }
17065     },
17066
17067     // private
17068     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17069
17070     // private
17071     getResizeEl : function(){
17072         return this.wrap;
17073     },
17074
17075     // private
17076     getPositionEl : function(){
17077         return this.wrap;
17078     },
17079
17080     // private
17081     alignErrorIcon : function(){
17082         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17083     },
17084
17085     // private
17086     onRender : function(ct, position){
17087         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17088         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17089         this.trigger = this.wrap.createChild(this.triggerConfig ||
17090                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17091         if(this.hideTrigger){
17092             this.trigger.setDisplayed(false);
17093         }
17094         this.initTrigger();
17095         if(!this.width){
17096             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17097         }
17098     },
17099
17100     // private
17101     initTrigger : function(){
17102         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17103         this.trigger.addClassOnOver('x-form-trigger-over');
17104         this.trigger.addClassOnClick('x-form-trigger-click');
17105     },
17106
17107     // private
17108     onDestroy : function(){
17109         if(this.trigger){
17110             this.trigger.removeAllListeners();
17111             this.trigger.remove();
17112         }
17113         if(this.wrap){
17114             this.wrap.remove();
17115         }
17116         Roo.form.TriggerField.superclass.onDestroy.call(this);
17117     },
17118
17119     // private
17120     onFocus : function(){
17121         Roo.form.TriggerField.superclass.onFocus.call(this);
17122         if(!this.mimicing){
17123             this.wrap.addClass('x-trigger-wrap-focus');
17124             this.mimicing = true;
17125             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17126             if(this.monitorTab){
17127                 this.el.on("keydown", this.checkTab, this);
17128             }
17129         }
17130     },
17131
17132     // private
17133     checkTab : function(e){
17134         if(e.getKey() == e.TAB){
17135             this.triggerBlur();
17136         }
17137     },
17138
17139     // private
17140     onBlur : function(){
17141         // do nothing
17142     },
17143
17144     // private
17145     mimicBlur : function(e, t){
17146         if(!this.wrap.contains(t) && this.validateBlur()){
17147             this.triggerBlur();
17148         }
17149     },
17150
17151     // private
17152     triggerBlur : function(){
17153         this.mimicing = false;
17154         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17155         if(this.monitorTab){
17156             this.el.un("keydown", this.checkTab, this);
17157         }
17158         this.wrap.removeClass('x-trigger-wrap-focus');
17159         Roo.form.TriggerField.superclass.onBlur.call(this);
17160     },
17161
17162     // private
17163     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17164     validateBlur : function(e, t){
17165         return true;
17166     },
17167
17168     // private
17169     onDisable : function(){
17170         Roo.form.TriggerField.superclass.onDisable.call(this);
17171         if(this.wrap){
17172             this.wrap.addClass('x-item-disabled');
17173         }
17174     },
17175
17176     // private
17177     onEnable : function(){
17178         Roo.form.TriggerField.superclass.onEnable.call(this);
17179         if(this.wrap){
17180             this.wrap.removeClass('x-item-disabled');
17181         }
17182     },
17183
17184     // private
17185     onShow : function(){
17186         var ae = this.getActionEl();
17187         
17188         if(ae){
17189             ae.dom.style.display = '';
17190             ae.dom.style.visibility = 'visible';
17191         }
17192     },
17193
17194     // private
17195     
17196     onHide : function(){
17197         var ae = this.getActionEl();
17198         ae.dom.style.display = 'none';
17199     },
17200
17201     /**
17202      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17203      * by an implementing function.
17204      * @method
17205      * @param {EventObject} e
17206      */
17207     onTriggerClick : Roo.emptyFn
17208 });
17209
17210 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17211 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17212 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17213 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17214     initComponent : function(){
17215         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17216
17217         this.triggerConfig = {
17218             tag:'span', cls:'x-form-twin-triggers', cn:[
17219             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17220             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17221         ]};
17222     },
17223
17224     getTrigger : function(index){
17225         return this.triggers[index];
17226     },
17227
17228     initTrigger : function(){
17229         var ts = this.trigger.select('.x-form-trigger', true);
17230         this.wrap.setStyle('overflow', 'hidden');
17231         var triggerField = this;
17232         ts.each(function(t, all, index){
17233             t.hide = function(){
17234                 var w = triggerField.wrap.getWidth();
17235                 this.dom.style.display = 'none';
17236                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17237             };
17238             t.show = function(){
17239                 var w = triggerField.wrap.getWidth();
17240                 this.dom.style.display = '';
17241                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17242             };
17243             var triggerIndex = 'Trigger'+(index+1);
17244
17245             if(this['hide'+triggerIndex]){
17246                 t.dom.style.display = 'none';
17247             }
17248             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17249             t.addClassOnOver('x-form-trigger-over');
17250             t.addClassOnClick('x-form-trigger-click');
17251         }, this);
17252         this.triggers = ts.elements;
17253     },
17254
17255     onTrigger1Click : Roo.emptyFn,
17256     onTrigger2Click : Roo.emptyFn
17257 });/*
17258  * Based on:
17259  * Ext JS Library 1.1.1
17260  * Copyright(c) 2006-2007, Ext JS, LLC.
17261  *
17262  * Originally Released Under LGPL - original licence link has changed is not relivant.
17263  *
17264  * Fork - LGPL
17265  * <script type="text/javascript">
17266  */
17267  
17268 /**
17269  * @class Roo.form.TextArea
17270  * @extends Roo.form.TextField
17271  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17272  * support for auto-sizing.
17273  * @constructor
17274  * Creates a new TextArea
17275  * @param {Object} config Configuration options
17276  */
17277 Roo.form.TextArea = function(config){
17278     Roo.form.TextArea.superclass.constructor.call(this, config);
17279     // these are provided exchanges for backwards compat
17280     // minHeight/maxHeight were replaced by growMin/growMax to be
17281     // compatible with TextField growing config values
17282     if(this.minHeight !== undefined){
17283         this.growMin = this.minHeight;
17284     }
17285     if(this.maxHeight !== undefined){
17286         this.growMax = this.maxHeight;
17287     }
17288 };
17289
17290 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17291     /**
17292      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17293      */
17294     growMin : 60,
17295     /**
17296      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17297      */
17298     growMax: 1000,
17299     /**
17300      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17301      * in the field (equivalent to setting overflow: hidden, defaults to false)
17302      */
17303     preventScrollbars: false,
17304     /**
17305      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17306      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17307      */
17308
17309     // private
17310     onRender : function(ct, position){
17311         if(!this.el){
17312             this.defaultAutoCreate = {
17313                 tag: "textarea",
17314                 style:"width:300px;height:60px;",
17315                 autocomplete: "new-password"
17316             };
17317         }
17318         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17319         if(this.grow){
17320             this.textSizeEl = Roo.DomHelper.append(document.body, {
17321                 tag: "pre", cls: "x-form-grow-sizer"
17322             });
17323             if(this.preventScrollbars){
17324                 this.el.setStyle("overflow", "hidden");
17325             }
17326             this.el.setHeight(this.growMin);
17327         }
17328     },
17329
17330     onDestroy : function(){
17331         if(this.textSizeEl){
17332             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17333         }
17334         Roo.form.TextArea.superclass.onDestroy.call(this);
17335     },
17336
17337     // private
17338     onKeyUp : function(e){
17339         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17340             this.autoSize();
17341         }
17342     },
17343
17344     /**
17345      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17346      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17347      */
17348     autoSize : function(){
17349         if(!this.grow || !this.textSizeEl){
17350             return;
17351         }
17352         var el = this.el;
17353         var v = el.dom.value;
17354         var ts = this.textSizeEl;
17355
17356         ts.innerHTML = '';
17357         ts.appendChild(document.createTextNode(v));
17358         v = ts.innerHTML;
17359
17360         Roo.fly(ts).setWidth(this.el.getWidth());
17361         if(v.length < 1){
17362             v = "&#160;&#160;";
17363         }else{
17364             if(Roo.isIE){
17365                 v = v.replace(/\n/g, '<p>&#160;</p>');
17366             }
17367             v += "&#160;\n&#160;";
17368         }
17369         ts.innerHTML = v;
17370         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17371         if(h != this.lastHeight){
17372             this.lastHeight = h;
17373             this.el.setHeight(h);
17374             this.fireEvent("autosize", this, h);
17375         }
17376     }
17377 });/*
17378  * Based on:
17379  * Ext JS Library 1.1.1
17380  * Copyright(c) 2006-2007, Ext JS, LLC.
17381  *
17382  * Originally Released Under LGPL - original licence link has changed is not relivant.
17383  *
17384  * Fork - LGPL
17385  * <script type="text/javascript">
17386  */
17387  
17388
17389 /**
17390  * @class Roo.form.NumberField
17391  * @extends Roo.form.TextField
17392  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17393  * @constructor
17394  * Creates a new NumberField
17395  * @param {Object} config Configuration options
17396  */
17397 Roo.form.NumberField = function(config){
17398     Roo.form.NumberField.superclass.constructor.call(this, config);
17399 };
17400
17401 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17402     /**
17403      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17404      */
17405     fieldClass: "x-form-field x-form-num-field",
17406     /**
17407      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17408      */
17409     allowDecimals : true,
17410     /**
17411      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17412      */
17413     decimalSeparator : ".",
17414     /**
17415      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17416      */
17417     decimalPrecision : 2,
17418     /**
17419      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17420      */
17421     allowNegative : true,
17422     /**
17423      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17424      */
17425     minValue : Number.NEGATIVE_INFINITY,
17426     /**
17427      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17428      */
17429     maxValue : Number.MAX_VALUE,
17430     /**
17431      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17432      */
17433     minText : "The minimum value for this field is {0}",
17434     /**
17435      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17436      */
17437     maxText : "The maximum value for this field is {0}",
17438     /**
17439      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17440      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17441      */
17442     nanText : "{0} is not a valid number",
17443
17444     // private
17445     initEvents : function(){
17446         Roo.form.NumberField.superclass.initEvents.call(this);
17447         var allowed = "0123456789";
17448         if(this.allowDecimals){
17449             allowed += this.decimalSeparator;
17450         }
17451         if(this.allowNegative){
17452             allowed += "-";
17453         }
17454         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17455         var keyPress = function(e){
17456             var k = e.getKey();
17457             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17458                 return;
17459             }
17460             var c = e.getCharCode();
17461             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17462                 e.stopEvent();
17463             }
17464         };
17465         this.el.on("keypress", keyPress, this);
17466     },
17467
17468     // private
17469     validateValue : function(value){
17470         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17471             return false;
17472         }
17473         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17474              return true;
17475         }
17476         var num = this.parseValue(value);
17477         if(isNaN(num)){
17478             this.markInvalid(String.format(this.nanText, value));
17479             return false;
17480         }
17481         if(num < this.minValue){
17482             this.markInvalid(String.format(this.minText, this.minValue));
17483             return false;
17484         }
17485         if(num > this.maxValue){
17486             this.markInvalid(String.format(this.maxText, this.maxValue));
17487             return false;
17488         }
17489         return true;
17490     },
17491
17492     getValue : function(){
17493         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17494     },
17495
17496     // private
17497     parseValue : function(value){
17498         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17499         return isNaN(value) ? '' : value;
17500     },
17501
17502     // private
17503     fixPrecision : function(value){
17504         var nan = isNaN(value);
17505         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17506             return nan ? '' : value;
17507         }
17508         return parseFloat(value).toFixed(this.decimalPrecision);
17509     },
17510
17511     setValue : function(v){
17512         v = this.fixPrecision(v);
17513         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17514     },
17515
17516     // private
17517     decimalPrecisionFcn : function(v){
17518         return Math.floor(v);
17519     },
17520
17521     beforeBlur : function(){
17522         var v = this.parseValue(this.getRawValue());
17523         if(v){
17524             this.setValue(v);
17525         }
17526     }
17527 });/*
17528  * Based on:
17529  * Ext JS Library 1.1.1
17530  * Copyright(c) 2006-2007, Ext JS, LLC.
17531  *
17532  * Originally Released Under LGPL - original licence link has changed is not relivant.
17533  *
17534  * Fork - LGPL
17535  * <script type="text/javascript">
17536  */
17537  
17538 /**
17539  * @class Roo.form.DateField
17540  * @extends Roo.form.TriggerField
17541  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17542 * @constructor
17543 * Create a new DateField
17544 * @param {Object} config
17545  */
17546 Roo.form.DateField = function(config)
17547 {
17548     Roo.form.DateField.superclass.constructor.call(this, config);
17549     
17550       this.addEvents({
17551          
17552         /**
17553          * @event select
17554          * Fires when a date is selected
17555              * @param {Roo.form.DateField} combo This combo box
17556              * @param {Date} date The date selected
17557              */
17558         'select' : true
17559          
17560     });
17561     
17562     
17563     if(typeof this.minValue == "string") {
17564         this.minValue = this.parseDate(this.minValue);
17565     }
17566     if(typeof this.maxValue == "string") {
17567         this.maxValue = this.parseDate(this.maxValue);
17568     }
17569     this.ddMatch = null;
17570     if(this.disabledDates){
17571         var dd = this.disabledDates;
17572         var re = "(?:";
17573         for(var i = 0; i < dd.length; i++){
17574             re += dd[i];
17575             if(i != dd.length-1) {
17576                 re += "|";
17577             }
17578         }
17579         this.ddMatch = new RegExp(re + ")");
17580     }
17581 };
17582
17583 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17584     /**
17585      * @cfg {String} format
17586      * The default date format string which can be overriden for localization support.  The format must be
17587      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17588      */
17589     format : "m/d/y",
17590     /**
17591      * @cfg {String} altFormats
17592      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17593      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17594      */
17595     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17596     /**
17597      * @cfg {Array} disabledDays
17598      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17599      */
17600     disabledDays : null,
17601     /**
17602      * @cfg {String} disabledDaysText
17603      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17604      */
17605     disabledDaysText : "Disabled",
17606     /**
17607      * @cfg {Array} disabledDates
17608      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17609      * expression so they are very powerful. Some examples:
17610      * <ul>
17611      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17612      * <li>["03/08", "09/16"] would disable those days for every year</li>
17613      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17614      * <li>["03/../2006"] would disable every day in March 2006</li>
17615      * <li>["^03"] would disable every day in every March</li>
17616      * </ul>
17617      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17618      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17619      */
17620     disabledDates : null,
17621     /**
17622      * @cfg {String} disabledDatesText
17623      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17624      */
17625     disabledDatesText : "Disabled",
17626         
17627         
17628         /**
17629      * @cfg {Date/String} zeroValue
17630      * if the date is less that this number, then the field is rendered as empty
17631      * default is 1800
17632      */
17633         zeroValue : '1800-01-01',
17634         
17635         
17636     /**
17637      * @cfg {Date/String} minValue
17638      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17639      * valid format (defaults to null).
17640      */
17641     minValue : null,
17642     /**
17643      * @cfg {Date/String} maxValue
17644      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17645      * valid format (defaults to null).
17646      */
17647     maxValue : null,
17648     /**
17649      * @cfg {String} minText
17650      * The error text to display when the date in the cell is before minValue (defaults to
17651      * 'The date in this field must be after {minValue}').
17652      */
17653     minText : "The date in this field must be equal to or after {0}",
17654     /**
17655      * @cfg {String} maxText
17656      * The error text to display when the date in the cell is after maxValue (defaults to
17657      * 'The date in this field must be before {maxValue}').
17658      */
17659     maxText : "The date in this field must be equal to or before {0}",
17660     /**
17661      * @cfg {String} invalidText
17662      * The error text to display when the date in the field is invalid (defaults to
17663      * '{value} is not a valid date - it must be in the format {format}').
17664      */
17665     invalidText : "{0} is not a valid date - it must be in the format {1}",
17666     /**
17667      * @cfg {String} triggerClass
17668      * An additional CSS class used to style the trigger button.  The trigger will always get the
17669      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17670      * which displays a calendar icon).
17671      */
17672     triggerClass : 'x-form-date-trigger',
17673     
17674
17675     /**
17676      * @cfg {Boolean} useIso
17677      * if enabled, then the date field will use a hidden field to store the 
17678      * real value as iso formated date. default (false)
17679      */ 
17680     useIso : false,
17681     /**
17682      * @cfg {String/Object} autoCreate
17683      * A DomHelper element spec, or true for a default element spec (defaults to
17684      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17685      */ 
17686     // private
17687     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17688     
17689     // private
17690     hiddenField: false,
17691     
17692     onRender : function(ct, position)
17693     {
17694         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17695         if (this.useIso) {
17696             //this.el.dom.removeAttribute('name'); 
17697             Roo.log("Changing name?");
17698             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17699             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17700                     'before', true);
17701             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17702             // prevent input submission
17703             this.hiddenName = this.name;
17704         }
17705             
17706             
17707     },
17708     
17709     // private
17710     validateValue : function(value)
17711     {
17712         value = this.formatDate(value);
17713         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17714             Roo.log('super failed');
17715             return false;
17716         }
17717         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17718              return true;
17719         }
17720         var svalue = value;
17721         value = this.parseDate(value);
17722         if(!value){
17723             Roo.log('parse date failed' + svalue);
17724             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17725             return false;
17726         }
17727         var time = value.getTime();
17728         if(this.minValue && time < this.minValue.getTime()){
17729             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17730             return false;
17731         }
17732         if(this.maxValue && time > this.maxValue.getTime()){
17733             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17734             return false;
17735         }
17736         if(this.disabledDays){
17737             var day = value.getDay();
17738             for(var i = 0; i < this.disabledDays.length; i++) {
17739                 if(day === this.disabledDays[i]){
17740                     this.markInvalid(this.disabledDaysText);
17741                     return false;
17742                 }
17743             }
17744         }
17745         var fvalue = this.formatDate(value);
17746         if(this.ddMatch && this.ddMatch.test(fvalue)){
17747             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17748             return false;
17749         }
17750         return true;
17751     },
17752
17753     // private
17754     // Provides logic to override the default TriggerField.validateBlur which just returns true
17755     validateBlur : function(){
17756         return !this.menu || !this.menu.isVisible();
17757     },
17758     
17759     getName: function()
17760     {
17761         // returns hidden if it's set..
17762         if (!this.rendered) {return ''};
17763         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17764         
17765     },
17766
17767     /**
17768      * Returns the current date value of the date field.
17769      * @return {Date} The date value
17770      */
17771     getValue : function(){
17772         
17773         return  this.hiddenField ?
17774                 this.hiddenField.value :
17775                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17776     },
17777
17778     /**
17779      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17780      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17781      * (the default format used is "m/d/y").
17782      * <br />Usage:
17783      * <pre><code>
17784 //All of these calls set the same date value (May 4, 2006)
17785
17786 //Pass a date object:
17787 var dt = new Date('5/4/06');
17788 dateField.setValue(dt);
17789
17790 //Pass a date string (default format):
17791 dateField.setValue('5/4/06');
17792
17793 //Pass a date string (custom format):
17794 dateField.format = 'Y-m-d';
17795 dateField.setValue('2006-5-4');
17796 </code></pre>
17797      * @param {String/Date} date The date or valid date string
17798      */
17799     setValue : function(date){
17800         if (this.hiddenField) {
17801             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17802         }
17803         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17804         // make sure the value field is always stored as a date..
17805         this.value = this.parseDate(date);
17806         
17807         
17808     },
17809
17810     // private
17811     parseDate : function(value){
17812                 
17813                 if (value instanceof Date) {
17814                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17815                                 return  '';
17816                         }
17817                         return value;
17818                 }
17819                 
17820                 
17821         if(!value || value instanceof Date){
17822             return value;
17823         }
17824         var v = Date.parseDate(value, this.format);
17825          if (!v && this.useIso) {
17826             v = Date.parseDate(value, 'Y-m-d');
17827         }
17828         if(!v && this.altFormats){
17829             if(!this.altFormatsArray){
17830                 this.altFormatsArray = this.altFormats.split("|");
17831             }
17832             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17833                 v = Date.parseDate(value, this.altFormatsArray[i]);
17834             }
17835         }
17836                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17837                         v = '';
17838                 }
17839         return v;
17840     },
17841
17842     // private
17843     formatDate : function(date, fmt){
17844         return (!date || !(date instanceof Date)) ?
17845                date : date.dateFormat(fmt || this.format);
17846     },
17847
17848     // private
17849     menuListeners : {
17850         select: function(m, d){
17851             
17852             this.setValue(d);
17853             this.fireEvent('select', this, d);
17854         },
17855         show : function(){ // retain focus styling
17856             this.onFocus();
17857         },
17858         hide : function(){
17859             this.focus.defer(10, this);
17860             var ml = this.menuListeners;
17861             this.menu.un("select", ml.select,  this);
17862             this.menu.un("show", ml.show,  this);
17863             this.menu.un("hide", ml.hide,  this);
17864         }
17865     },
17866
17867     // private
17868     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17869     onTriggerClick : function(){
17870         if(this.disabled){
17871             return;
17872         }
17873         if(this.menu == null){
17874             this.menu = new Roo.menu.DateMenu();
17875         }
17876         Roo.apply(this.menu.picker,  {
17877             showClear: this.allowBlank,
17878             minDate : this.minValue,
17879             maxDate : this.maxValue,
17880             disabledDatesRE : this.ddMatch,
17881             disabledDatesText : this.disabledDatesText,
17882             disabledDays : this.disabledDays,
17883             disabledDaysText : this.disabledDaysText,
17884             format : this.useIso ? 'Y-m-d' : this.format,
17885             minText : String.format(this.minText, this.formatDate(this.minValue)),
17886             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17887         });
17888         this.menu.on(Roo.apply({}, this.menuListeners, {
17889             scope:this
17890         }));
17891         this.menu.picker.setValue(this.getValue() || new Date());
17892         this.menu.show(this.el, "tl-bl?");
17893     },
17894
17895     beforeBlur : function(){
17896         var v = this.parseDate(this.getRawValue());
17897         if(v){
17898             this.setValue(v);
17899         }
17900     },
17901
17902     /*@
17903      * overide
17904      * 
17905      */
17906     isDirty : function() {
17907         if(this.disabled) {
17908             return false;
17909         }
17910         
17911         if(typeof(this.startValue) === 'undefined'){
17912             return false;
17913         }
17914         
17915         return String(this.getValue()) !== String(this.startValue);
17916         
17917     },
17918     // @overide
17919     cleanLeadingSpace : function(e)
17920     {
17921        return;
17922     }
17923     
17924 });/*
17925  * Based on:
17926  * Ext JS Library 1.1.1
17927  * Copyright(c) 2006-2007, Ext JS, LLC.
17928  *
17929  * Originally Released Under LGPL - original licence link has changed is not relivant.
17930  *
17931  * Fork - LGPL
17932  * <script type="text/javascript">
17933  */
17934  
17935 /**
17936  * @class Roo.form.MonthField
17937  * @extends Roo.form.TriggerField
17938  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17939 * @constructor
17940 * Create a new MonthField
17941 * @param {Object} config
17942  */
17943 Roo.form.MonthField = function(config){
17944     
17945     Roo.form.MonthField.superclass.constructor.call(this, config);
17946     
17947       this.addEvents({
17948          
17949         /**
17950          * @event select
17951          * Fires when a date is selected
17952              * @param {Roo.form.MonthFieeld} combo This combo box
17953              * @param {Date} date The date selected
17954              */
17955         'select' : true
17956          
17957     });
17958     
17959     
17960     if(typeof this.minValue == "string") {
17961         this.minValue = this.parseDate(this.minValue);
17962     }
17963     if(typeof this.maxValue == "string") {
17964         this.maxValue = this.parseDate(this.maxValue);
17965     }
17966     this.ddMatch = null;
17967     if(this.disabledDates){
17968         var dd = this.disabledDates;
17969         var re = "(?:";
17970         for(var i = 0; i < dd.length; i++){
17971             re += dd[i];
17972             if(i != dd.length-1) {
17973                 re += "|";
17974             }
17975         }
17976         this.ddMatch = new RegExp(re + ")");
17977     }
17978 };
17979
17980 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17981     /**
17982      * @cfg {String} format
17983      * The default date format string which can be overriden for localization support.  The format must be
17984      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17985      */
17986     format : "M Y",
17987     /**
17988      * @cfg {String} altFormats
17989      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17990      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17991      */
17992     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17993     /**
17994      * @cfg {Array} disabledDays
17995      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17996      */
17997     disabledDays : [0,1,2,3,4,5,6],
17998     /**
17999      * @cfg {String} disabledDaysText
18000      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18001      */
18002     disabledDaysText : "Disabled",
18003     /**
18004      * @cfg {Array} disabledDates
18005      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18006      * expression so they are very powerful. Some examples:
18007      * <ul>
18008      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18009      * <li>["03/08", "09/16"] would disable those days for every year</li>
18010      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18011      * <li>["03/../2006"] would disable every day in March 2006</li>
18012      * <li>["^03"] would disable every day in every March</li>
18013      * </ul>
18014      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18015      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18016      */
18017     disabledDates : null,
18018     /**
18019      * @cfg {String} disabledDatesText
18020      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18021      */
18022     disabledDatesText : "Disabled",
18023     /**
18024      * @cfg {Date/String} minValue
18025      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18026      * valid format (defaults to null).
18027      */
18028     minValue : null,
18029     /**
18030      * @cfg {Date/String} maxValue
18031      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18032      * valid format (defaults to null).
18033      */
18034     maxValue : null,
18035     /**
18036      * @cfg {String} minText
18037      * The error text to display when the date in the cell is before minValue (defaults to
18038      * 'The date in this field must be after {minValue}').
18039      */
18040     minText : "The date in this field must be equal to or after {0}",
18041     /**
18042      * @cfg {String} maxTextf
18043      * The error text to display when the date in the cell is after maxValue (defaults to
18044      * 'The date in this field must be before {maxValue}').
18045      */
18046     maxText : "The date in this field must be equal to or before {0}",
18047     /**
18048      * @cfg {String} invalidText
18049      * The error text to display when the date in the field is invalid (defaults to
18050      * '{value} is not a valid date - it must be in the format {format}').
18051      */
18052     invalidText : "{0} is not a valid date - it must be in the format {1}",
18053     /**
18054      * @cfg {String} triggerClass
18055      * An additional CSS class used to style the trigger button.  The trigger will always get the
18056      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18057      * which displays a calendar icon).
18058      */
18059     triggerClass : 'x-form-date-trigger',
18060     
18061
18062     /**
18063      * @cfg {Boolean} useIso
18064      * if enabled, then the date field will use a hidden field to store the 
18065      * real value as iso formated date. default (true)
18066      */ 
18067     useIso : true,
18068     /**
18069      * @cfg {String/Object} autoCreate
18070      * A DomHelper element spec, or true for a default element spec (defaults to
18071      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18072      */ 
18073     // private
18074     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18075     
18076     // private
18077     hiddenField: false,
18078     
18079     hideMonthPicker : false,
18080     
18081     onRender : function(ct, position)
18082     {
18083         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18084         if (this.useIso) {
18085             this.el.dom.removeAttribute('name'); 
18086             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18087                     'before', true);
18088             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18089             // prevent input submission
18090             this.hiddenName = this.name;
18091         }
18092             
18093             
18094     },
18095     
18096     // private
18097     validateValue : function(value)
18098     {
18099         value = this.formatDate(value);
18100         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18101             return false;
18102         }
18103         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18104              return true;
18105         }
18106         var svalue = value;
18107         value = this.parseDate(value);
18108         if(!value){
18109             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18110             return false;
18111         }
18112         var time = value.getTime();
18113         if(this.minValue && time < this.minValue.getTime()){
18114             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18115             return false;
18116         }
18117         if(this.maxValue && time > this.maxValue.getTime()){
18118             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18119             return false;
18120         }
18121         /*if(this.disabledDays){
18122             var day = value.getDay();
18123             for(var i = 0; i < this.disabledDays.length; i++) {
18124                 if(day === this.disabledDays[i]){
18125                     this.markInvalid(this.disabledDaysText);
18126                     return false;
18127                 }
18128             }
18129         }
18130         */
18131         var fvalue = this.formatDate(value);
18132         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18133             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18134             return false;
18135         }
18136         */
18137         return true;
18138     },
18139
18140     // private
18141     // Provides logic to override the default TriggerField.validateBlur which just returns true
18142     validateBlur : function(){
18143         return !this.menu || !this.menu.isVisible();
18144     },
18145
18146     /**
18147      * Returns the current date value of the date field.
18148      * @return {Date} The date value
18149      */
18150     getValue : function(){
18151         
18152         
18153         
18154         return  this.hiddenField ?
18155                 this.hiddenField.value :
18156                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18157     },
18158
18159     /**
18160      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18161      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18162      * (the default format used is "m/d/y").
18163      * <br />Usage:
18164      * <pre><code>
18165 //All of these calls set the same date value (May 4, 2006)
18166
18167 //Pass a date object:
18168 var dt = new Date('5/4/06');
18169 monthField.setValue(dt);
18170
18171 //Pass a date string (default format):
18172 monthField.setValue('5/4/06');
18173
18174 //Pass a date string (custom format):
18175 monthField.format = 'Y-m-d';
18176 monthField.setValue('2006-5-4');
18177 </code></pre>
18178      * @param {String/Date} date The date or valid date string
18179      */
18180     setValue : function(date){
18181         Roo.log('month setValue' + date);
18182         // can only be first of month..
18183         
18184         var val = this.parseDate(date);
18185         
18186         if (this.hiddenField) {
18187             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18188         }
18189         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18190         this.value = this.parseDate(date);
18191     },
18192
18193     // private
18194     parseDate : function(value){
18195         if(!value || value instanceof Date){
18196             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18197             return value;
18198         }
18199         var v = Date.parseDate(value, this.format);
18200         if (!v && this.useIso) {
18201             v = Date.parseDate(value, 'Y-m-d');
18202         }
18203         if (v) {
18204             // 
18205             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18206         }
18207         
18208         
18209         if(!v && this.altFormats){
18210             if(!this.altFormatsArray){
18211                 this.altFormatsArray = this.altFormats.split("|");
18212             }
18213             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18214                 v = Date.parseDate(value, this.altFormatsArray[i]);
18215             }
18216         }
18217         return v;
18218     },
18219
18220     // private
18221     formatDate : function(date, fmt){
18222         return (!date || !(date instanceof Date)) ?
18223                date : date.dateFormat(fmt || this.format);
18224     },
18225
18226     // private
18227     menuListeners : {
18228         select: function(m, d){
18229             this.setValue(d);
18230             this.fireEvent('select', this, d);
18231         },
18232         show : function(){ // retain focus styling
18233             this.onFocus();
18234         },
18235         hide : function(){
18236             this.focus.defer(10, this);
18237             var ml = this.menuListeners;
18238             this.menu.un("select", ml.select,  this);
18239             this.menu.un("show", ml.show,  this);
18240             this.menu.un("hide", ml.hide,  this);
18241         }
18242     },
18243     // private
18244     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18245     onTriggerClick : function(){
18246         if(this.disabled){
18247             return;
18248         }
18249         if(this.menu == null){
18250             this.menu = new Roo.menu.DateMenu();
18251            
18252         }
18253         
18254         Roo.apply(this.menu.picker,  {
18255             
18256             showClear: this.allowBlank,
18257             minDate : this.minValue,
18258             maxDate : this.maxValue,
18259             disabledDatesRE : this.ddMatch,
18260             disabledDatesText : this.disabledDatesText,
18261             
18262             format : this.useIso ? 'Y-m-d' : this.format,
18263             minText : String.format(this.minText, this.formatDate(this.minValue)),
18264             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18265             
18266         });
18267          this.menu.on(Roo.apply({}, this.menuListeners, {
18268             scope:this
18269         }));
18270        
18271         
18272         var m = this.menu;
18273         var p = m.picker;
18274         
18275         // hide month picker get's called when we called by 'before hide';
18276         
18277         var ignorehide = true;
18278         p.hideMonthPicker  = function(disableAnim){
18279             if (ignorehide) {
18280                 return;
18281             }
18282              if(this.monthPicker){
18283                 Roo.log("hideMonthPicker called");
18284                 if(disableAnim === true){
18285                     this.monthPicker.hide();
18286                 }else{
18287                     this.monthPicker.slideOut('t', {duration:.2});
18288                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18289                     p.fireEvent("select", this, this.value);
18290                     m.hide();
18291                 }
18292             }
18293         }
18294         
18295         Roo.log('picker set value');
18296         Roo.log(this.getValue());
18297         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18298         m.show(this.el, 'tl-bl?');
18299         ignorehide  = false;
18300         // this will trigger hideMonthPicker..
18301         
18302         
18303         // hidden the day picker
18304         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18305         
18306         
18307         
18308       
18309         
18310         p.showMonthPicker.defer(100, p);
18311     
18312         
18313        
18314     },
18315
18316     beforeBlur : function(){
18317         var v = this.parseDate(this.getRawValue());
18318         if(v){
18319             this.setValue(v);
18320         }
18321     }
18322
18323     /** @cfg {Boolean} grow @hide */
18324     /** @cfg {Number} growMin @hide */
18325     /** @cfg {Number} growMax @hide */
18326     /**
18327      * @hide
18328      * @method autoSize
18329      */
18330 });/*
18331  * Based on:
18332  * Ext JS Library 1.1.1
18333  * Copyright(c) 2006-2007, Ext JS, LLC.
18334  *
18335  * Originally Released Under LGPL - original licence link has changed is not relivant.
18336  *
18337  * Fork - LGPL
18338  * <script type="text/javascript">
18339  */
18340  
18341
18342 /**
18343  * @class Roo.form.ComboBox
18344  * @extends Roo.form.TriggerField
18345  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18346  * @constructor
18347  * Create a new ComboBox.
18348  * @param {Object} config Configuration options
18349  */
18350 Roo.form.ComboBox = function(config){
18351     Roo.form.ComboBox.superclass.constructor.call(this, config);
18352     this.addEvents({
18353         /**
18354          * @event expand
18355          * Fires when the dropdown list is expanded
18356              * @param {Roo.form.ComboBox} combo This combo box
18357              */
18358         'expand' : true,
18359         /**
18360          * @event collapse
18361          * Fires when the dropdown list is collapsed
18362              * @param {Roo.form.ComboBox} combo This combo box
18363              */
18364         'collapse' : true,
18365         /**
18366          * @event beforeselect
18367          * Fires before a list item is selected. Return false to cancel the selection.
18368              * @param {Roo.form.ComboBox} combo This combo box
18369              * @param {Roo.data.Record} record The data record returned from the underlying store
18370              * @param {Number} index The index of the selected item in the dropdown list
18371              */
18372         'beforeselect' : true,
18373         /**
18374          * @event select
18375          * Fires when a list item is selected
18376              * @param {Roo.form.ComboBox} combo This combo box
18377              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18378              * @param {Number} index The index of the selected item in the dropdown list
18379              */
18380         'select' : true,
18381         /**
18382          * @event beforequery
18383          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18384          * The event object passed has these properties:
18385              * @param {Roo.form.ComboBox} combo This combo box
18386              * @param {String} query The query
18387              * @param {Boolean} forceAll true to force "all" query
18388              * @param {Boolean} cancel true to cancel the query
18389              * @param {Object} e The query event object
18390              */
18391         'beforequery': true,
18392          /**
18393          * @event add
18394          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18395              * @param {Roo.form.ComboBox} combo This combo box
18396              */
18397         'add' : true,
18398         /**
18399          * @event edit
18400          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18401              * @param {Roo.form.ComboBox} combo This combo box
18402              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18403              */
18404         'edit' : true
18405         
18406         
18407     });
18408     if(this.transform){
18409         this.allowDomMove = false;
18410         var s = Roo.getDom(this.transform);
18411         if(!this.hiddenName){
18412             this.hiddenName = s.name;
18413         }
18414         if(!this.store){
18415             this.mode = 'local';
18416             var d = [], opts = s.options;
18417             for(var i = 0, len = opts.length;i < len; i++){
18418                 var o = opts[i];
18419                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18420                 if(o.selected) {
18421                     this.value = value;
18422                 }
18423                 d.push([value, o.text]);
18424             }
18425             this.store = new Roo.data.SimpleStore({
18426                 'id': 0,
18427                 fields: ['value', 'text'],
18428                 data : d
18429             });
18430             this.valueField = 'value';
18431             this.displayField = 'text';
18432         }
18433         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18434         if(!this.lazyRender){
18435             this.target = true;
18436             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18437             s.parentNode.removeChild(s); // remove it
18438             this.render(this.el.parentNode);
18439         }else{
18440             s.parentNode.removeChild(s); // remove it
18441         }
18442
18443     }
18444     if (this.store) {
18445         this.store = Roo.factory(this.store, Roo.data);
18446     }
18447     
18448     this.selectedIndex = -1;
18449     if(this.mode == 'local'){
18450         if(config.queryDelay === undefined){
18451             this.queryDelay = 10;
18452         }
18453         if(config.minChars === undefined){
18454             this.minChars = 0;
18455         }
18456     }
18457 };
18458
18459 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18460     /**
18461      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18462      */
18463     /**
18464      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18465      * rendering into an Roo.Editor, defaults to false)
18466      */
18467     /**
18468      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18469      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18470      */
18471     /**
18472      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18473      */
18474     /**
18475      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18476      * the dropdown list (defaults to undefined, with no header element)
18477      */
18478
18479      /**
18480      * @cfg {String/Roo.Template} tpl The template to use to render the output
18481      */
18482      
18483     // private
18484     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18485     /**
18486      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18487      */
18488     listWidth: undefined,
18489     /**
18490      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18491      * mode = 'remote' or 'text' if mode = 'local')
18492      */
18493     displayField: undefined,
18494     /**
18495      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18496      * mode = 'remote' or 'value' if mode = 'local'). 
18497      * Note: use of a valueField requires the user make a selection
18498      * in order for a value to be mapped.
18499      */
18500     valueField: undefined,
18501     
18502     
18503     /**
18504      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18505      * field's data value (defaults to the underlying DOM element's name)
18506      */
18507     hiddenName: undefined,
18508     /**
18509      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18510      */
18511     listClass: '',
18512     /**
18513      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18514      */
18515     selectedClass: 'x-combo-selected',
18516     /**
18517      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18518      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18519      * which displays a downward arrow icon).
18520      */
18521     triggerClass : 'x-form-arrow-trigger',
18522     /**
18523      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18524      */
18525     shadow:'sides',
18526     /**
18527      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18528      * anchor positions (defaults to 'tl-bl')
18529      */
18530     listAlign: 'tl-bl?',
18531     /**
18532      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18533      */
18534     maxHeight: 300,
18535     /**
18536      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18537      * query specified by the allQuery config option (defaults to 'query')
18538      */
18539     triggerAction: 'query',
18540     /**
18541      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18542      * (defaults to 4, does not apply if editable = false)
18543      */
18544     minChars : 4,
18545     /**
18546      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18547      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18548      */
18549     typeAhead: false,
18550     /**
18551      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18552      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18553      */
18554     queryDelay: 500,
18555     /**
18556      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18557      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18558      */
18559     pageSize: 0,
18560     /**
18561      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18562      * when editable = true (defaults to false)
18563      */
18564     selectOnFocus:false,
18565     /**
18566      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18567      */
18568     queryParam: 'query',
18569     /**
18570      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18571      * when mode = 'remote' (defaults to 'Loading...')
18572      */
18573     loadingText: 'Loading...',
18574     /**
18575      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18576      */
18577     resizable: false,
18578     /**
18579      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18580      */
18581     handleHeight : 8,
18582     /**
18583      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18584      * traditional select (defaults to true)
18585      */
18586     editable: true,
18587     /**
18588      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18589      */
18590     allQuery: '',
18591     /**
18592      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18593      */
18594     mode: 'remote',
18595     /**
18596      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18597      * listWidth has a higher value)
18598      */
18599     minListWidth : 70,
18600     /**
18601      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18602      * allow the user to set arbitrary text into the field (defaults to false)
18603      */
18604     forceSelection:false,
18605     /**
18606      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18607      * if typeAhead = true (defaults to 250)
18608      */
18609     typeAheadDelay : 250,
18610     /**
18611      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18612      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18613      */
18614     valueNotFoundText : undefined,
18615     /**
18616      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18617      */
18618     blockFocus : false,
18619     
18620     /**
18621      * @cfg {Boolean} disableClear Disable showing of clear button.
18622      */
18623     disableClear : false,
18624     /**
18625      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18626      */
18627     alwaysQuery : false,
18628     
18629     //private
18630     addicon : false,
18631     editicon: false,
18632     
18633     // element that contains real text value.. (when hidden is used..)
18634      
18635     // private
18636     onRender : function(ct, position)
18637     {
18638         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18639         
18640         if(this.hiddenName){
18641             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18642                     'before', true);
18643             this.hiddenField.value =
18644                 this.hiddenValue !== undefined ? this.hiddenValue :
18645                 this.value !== undefined ? this.value : '';
18646
18647             // prevent input submission
18648             this.el.dom.removeAttribute('name');
18649              
18650              
18651         }
18652         
18653         if(Roo.isGecko){
18654             this.el.dom.setAttribute('autocomplete', 'off');
18655         }
18656
18657         var cls = 'x-combo-list';
18658
18659         this.list = new Roo.Layer({
18660             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18661         });
18662
18663         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18664         this.list.setWidth(lw);
18665         this.list.swallowEvent('mousewheel');
18666         this.assetHeight = 0;
18667
18668         if(this.title){
18669             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18670             this.assetHeight += this.header.getHeight();
18671         }
18672
18673         this.innerList = this.list.createChild({cls:cls+'-inner'});
18674         this.innerList.on('mouseover', this.onViewOver, this);
18675         this.innerList.on('mousemove', this.onViewMove, this);
18676         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18677         
18678         if(this.allowBlank && !this.pageSize && !this.disableClear){
18679             this.footer = this.list.createChild({cls:cls+'-ft'});
18680             this.pageTb = new Roo.Toolbar(this.footer);
18681            
18682         }
18683         if(this.pageSize){
18684             this.footer = this.list.createChild({cls:cls+'-ft'});
18685             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18686                     {pageSize: this.pageSize});
18687             
18688         }
18689         
18690         if (this.pageTb && this.allowBlank && !this.disableClear) {
18691             var _this = this;
18692             this.pageTb.add(new Roo.Toolbar.Fill(), {
18693                 cls: 'x-btn-icon x-btn-clear',
18694                 text: '&#160;',
18695                 handler: function()
18696                 {
18697                     _this.collapse();
18698                     _this.clearValue();
18699                     _this.onSelect(false, -1);
18700                 }
18701             });
18702         }
18703         if (this.footer) {
18704             this.assetHeight += this.footer.getHeight();
18705         }
18706         
18707
18708         if(!this.tpl){
18709             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18710         }
18711
18712         this.view = new Roo.View(this.innerList, this.tpl, {
18713             singleSelect:true,
18714             store: this.store,
18715             selectedClass: this.selectedClass
18716         });
18717
18718         this.view.on('click', this.onViewClick, this);
18719
18720         this.store.on('beforeload', this.onBeforeLoad, this);
18721         this.store.on('load', this.onLoad, this);
18722         this.store.on('loadexception', this.onLoadException, this);
18723
18724         if(this.resizable){
18725             this.resizer = new Roo.Resizable(this.list,  {
18726                pinned:true, handles:'se'
18727             });
18728             this.resizer.on('resize', function(r, w, h){
18729                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18730                 this.listWidth = w;
18731                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18732                 this.restrictHeight();
18733             }, this);
18734             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18735         }
18736         if(!this.editable){
18737             this.editable = true;
18738             this.setEditable(false);
18739         }  
18740         
18741         
18742         if (typeof(this.events.add.listeners) != 'undefined') {
18743             
18744             this.addicon = this.wrap.createChild(
18745                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18746        
18747             this.addicon.on('click', function(e) {
18748                 this.fireEvent('add', this);
18749             }, this);
18750         }
18751         if (typeof(this.events.edit.listeners) != 'undefined') {
18752             
18753             this.editicon = this.wrap.createChild(
18754                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18755             if (this.addicon) {
18756                 this.editicon.setStyle('margin-left', '40px');
18757             }
18758             this.editicon.on('click', function(e) {
18759                 
18760                 // we fire even  if inothing is selected..
18761                 this.fireEvent('edit', this, this.lastData );
18762                 
18763             }, this);
18764         }
18765         
18766         
18767         
18768     },
18769
18770     // private
18771     initEvents : function(){
18772         Roo.form.ComboBox.superclass.initEvents.call(this);
18773
18774         this.keyNav = new Roo.KeyNav(this.el, {
18775             "up" : function(e){
18776                 this.inKeyMode = true;
18777                 this.selectPrev();
18778             },
18779
18780             "down" : function(e){
18781                 if(!this.isExpanded()){
18782                     this.onTriggerClick();
18783                 }else{
18784                     this.inKeyMode = true;
18785                     this.selectNext();
18786                 }
18787             },
18788
18789             "enter" : function(e){
18790                 this.onViewClick();
18791                 //return true;
18792             },
18793
18794             "esc" : function(e){
18795                 this.collapse();
18796             },
18797
18798             "tab" : function(e){
18799                 this.onViewClick(false);
18800                 this.fireEvent("specialkey", this, e);
18801                 return true;
18802             },
18803
18804             scope : this,
18805
18806             doRelay : function(foo, bar, hname){
18807                 if(hname == 'down' || this.scope.isExpanded()){
18808                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18809                 }
18810                 return true;
18811             },
18812
18813             forceKeyDown: true
18814         });
18815         this.queryDelay = Math.max(this.queryDelay || 10,
18816                 this.mode == 'local' ? 10 : 250);
18817         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18818         if(this.typeAhead){
18819             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18820         }
18821         if(this.editable !== false){
18822             this.el.on("keyup", this.onKeyUp, this);
18823         }
18824         if(this.forceSelection){
18825             this.on('blur', this.doForce, this);
18826         }
18827     },
18828
18829     onDestroy : function(){
18830         if(this.view){
18831             this.view.setStore(null);
18832             this.view.el.removeAllListeners();
18833             this.view.el.remove();
18834             this.view.purgeListeners();
18835         }
18836         if(this.list){
18837             this.list.destroy();
18838         }
18839         if(this.store){
18840             this.store.un('beforeload', this.onBeforeLoad, this);
18841             this.store.un('load', this.onLoad, this);
18842             this.store.un('loadexception', this.onLoadException, this);
18843         }
18844         Roo.form.ComboBox.superclass.onDestroy.call(this);
18845     },
18846
18847     // private
18848     fireKey : function(e){
18849         if(e.isNavKeyPress() && !this.list.isVisible()){
18850             this.fireEvent("specialkey", this, e);
18851         }
18852     },
18853
18854     // private
18855     onResize: function(w, h){
18856         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18857         
18858         if(typeof w != 'number'){
18859             // we do not handle it!?!?
18860             return;
18861         }
18862         var tw = this.trigger.getWidth();
18863         tw += this.addicon ? this.addicon.getWidth() : 0;
18864         tw += this.editicon ? this.editicon.getWidth() : 0;
18865         var x = w - tw;
18866         this.el.setWidth( this.adjustWidth('input', x));
18867             
18868         this.trigger.setStyle('left', x+'px');
18869         
18870         if(this.list && this.listWidth === undefined){
18871             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18872             this.list.setWidth(lw);
18873             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18874         }
18875         
18876     
18877         
18878     },
18879
18880     /**
18881      * Allow or prevent the user from directly editing the field text.  If false is passed,
18882      * the user will only be able to select from the items defined in the dropdown list.  This method
18883      * is the runtime equivalent of setting the 'editable' config option at config time.
18884      * @param {Boolean} value True to allow the user to directly edit the field text
18885      */
18886     setEditable : function(value){
18887         if(value == this.editable){
18888             return;
18889         }
18890         this.editable = value;
18891         if(!value){
18892             this.el.dom.setAttribute('readOnly', true);
18893             this.el.on('mousedown', this.onTriggerClick,  this);
18894             this.el.addClass('x-combo-noedit');
18895         }else{
18896             this.el.dom.setAttribute('readOnly', false);
18897             this.el.un('mousedown', this.onTriggerClick,  this);
18898             this.el.removeClass('x-combo-noedit');
18899         }
18900     },
18901
18902     // private
18903     onBeforeLoad : function(){
18904         if(!this.hasFocus){
18905             return;
18906         }
18907         this.innerList.update(this.loadingText ?
18908                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18909         this.restrictHeight();
18910         this.selectedIndex = -1;
18911     },
18912
18913     // private
18914     onLoad : function(){
18915         if(!this.hasFocus){
18916             return;
18917         }
18918         if(this.store.getCount() > 0){
18919             this.expand();
18920             this.restrictHeight();
18921             if(this.lastQuery == this.allQuery){
18922                 if(this.editable){
18923                     this.el.dom.select();
18924                 }
18925                 if(!this.selectByValue(this.value, true)){
18926                     this.select(0, true);
18927                 }
18928             }else{
18929                 this.selectNext();
18930                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18931                     this.taTask.delay(this.typeAheadDelay);
18932                 }
18933             }
18934         }else{
18935             this.onEmptyResults();
18936         }
18937         //this.el.focus();
18938     },
18939     // private
18940     onLoadException : function()
18941     {
18942         this.collapse();
18943         Roo.log(this.store.reader.jsonData);
18944         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18945             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18946         }
18947         
18948         
18949     },
18950     // private
18951     onTypeAhead : function(){
18952         if(this.store.getCount() > 0){
18953             var r = this.store.getAt(0);
18954             var newValue = r.data[this.displayField];
18955             var len = newValue.length;
18956             var selStart = this.getRawValue().length;
18957             if(selStart != len){
18958                 this.setRawValue(newValue);
18959                 this.selectText(selStart, newValue.length);
18960             }
18961         }
18962     },
18963
18964     // private
18965     onSelect : function(record, index){
18966         if(this.fireEvent('beforeselect', this, record, index) !== false){
18967             this.setFromData(index > -1 ? record.data : false);
18968             this.collapse();
18969             this.fireEvent('select', this, record, index);
18970         }
18971     },
18972
18973     /**
18974      * Returns the currently selected field value or empty string if no value is set.
18975      * @return {String} value The selected value
18976      */
18977     getValue : function(){
18978         if(this.valueField){
18979             return typeof this.value != 'undefined' ? this.value : '';
18980         }
18981         return Roo.form.ComboBox.superclass.getValue.call(this);
18982     },
18983
18984     /**
18985      * Clears any text/value currently set in the field
18986      */
18987     clearValue : function(){
18988         if(this.hiddenField){
18989             this.hiddenField.value = '';
18990         }
18991         this.value = '';
18992         this.setRawValue('');
18993         this.lastSelectionText = '';
18994         
18995     },
18996
18997     /**
18998      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18999      * will be displayed in the field.  If the value does not match the data value of an existing item,
19000      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19001      * Otherwise the field will be blank (although the value will still be set).
19002      * @param {String} value The value to match
19003      */
19004     setValue : function(v){
19005         var text = v;
19006         if(this.valueField){
19007             var r = this.findRecord(this.valueField, v);
19008             if(r){
19009                 text = r.data[this.displayField];
19010             }else if(this.valueNotFoundText !== undefined){
19011                 text = this.valueNotFoundText;
19012             }
19013         }
19014         this.lastSelectionText = text;
19015         if(this.hiddenField){
19016             this.hiddenField.value = v;
19017         }
19018         Roo.form.ComboBox.superclass.setValue.call(this, text);
19019         this.value = v;
19020     },
19021     /**
19022      * @property {Object} the last set data for the element
19023      */
19024     
19025     lastData : false,
19026     /**
19027      * Sets the value of the field based on a object which is related to the record format for the store.
19028      * @param {Object} value the value to set as. or false on reset?
19029      */
19030     setFromData : function(o){
19031         var dv = ''; // display value
19032         var vv = ''; // value value..
19033         this.lastData = o;
19034         if (this.displayField) {
19035             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19036         } else {
19037             // this is an error condition!!!
19038             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19039         }
19040         
19041         if(this.valueField){
19042             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19043         }
19044         if(this.hiddenField){
19045             this.hiddenField.value = vv;
19046             
19047             this.lastSelectionText = dv;
19048             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19049             this.value = vv;
19050             return;
19051         }
19052         // no hidden field.. - we store the value in 'value', but still display
19053         // display field!!!!
19054         this.lastSelectionText = dv;
19055         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19056         this.value = vv;
19057         
19058         
19059     },
19060     // private
19061     reset : function(){
19062         // overridden so that last data is reset..
19063         this.setValue(this.resetValue);
19064         this.originalValue = this.getValue();
19065         this.clearInvalid();
19066         this.lastData = false;
19067         if (this.view) {
19068             this.view.clearSelections();
19069         }
19070     },
19071     // private
19072     findRecord : function(prop, value){
19073         var record;
19074         if(this.store.getCount() > 0){
19075             this.store.each(function(r){
19076                 if(r.data[prop] == value){
19077                     record = r;
19078                     return false;
19079                 }
19080                 return true;
19081             });
19082         }
19083         return record;
19084     },
19085     
19086     getName: function()
19087     {
19088         // returns hidden if it's set..
19089         if (!this.rendered) {return ''};
19090         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19091         
19092     },
19093     // private
19094     onViewMove : function(e, t){
19095         this.inKeyMode = false;
19096     },
19097
19098     // private
19099     onViewOver : function(e, t){
19100         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19101             return;
19102         }
19103         var item = this.view.findItemFromChild(t);
19104         if(item){
19105             var index = this.view.indexOf(item);
19106             this.select(index, false);
19107         }
19108     },
19109
19110     // private
19111     onViewClick : function(doFocus)
19112     {
19113         var index = this.view.getSelectedIndexes()[0];
19114         var r = this.store.getAt(index);
19115         if(r){
19116             this.onSelect(r, index);
19117         }
19118         if(doFocus !== false && !this.blockFocus){
19119             this.el.focus();
19120         }
19121     },
19122
19123     // private
19124     restrictHeight : function(){
19125         this.innerList.dom.style.height = '';
19126         var inner = this.innerList.dom;
19127         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19128         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19129         this.list.beginUpdate();
19130         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19131         this.list.alignTo(this.el, this.listAlign);
19132         this.list.endUpdate();
19133     },
19134
19135     // private
19136     onEmptyResults : function(){
19137         this.collapse();
19138     },
19139
19140     /**
19141      * Returns true if the dropdown list is expanded, else false.
19142      */
19143     isExpanded : function(){
19144         return this.list.isVisible();
19145     },
19146
19147     /**
19148      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19149      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19150      * @param {String} value The data value of the item to select
19151      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19152      * selected item if it is not currently in view (defaults to true)
19153      * @return {Boolean} True if the value matched an item in the list, else false
19154      */
19155     selectByValue : function(v, scrollIntoView){
19156         if(v !== undefined && v !== null){
19157             var r = this.findRecord(this.valueField || this.displayField, v);
19158             if(r){
19159                 this.select(this.store.indexOf(r), scrollIntoView);
19160                 return true;
19161             }
19162         }
19163         return false;
19164     },
19165
19166     /**
19167      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19168      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19169      * @param {Number} index The zero-based index of the list item to select
19170      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19171      * selected item if it is not currently in view (defaults to true)
19172      */
19173     select : function(index, scrollIntoView){
19174         this.selectedIndex = index;
19175         this.view.select(index);
19176         if(scrollIntoView !== false){
19177             var el = this.view.getNode(index);
19178             if(el){
19179                 this.innerList.scrollChildIntoView(el, false);
19180             }
19181         }
19182     },
19183
19184     // private
19185     selectNext : function(){
19186         var ct = this.store.getCount();
19187         if(ct > 0){
19188             if(this.selectedIndex == -1){
19189                 this.select(0);
19190             }else if(this.selectedIndex < ct-1){
19191                 this.select(this.selectedIndex+1);
19192             }
19193         }
19194     },
19195
19196     // private
19197     selectPrev : function(){
19198         var ct = this.store.getCount();
19199         if(ct > 0){
19200             if(this.selectedIndex == -1){
19201                 this.select(0);
19202             }else if(this.selectedIndex != 0){
19203                 this.select(this.selectedIndex-1);
19204             }
19205         }
19206     },
19207
19208     // private
19209     onKeyUp : function(e){
19210         if(this.editable !== false && !e.isSpecialKey()){
19211             this.lastKey = e.getKey();
19212             this.dqTask.delay(this.queryDelay);
19213         }
19214     },
19215
19216     // private
19217     validateBlur : function(){
19218         return !this.list || !this.list.isVisible();   
19219     },
19220
19221     // private
19222     initQuery : function(){
19223         this.doQuery(this.getRawValue());
19224     },
19225
19226     // private
19227     doForce : function(){
19228         if(this.el.dom.value.length > 0){
19229             this.el.dom.value =
19230                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19231              
19232         }
19233     },
19234
19235     /**
19236      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19237      * query allowing the query action to be canceled if needed.
19238      * @param {String} query The SQL query to execute
19239      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19240      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19241      * saved in the current store (defaults to false)
19242      */
19243     doQuery : function(q, forceAll){
19244         if(q === undefined || q === null){
19245             q = '';
19246         }
19247         var qe = {
19248             query: q,
19249             forceAll: forceAll,
19250             combo: this,
19251             cancel:false
19252         };
19253         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19254             return false;
19255         }
19256         q = qe.query;
19257         forceAll = qe.forceAll;
19258         if(forceAll === true || (q.length >= this.minChars)){
19259             if(this.lastQuery != q || this.alwaysQuery){
19260                 this.lastQuery = q;
19261                 if(this.mode == 'local'){
19262                     this.selectedIndex = -1;
19263                     if(forceAll){
19264                         this.store.clearFilter();
19265                     }else{
19266                         this.store.filter(this.displayField, q);
19267                     }
19268                     this.onLoad();
19269                 }else{
19270                     this.store.baseParams[this.queryParam] = q;
19271                     this.store.load({
19272                         params: this.getParams(q)
19273                     });
19274                     this.expand();
19275                 }
19276             }else{
19277                 this.selectedIndex = -1;
19278                 this.onLoad();   
19279             }
19280         }
19281     },
19282
19283     // private
19284     getParams : function(q){
19285         var p = {};
19286         //p[this.queryParam] = q;
19287         if(this.pageSize){
19288             p.start = 0;
19289             p.limit = this.pageSize;
19290         }
19291         return p;
19292     },
19293
19294     /**
19295      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19296      */
19297     collapse : function(){
19298         if(!this.isExpanded()){
19299             return;
19300         }
19301         this.list.hide();
19302         Roo.get(document).un('mousedown', this.collapseIf, this);
19303         Roo.get(document).un('mousewheel', this.collapseIf, this);
19304         if (!this.editable) {
19305             Roo.get(document).un('keydown', this.listKeyPress, this);
19306         }
19307         this.fireEvent('collapse', this);
19308     },
19309
19310     // private
19311     collapseIf : function(e){
19312         if(!e.within(this.wrap) && !e.within(this.list)){
19313             this.collapse();
19314         }
19315     },
19316
19317     /**
19318      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19319      */
19320     expand : function(){
19321         if(this.isExpanded() || !this.hasFocus){
19322             return;
19323         }
19324         this.list.alignTo(this.el, this.listAlign);
19325         this.list.show();
19326         Roo.get(document).on('mousedown', this.collapseIf, this);
19327         Roo.get(document).on('mousewheel', this.collapseIf, this);
19328         if (!this.editable) {
19329             Roo.get(document).on('keydown', this.listKeyPress, this);
19330         }
19331         
19332         this.fireEvent('expand', this);
19333     },
19334
19335     // private
19336     // Implements the default empty TriggerField.onTriggerClick function
19337     onTriggerClick : function(){
19338         if(this.disabled){
19339             return;
19340         }
19341         if(this.isExpanded()){
19342             this.collapse();
19343             if (!this.blockFocus) {
19344                 this.el.focus();
19345             }
19346             
19347         }else {
19348             this.hasFocus = true;
19349             if(this.triggerAction == 'all') {
19350                 this.doQuery(this.allQuery, true);
19351             } else {
19352                 this.doQuery(this.getRawValue());
19353             }
19354             if (!this.blockFocus) {
19355                 this.el.focus();
19356             }
19357         }
19358     },
19359     listKeyPress : function(e)
19360     {
19361         //Roo.log('listkeypress');
19362         // scroll to first matching element based on key pres..
19363         if (e.isSpecialKey()) {
19364             return false;
19365         }
19366         var k = String.fromCharCode(e.getKey()).toUpperCase();
19367         //Roo.log(k);
19368         var match  = false;
19369         var csel = this.view.getSelectedNodes();
19370         var cselitem = false;
19371         if (csel.length) {
19372             var ix = this.view.indexOf(csel[0]);
19373             cselitem  = this.store.getAt(ix);
19374             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19375                 cselitem = false;
19376             }
19377             
19378         }
19379         
19380         this.store.each(function(v) { 
19381             if (cselitem) {
19382                 // start at existing selection.
19383                 if (cselitem.id == v.id) {
19384                     cselitem = false;
19385                 }
19386                 return;
19387             }
19388                 
19389             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19390                 match = this.store.indexOf(v);
19391                 return false;
19392             }
19393         }, this);
19394         
19395         if (match === false) {
19396             return true; // no more action?
19397         }
19398         // scroll to?
19399         this.view.select(match);
19400         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19401         sn.scrollIntoView(sn.dom.parentNode, false);
19402     } 
19403
19404     /** 
19405     * @cfg {Boolean} grow 
19406     * @hide 
19407     */
19408     /** 
19409     * @cfg {Number} growMin 
19410     * @hide 
19411     */
19412     /** 
19413     * @cfg {Number} growMax 
19414     * @hide 
19415     */
19416     /**
19417      * @hide
19418      * @method autoSize
19419      */
19420 });/*
19421  * Copyright(c) 2010-2012, Roo J Solutions Limited
19422  *
19423  * Licence LGPL
19424  *
19425  */
19426
19427 /**
19428  * @class Roo.form.ComboBoxArray
19429  * @extends Roo.form.TextField
19430  * A facebook style adder... for lists of email / people / countries  etc...
19431  * pick multiple items from a combo box, and shows each one.
19432  *
19433  *  Fred [x]  Brian [x]  [Pick another |v]
19434  *
19435  *
19436  *  For this to work: it needs various extra information
19437  *    - normal combo problay has
19438  *      name, hiddenName
19439  *    + displayField, valueField
19440  *
19441  *    For our purpose...
19442  *
19443  *
19444  *   If we change from 'extends' to wrapping...
19445  *   
19446  *  
19447  *
19448  
19449  
19450  * @constructor
19451  * Create a new ComboBoxArray.
19452  * @param {Object} config Configuration options
19453  */
19454  
19455
19456 Roo.form.ComboBoxArray = function(config)
19457 {
19458     this.addEvents({
19459         /**
19460          * @event beforeremove
19461          * Fires before remove the value from the list
19462              * @param {Roo.form.ComboBoxArray} _self This combo box array
19463              * @param {Roo.form.ComboBoxArray.Item} item removed item
19464              */
19465         'beforeremove' : true,
19466         /**
19467          * @event remove
19468          * Fires when remove the value from the list
19469              * @param {Roo.form.ComboBoxArray} _self This combo box array
19470              * @param {Roo.form.ComboBoxArray.Item} item removed item
19471              */
19472         'remove' : true
19473         
19474         
19475     });
19476     
19477     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19478     
19479     this.items = new Roo.util.MixedCollection(false);
19480     
19481     // construct the child combo...
19482     
19483     
19484     
19485     
19486    
19487     
19488 }
19489
19490  
19491 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19492
19493     /**
19494      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19495      */
19496     
19497     lastData : false,
19498     
19499     // behavies liek a hiddne field
19500     inputType:      'hidden',
19501     /**
19502      * @cfg {Number} width The width of the box that displays the selected element
19503      */ 
19504     width:          300,
19505
19506     
19507     
19508     /**
19509      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19510      */
19511     name : false,
19512     /**
19513      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19514      */
19515     hiddenName : false,
19516       /**
19517      * @cfg {String} seperator    The value seperator normally ',' 
19518      */
19519     seperator : ',',
19520     
19521     // private the array of items that are displayed..
19522     items  : false,
19523     // private - the hidden field el.
19524     hiddenEl : false,
19525     // private - the filed el..
19526     el : false,
19527     
19528     //validateValue : function() { return true; }, // all values are ok!
19529     //onAddClick: function() { },
19530     
19531     onRender : function(ct, position) 
19532     {
19533         
19534         // create the standard hidden element
19535         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19536         
19537         
19538         // give fake names to child combo;
19539         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19540         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19541         
19542         this.combo = Roo.factory(this.combo, Roo.form);
19543         this.combo.onRender(ct, position);
19544         if (typeof(this.combo.width) != 'undefined') {
19545             this.combo.onResize(this.combo.width,0);
19546         }
19547         
19548         this.combo.initEvents();
19549         
19550         // assigned so form know we need to do this..
19551         this.store          = this.combo.store;
19552         this.valueField     = this.combo.valueField;
19553         this.displayField   = this.combo.displayField ;
19554         
19555         
19556         this.combo.wrap.addClass('x-cbarray-grp');
19557         
19558         var cbwrap = this.combo.wrap.createChild(
19559             {tag: 'div', cls: 'x-cbarray-cb'},
19560             this.combo.el.dom
19561         );
19562         
19563              
19564         this.hiddenEl = this.combo.wrap.createChild({
19565             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19566         });
19567         this.el = this.combo.wrap.createChild({
19568             tag: 'input',  type:'hidden' , name: this.name, value : ''
19569         });
19570          //   this.el.dom.removeAttribute("name");
19571         
19572         
19573         this.outerWrap = this.combo.wrap;
19574         this.wrap = cbwrap;
19575         
19576         this.outerWrap.setWidth(this.width);
19577         this.outerWrap.dom.removeChild(this.el.dom);
19578         
19579         this.wrap.dom.appendChild(this.el.dom);
19580         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19581         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19582         
19583         this.combo.trigger.setStyle('position','relative');
19584         this.combo.trigger.setStyle('left', '0px');
19585         this.combo.trigger.setStyle('top', '2px');
19586         
19587         this.combo.el.setStyle('vertical-align', 'text-bottom');
19588         
19589         //this.trigger.setStyle('vertical-align', 'top');
19590         
19591         // this should use the code from combo really... on('add' ....)
19592         if (this.adder) {
19593             
19594         
19595             this.adder = this.outerWrap.createChild(
19596                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19597             var _t = this;
19598             this.adder.on('click', function(e) {
19599                 _t.fireEvent('adderclick', this, e);
19600             }, _t);
19601         }
19602         //var _t = this;
19603         //this.adder.on('click', this.onAddClick, _t);
19604         
19605         
19606         this.combo.on('select', function(cb, rec, ix) {
19607             this.addItem(rec.data);
19608             
19609             cb.setValue('');
19610             cb.el.dom.value = '';
19611             //cb.lastData = rec.data;
19612             // add to list
19613             
19614         }, this);
19615         
19616         
19617     },
19618     
19619     
19620     getName: function()
19621     {
19622         // returns hidden if it's set..
19623         if (!this.rendered) {return ''};
19624         return  this.hiddenName ? this.hiddenName : this.name;
19625         
19626     },
19627     
19628     
19629     onResize: function(w, h){
19630         
19631         return;
19632         // not sure if this is needed..
19633         //this.combo.onResize(w,h);
19634         
19635         if(typeof w != 'number'){
19636             // we do not handle it!?!?
19637             return;
19638         }
19639         var tw = this.combo.trigger.getWidth();
19640         tw += this.addicon ? this.addicon.getWidth() : 0;
19641         tw += this.editicon ? this.editicon.getWidth() : 0;
19642         var x = w - tw;
19643         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19644             
19645         this.combo.trigger.setStyle('left', '0px');
19646         
19647         if(this.list && this.listWidth === undefined){
19648             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19649             this.list.setWidth(lw);
19650             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19651         }
19652         
19653     
19654         
19655     },
19656     
19657     addItem: function(rec)
19658     {
19659         var valueField = this.combo.valueField;
19660         var displayField = this.combo.displayField;
19661         
19662         if (this.items.indexOfKey(rec[valueField]) > -1) {
19663             //console.log("GOT " + rec.data.id);
19664             return;
19665         }
19666         
19667         var x = new Roo.form.ComboBoxArray.Item({
19668             //id : rec[this.idField],
19669             data : rec,
19670             displayField : displayField ,
19671             tipField : displayField ,
19672             cb : this
19673         });
19674         // use the 
19675         this.items.add(rec[valueField],x);
19676         // add it before the element..
19677         this.updateHiddenEl();
19678         x.render(this.outerWrap, this.wrap.dom);
19679         // add the image handler..
19680     },
19681     
19682     updateHiddenEl : function()
19683     {
19684         this.validate();
19685         if (!this.hiddenEl) {
19686             return;
19687         }
19688         var ar = [];
19689         var idField = this.combo.valueField;
19690         
19691         this.items.each(function(f) {
19692             ar.push(f.data[idField]);
19693         });
19694         this.hiddenEl.dom.value = ar.join(this.seperator);
19695         this.validate();
19696     },
19697     
19698     reset : function()
19699     {
19700         this.items.clear();
19701         
19702         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19703            el.remove();
19704         });
19705         
19706         this.el.dom.value = '';
19707         if (this.hiddenEl) {
19708             this.hiddenEl.dom.value = '';
19709         }
19710         
19711     },
19712     getValue: function()
19713     {
19714         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19715     },
19716     setValue: function(v) // not a valid action - must use addItems..
19717     {
19718         
19719         this.reset();
19720          
19721         if (this.store.isLocal && (typeof(v) == 'string')) {
19722             // then we can use the store to find the values..
19723             // comma seperated at present.. this needs to allow JSON based encoding..
19724             this.hiddenEl.value  = v;
19725             var v_ar = [];
19726             Roo.each(v.split(this.seperator), function(k) {
19727                 Roo.log("CHECK " + this.valueField + ',' + k);
19728                 var li = this.store.query(this.valueField, k);
19729                 if (!li.length) {
19730                     return;
19731                 }
19732                 var add = {};
19733                 add[this.valueField] = k;
19734                 add[this.displayField] = li.item(0).data[this.displayField];
19735                 
19736                 this.addItem(add);
19737             }, this) 
19738              
19739         }
19740         if (typeof(v) == 'object' ) {
19741             // then let's assume it's an array of objects..
19742             Roo.each(v, function(l) {
19743                 var add = l;
19744                 if (typeof(l) == 'string') {
19745                     add = {};
19746                     add[this.valueField] = l;
19747                     add[this.displayField] = l
19748                 }
19749                 this.addItem(add);
19750             }, this);
19751              
19752         }
19753         
19754         
19755     },
19756     setFromData: function(v)
19757     {
19758         // this recieves an object, if setValues is called.
19759         this.reset();
19760         this.el.dom.value = v[this.displayField];
19761         this.hiddenEl.dom.value = v[this.valueField];
19762         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19763             return;
19764         }
19765         var kv = v[this.valueField];
19766         var dv = v[this.displayField];
19767         kv = typeof(kv) != 'string' ? '' : kv;
19768         dv = typeof(dv) != 'string' ? '' : dv;
19769         
19770         
19771         var keys = kv.split(this.seperator);
19772         var display = dv.split(this.seperator);
19773         for (var i = 0 ; i < keys.length; i++) {
19774             add = {};
19775             add[this.valueField] = keys[i];
19776             add[this.displayField] = display[i];
19777             this.addItem(add);
19778         }
19779       
19780         
19781     },
19782     
19783     /**
19784      * Validates the combox array value
19785      * @return {Boolean} True if the value is valid, else false
19786      */
19787     validate : function(){
19788         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19789             this.clearInvalid();
19790             return true;
19791         }
19792         return false;
19793     },
19794     
19795     validateValue : function(value){
19796         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19797         
19798     },
19799     
19800     /*@
19801      * overide
19802      * 
19803      */
19804     isDirty : function() {
19805         if(this.disabled) {
19806             return false;
19807         }
19808         
19809         try {
19810             var d = Roo.decode(String(this.originalValue));
19811         } catch (e) {
19812             return String(this.getValue()) !== String(this.originalValue);
19813         }
19814         
19815         var originalValue = [];
19816         
19817         for (var i = 0; i < d.length; i++){
19818             originalValue.push(d[i][this.valueField]);
19819         }
19820         
19821         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19822         
19823     }
19824     
19825 });
19826
19827
19828
19829 /**
19830  * @class Roo.form.ComboBoxArray.Item
19831  * @extends Roo.BoxComponent
19832  * A selected item in the list
19833  *  Fred [x]  Brian [x]  [Pick another |v]
19834  * 
19835  * @constructor
19836  * Create a new item.
19837  * @param {Object} config Configuration options
19838  */
19839  
19840 Roo.form.ComboBoxArray.Item = function(config) {
19841     config.id = Roo.id();
19842     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19843 }
19844
19845 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19846     data : {},
19847     cb: false,
19848     displayField : false,
19849     tipField : false,
19850     
19851     
19852     defaultAutoCreate : {
19853         tag: 'div',
19854         cls: 'x-cbarray-item',
19855         cn : [ 
19856             { tag: 'div' },
19857             {
19858                 tag: 'img',
19859                 width:16,
19860                 height : 16,
19861                 src : Roo.BLANK_IMAGE_URL ,
19862                 align: 'center'
19863             }
19864         ]
19865         
19866     },
19867     
19868  
19869     onRender : function(ct, position)
19870     {
19871         Roo.form.Field.superclass.onRender.call(this, ct, position);
19872         
19873         if(!this.el){
19874             var cfg = this.getAutoCreate();
19875             this.el = ct.createChild(cfg, position);
19876         }
19877         
19878         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19879         
19880         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19881             this.cb.renderer(this.data) :
19882             String.format('{0}',this.data[this.displayField]);
19883         
19884             
19885         this.el.child('div').dom.setAttribute('qtip',
19886                         String.format('{0}',this.data[this.tipField])
19887         );
19888         
19889         this.el.child('img').on('click', this.remove, this);
19890         
19891     },
19892    
19893     remove : function()
19894     {
19895         if(this.cb.disabled){
19896             return;
19897         }
19898         
19899         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19900             this.cb.items.remove(this);
19901             this.el.child('img').un('click', this.remove, this);
19902             this.el.remove();
19903             this.cb.updateHiddenEl();
19904
19905             this.cb.fireEvent('remove', this.cb, this);
19906         }
19907         
19908     }
19909 });/*
19910  * RooJS Library 1.1.1
19911  * Copyright(c) 2008-2011  Alan Knowles
19912  *
19913  * License - LGPL
19914  */
19915  
19916
19917 /**
19918  * @class Roo.form.ComboNested
19919  * @extends Roo.form.ComboBox
19920  * A combobox for that allows selection of nested items in a list,
19921  * eg.
19922  *
19923  *  Book
19924  *    -> red
19925  *    -> green
19926  *  Table
19927  *    -> square
19928  *      ->red
19929  *      ->green
19930  *    -> rectangle
19931  *      ->green
19932  *      
19933  * 
19934  * @constructor
19935  * Create a new ComboNested
19936  * @param {Object} config Configuration options
19937  */
19938 Roo.form.ComboNested = function(config){
19939     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19940     // should verify some data...
19941     // like
19942     // hiddenName = required..
19943     // displayField = required
19944     // valudField == required
19945     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19946     var _t = this;
19947     Roo.each(req, function(e) {
19948         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19949             throw "Roo.form.ComboNested : missing value for: " + e;
19950         }
19951     });
19952      
19953     
19954 };
19955
19956 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19957    
19958     /*
19959      * @config {Number} max Number of columns to show
19960      */
19961     
19962     maxColumns : 3,
19963    
19964     list : null, // the outermost div..
19965     innerLists : null, // the
19966     views : null,
19967     stores : null,
19968     // private
19969     loadingChildren : false,
19970     
19971     onRender : function(ct, position)
19972     {
19973         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19974         
19975         if(this.hiddenName){
19976             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19977                     'before', true);
19978             this.hiddenField.value =
19979                 this.hiddenValue !== undefined ? this.hiddenValue :
19980                 this.value !== undefined ? this.value : '';
19981
19982             // prevent input submission
19983             this.el.dom.removeAttribute('name');
19984              
19985              
19986         }
19987         
19988         if(Roo.isGecko){
19989             this.el.dom.setAttribute('autocomplete', 'off');
19990         }
19991
19992         var cls = 'x-combo-list';
19993
19994         this.list = new Roo.Layer({
19995             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19996         });
19997
19998         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19999         this.list.setWidth(lw);
20000         this.list.swallowEvent('mousewheel');
20001         this.assetHeight = 0;
20002
20003         if(this.title){
20004             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20005             this.assetHeight += this.header.getHeight();
20006         }
20007         this.innerLists = [];
20008         this.views = [];
20009         this.stores = [];
20010         for (var i =0 ; i < this.maxColumns; i++) {
20011             this.onRenderList( cls, i);
20012         }
20013         
20014         // always needs footer, as we are going to have an 'OK' button.
20015         this.footer = this.list.createChild({cls:cls+'-ft'});
20016         this.pageTb = new Roo.Toolbar(this.footer);  
20017         var _this = this;
20018         this.pageTb.add(  {
20019             
20020             text: 'Done',
20021             handler: function()
20022             {
20023                 _this.collapse();
20024             }
20025         });
20026         
20027         if ( this.allowBlank && !this.disableClear) {
20028             
20029             this.pageTb.add(new Roo.Toolbar.Fill(), {
20030                 cls: 'x-btn-icon x-btn-clear',
20031                 text: '&#160;',
20032                 handler: function()
20033                 {
20034                     _this.collapse();
20035                     _this.clearValue();
20036                     _this.onSelect(false, -1);
20037                 }
20038             });
20039         }
20040         if (this.footer) {
20041             this.assetHeight += this.footer.getHeight();
20042         }
20043         
20044     },
20045     onRenderList : function (  cls, i)
20046     {
20047         
20048         var lw = Math.floor(
20049                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20050         );
20051         
20052         this.list.setWidth(lw); // default to '1'
20053
20054         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20055         //il.on('mouseover', this.onViewOver, this, { list:  i });
20056         //il.on('mousemove', this.onViewMove, this, { list:  i });
20057         il.setWidth(lw);
20058         il.setStyle({ 'overflow-x' : 'hidden'});
20059
20060         if(!this.tpl){
20061             this.tpl = new Roo.Template({
20062                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20063                 isEmpty: function (value, allValues) {
20064                     //Roo.log(value);
20065                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20066                     return dl ? 'has-children' : 'no-children'
20067                 }
20068             });
20069         }
20070         
20071         var store  = this.store;
20072         if (i > 0) {
20073             store  = new Roo.data.SimpleStore({
20074                 //fields : this.store.reader.meta.fields,
20075                 reader : this.store.reader,
20076                 data : [ ]
20077             });
20078         }
20079         this.stores[i]  = store;
20080                   
20081         var view = this.views[i] = new Roo.View(
20082             il,
20083             this.tpl,
20084             {
20085                 singleSelect:true,
20086                 store: store,
20087                 selectedClass: this.selectedClass
20088             }
20089         );
20090         view.getEl().setWidth(lw);
20091         view.getEl().setStyle({
20092             position: i < 1 ? 'relative' : 'absolute',
20093             top: 0,
20094             left: (i * lw ) + 'px',
20095             display : i > 0 ? 'none' : 'block'
20096         });
20097         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20098         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20099         //view.on('click', this.onViewClick, this, { list : i });
20100
20101         store.on('beforeload', this.onBeforeLoad, this);
20102         store.on('load',  this.onLoad, this, { list  : i});
20103         store.on('loadexception', this.onLoadException, this);
20104
20105         // hide the other vies..
20106         
20107         
20108         
20109     },
20110       
20111     restrictHeight : function()
20112     {
20113         var mh = 0;
20114         Roo.each(this.innerLists, function(il,i) {
20115             var el = this.views[i].getEl();
20116             el.dom.style.height = '';
20117             var inner = el.dom;
20118             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20119             // only adjust heights on other ones..
20120             mh = Math.max(h, mh);
20121             if (i < 1) {
20122                 
20123                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20124                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20125                
20126             }
20127             
20128             
20129         }, this);
20130         
20131         this.list.beginUpdate();
20132         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20133         this.list.alignTo(this.el, this.listAlign);
20134         this.list.endUpdate();
20135         
20136     },
20137      
20138     
20139     // -- store handlers..
20140     // private
20141     onBeforeLoad : function()
20142     {
20143         if(!this.hasFocus){
20144             return;
20145         }
20146         this.innerLists[0].update(this.loadingText ?
20147                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20148         this.restrictHeight();
20149         this.selectedIndex = -1;
20150     },
20151     // private
20152     onLoad : function(a,b,c,d)
20153     {
20154         if (!this.loadingChildren) {
20155             // then we are loading the top level. - hide the children
20156             for (var i = 1;i < this.views.length; i++) {
20157                 this.views[i].getEl().setStyle({ display : 'none' });
20158             }
20159             var lw = Math.floor(
20160                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20161             );
20162         
20163              this.list.setWidth(lw); // default to '1'
20164
20165             
20166         }
20167         if(!this.hasFocus){
20168             return;
20169         }
20170         
20171         if(this.store.getCount() > 0) {
20172             this.expand();
20173             this.restrictHeight();   
20174         } else {
20175             this.onEmptyResults();
20176         }
20177         
20178         if (!this.loadingChildren) {
20179             this.selectActive();
20180         }
20181         /*
20182         this.stores[1].loadData([]);
20183         this.stores[2].loadData([]);
20184         this.views
20185         */    
20186     
20187         //this.el.focus();
20188     },
20189     
20190     
20191     // private
20192     onLoadException : function()
20193     {
20194         this.collapse();
20195         Roo.log(this.store.reader.jsonData);
20196         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20197             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20198         }
20199         
20200         
20201     },
20202     // no cleaning of leading spaces on blur here.
20203     cleanLeadingSpace : function(e) { },
20204     
20205
20206     onSelectChange : function (view, sels, opts )
20207     {
20208         var ix = view.getSelectedIndexes();
20209          
20210         if (opts.list > this.maxColumns - 2) {
20211             if (view.store.getCount()<  1) {
20212                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20213
20214             } else  {
20215                 if (ix.length) {
20216                     // used to clear ?? but if we are loading unselected 
20217                     this.setFromData(view.store.getAt(ix[0]).data);
20218                 }
20219                 
20220             }
20221             
20222             return;
20223         }
20224         
20225         if (!ix.length) {
20226             // this get's fired when trigger opens..
20227            // this.setFromData({});
20228             var str = this.stores[opts.list+1];
20229             str.data.clear(); // removeall wihtout the fire events..
20230             return;
20231         }
20232         
20233         var rec = view.store.getAt(ix[0]);
20234          
20235         this.setFromData(rec.data);
20236         this.fireEvent('select', this, rec, ix[0]);
20237         
20238         var lw = Math.floor(
20239              (
20240                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20241              ) / this.maxColumns
20242         );
20243         this.loadingChildren = true;
20244         this.stores[opts.list+1].loadDataFromChildren( rec );
20245         this.loadingChildren = false;
20246         var dl = this.stores[opts.list+1]. getTotalCount();
20247         
20248         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20249         
20250         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20251         for (var i = opts.list+2; i < this.views.length;i++) {
20252             this.views[i].getEl().setStyle({ display : 'none' });
20253         }
20254         
20255         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20256         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20257         
20258         if (this.isLoading) {
20259            // this.selectActive(opts.list);
20260         }
20261          
20262     },
20263     
20264     
20265     
20266     
20267     onDoubleClick : function()
20268     {
20269         this.collapse(); //??
20270     },
20271     
20272      
20273     
20274     
20275     
20276     // private
20277     recordToStack : function(store, prop, value, stack)
20278     {
20279         var cstore = new Roo.data.SimpleStore({
20280             //fields : this.store.reader.meta.fields, // we need array reader.. for
20281             reader : this.store.reader,
20282             data : [ ]
20283         });
20284         var _this = this;
20285         var record  = false;
20286         var srec = false;
20287         if(store.getCount() < 1){
20288             return false;
20289         }
20290         store.each(function(r){
20291             if(r.data[prop] == value){
20292                 record = r;
20293             srec = r;
20294                 return false;
20295             }
20296             if (r.data.cn && r.data.cn.length) {
20297                 cstore.loadDataFromChildren( r);
20298                 var cret = _this.recordToStack(cstore, prop, value, stack);
20299                 if (cret !== false) {
20300                     record = cret;
20301                     srec = r;
20302                     return false;
20303                 }
20304             }
20305              
20306             return true;
20307         });
20308         if (record == false) {
20309             return false
20310         }
20311         stack.unshift(srec);
20312         return record;
20313     },
20314     
20315     /*
20316      * find the stack of stores that match our value.
20317      *
20318      * 
20319      */
20320     
20321     selectActive : function ()
20322     {
20323         // if store is not loaded, then we will need to wait for that to happen first.
20324         var stack = [];
20325         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20326         for (var i = 0; i < stack.length; i++ ) {
20327             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20328         }
20329         
20330     }
20331         
20332          
20333     
20334     
20335     
20336     
20337 });/*
20338  * Based on:
20339  * Ext JS Library 1.1.1
20340  * Copyright(c) 2006-2007, Ext JS, LLC.
20341  *
20342  * Originally Released Under LGPL - original licence link has changed is not relivant.
20343  *
20344  * Fork - LGPL
20345  * <script type="text/javascript">
20346  */
20347 /**
20348  * @class Roo.form.Checkbox
20349  * @extends Roo.form.Field
20350  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20351  * @constructor
20352  * Creates a new Checkbox
20353  * @param {Object} config Configuration options
20354  */
20355 Roo.form.Checkbox = function(config){
20356     Roo.form.Checkbox.superclass.constructor.call(this, config);
20357     this.addEvents({
20358         /**
20359          * @event check
20360          * Fires when the checkbox is checked or unchecked.
20361              * @param {Roo.form.Checkbox} this This checkbox
20362              * @param {Boolean} checked The new checked value
20363              */
20364         check : true
20365     });
20366 };
20367
20368 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20369     /**
20370      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20371      */
20372     focusClass : undefined,
20373     /**
20374      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20375      */
20376     fieldClass: "x-form-field",
20377     /**
20378      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20379      */
20380     checked: false,
20381     /**
20382      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20383      * {tag: "input", type: "checkbox", autocomplete: "off"})
20384      */
20385     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20386     /**
20387      * @cfg {String} boxLabel The text that appears beside the checkbox
20388      */
20389     boxLabel : "",
20390     /**
20391      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20392      */  
20393     inputValue : '1',
20394     /**
20395      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20396      */
20397      valueOff: '0', // value when not checked..
20398
20399     actionMode : 'viewEl', 
20400     //
20401     // private
20402     itemCls : 'x-menu-check-item x-form-item',
20403     groupClass : 'x-menu-group-item',
20404     inputType : 'hidden',
20405     
20406     
20407     inSetChecked: false, // check that we are not calling self...
20408     
20409     inputElement: false, // real input element?
20410     basedOn: false, // ????
20411     
20412     isFormField: true, // not sure where this is needed!!!!
20413
20414     onResize : function(){
20415         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20416         if(!this.boxLabel){
20417             this.el.alignTo(this.wrap, 'c-c');
20418         }
20419     },
20420
20421     initEvents : function(){
20422         Roo.form.Checkbox.superclass.initEvents.call(this);
20423         this.el.on("click", this.onClick,  this);
20424         this.el.on("change", this.onClick,  this);
20425     },
20426
20427
20428     getResizeEl : function(){
20429         return this.wrap;
20430     },
20431
20432     getPositionEl : function(){
20433         return this.wrap;
20434     },
20435
20436     // private
20437     onRender : function(ct, position){
20438         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20439         /*
20440         if(this.inputValue !== undefined){
20441             this.el.dom.value = this.inputValue;
20442         }
20443         */
20444         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20445         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20446         var viewEl = this.wrap.createChild({ 
20447             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20448         this.viewEl = viewEl;   
20449         this.wrap.on('click', this.onClick,  this); 
20450         
20451         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20452         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20453         
20454         
20455         
20456         if(this.boxLabel){
20457             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20458         //    viewEl.on('click', this.onClick,  this); 
20459         }
20460         //if(this.checked){
20461             this.setChecked(this.checked);
20462         //}else{
20463             //this.checked = this.el.dom;
20464         //}
20465
20466     },
20467
20468     // private
20469     initValue : Roo.emptyFn,
20470
20471     /**
20472      * Returns the checked state of the checkbox.
20473      * @return {Boolean} True if checked, else false
20474      */
20475     getValue : function(){
20476         if(this.el){
20477             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20478         }
20479         return this.valueOff;
20480         
20481     },
20482
20483         // private
20484     onClick : function(){ 
20485         if (this.disabled) {
20486             return;
20487         }
20488         this.setChecked(!this.checked);
20489
20490         //if(this.el.dom.checked != this.checked){
20491         //    this.setValue(this.el.dom.checked);
20492        // }
20493     },
20494
20495     /**
20496      * Sets the checked state of the checkbox.
20497      * On is always based on a string comparison between inputValue and the param.
20498      * @param {Boolean/String} value - the value to set 
20499      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20500      */
20501     setValue : function(v,suppressEvent){
20502         
20503         
20504         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20505         //if(this.el && this.el.dom){
20506         //    this.el.dom.checked = this.checked;
20507         //    this.el.dom.defaultChecked = this.checked;
20508         //}
20509         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20510         //this.fireEvent("check", this, this.checked);
20511     },
20512     // private..
20513     setChecked : function(state,suppressEvent)
20514     {
20515         if (this.inSetChecked) {
20516             this.checked = state;
20517             return;
20518         }
20519         
20520     
20521         if(this.wrap){
20522             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20523         }
20524         this.checked = state;
20525         if(suppressEvent !== true){
20526             this.fireEvent('check', this, state);
20527         }
20528         this.inSetChecked = true;
20529         this.el.dom.value = state ? this.inputValue : this.valueOff;
20530         this.inSetChecked = false;
20531         
20532     },
20533     // handle setting of hidden value by some other method!!?!?
20534     setFromHidden: function()
20535     {
20536         if(!this.el){
20537             return;
20538         }
20539         //console.log("SET FROM HIDDEN");
20540         //alert('setFrom hidden');
20541         this.setValue(this.el.dom.value);
20542     },
20543     
20544     onDestroy : function()
20545     {
20546         if(this.viewEl){
20547             Roo.get(this.viewEl).remove();
20548         }
20549          
20550         Roo.form.Checkbox.superclass.onDestroy.call(this);
20551     },
20552     
20553     setBoxLabel : function(str)
20554     {
20555         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20556     }
20557
20558 });/*
20559  * Based on:
20560  * Ext JS Library 1.1.1
20561  * Copyright(c) 2006-2007, Ext JS, LLC.
20562  *
20563  * Originally Released Under LGPL - original licence link has changed is not relivant.
20564  *
20565  * Fork - LGPL
20566  * <script type="text/javascript">
20567  */
20568  
20569 /**
20570  * @class Roo.form.Radio
20571  * @extends Roo.form.Checkbox
20572  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20573  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20574  * @constructor
20575  * Creates a new Radio
20576  * @param {Object} config Configuration options
20577  */
20578 Roo.form.Radio = function(){
20579     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20580 };
20581 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20582     inputType: 'radio',
20583
20584     /**
20585      * If this radio is part of a group, it will return the selected value
20586      * @return {String}
20587      */
20588     getGroupValue : function(){
20589         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20590     },
20591     
20592     
20593     onRender : function(ct, position){
20594         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20595         
20596         if(this.inputValue !== undefined){
20597             this.el.dom.value = this.inputValue;
20598         }
20599          
20600         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20601         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20602         //var viewEl = this.wrap.createChild({ 
20603         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20604         //this.viewEl = viewEl;   
20605         //this.wrap.on('click', this.onClick,  this); 
20606         
20607         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20608         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20609         
20610         
20611         
20612         if(this.boxLabel){
20613             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20614         //    viewEl.on('click', this.onClick,  this); 
20615         }
20616          if(this.checked){
20617             this.el.dom.checked =   'checked' ;
20618         }
20619          
20620     } 
20621     
20622     
20623 });Roo.rtf = {}; // namespace
20624 Roo.rtf.Hex = function(hex)
20625 {
20626     this.hexstr = hex;
20627 };
20628 Roo.rtf.Paragraph = function(opts)
20629 {
20630     this.content = []; ///??? is that used?
20631 };Roo.rtf.Span = function(opts)
20632 {
20633     this.value = opts.value;
20634 };
20635
20636 Roo.rtf.Group = function(parent)
20637 {
20638     // we dont want to acutally store parent - it will make debug a nightmare..
20639     this.content = [];
20640     this.cn  = [];
20641      
20642        
20643     
20644 };
20645
20646 Roo.rtf.Group.prototype = {
20647     ignorable : false,
20648     content: false,
20649     cn: false,
20650     addContent : function(node) {
20651         // could set styles...
20652         this.content.push(node);
20653     },
20654     addChild : function(cn)
20655     {
20656         this.cn.push(cn);
20657     },
20658     // only for images really...
20659     toDataURL : function()
20660     {
20661         var mimetype = false;
20662         switch(true) {
20663             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20664                 mimetype = "image/png";
20665                 break;
20666              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20667                 mimetype = "image/jpeg";
20668                 break;
20669             default :
20670                 return 'about:blank'; // ?? error?
20671         }
20672         
20673         
20674         var hexstring = this.content[this.content.length-1].value;
20675         
20676         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20677             return String.fromCharCode(parseInt(a, 16));
20678         }).join(""));
20679     }
20680     
20681 };
20682 // this looks like it's normally the {rtf{ .... }}
20683 Roo.rtf.Document = function()
20684 {
20685     // we dont want to acutally store parent - it will make debug a nightmare..
20686     this.rtlch  = [];
20687     this.content = [];
20688     this.cn = [];
20689     
20690 };
20691 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20692     addChild : function(cn)
20693     {
20694         this.cn.push(cn);
20695         switch(cn.type) {
20696             case 'rtlch': // most content seems to be inside this??
20697             case 'listtext':
20698             case 'shpinst':
20699                 this.rtlch.push(cn);
20700                 return;
20701             default:
20702                 this[cn.type] = cn;
20703         }
20704         
20705     },
20706     
20707     getElementsByType : function(type)
20708     {
20709         var ret =  [];
20710         this._getElementsByType(type, ret, this.cn, 'rtf');
20711         return ret;
20712     },
20713     _getElementsByType : function (type, ret, search_array, path)
20714     {
20715         search_array.forEach(function(n,i) {
20716             if (n.type == type) {
20717                 n.path = path + '/' + n.type + ':' + i;
20718                 ret.push(n);
20719             }
20720             if (n.cn.length > 0) {
20721                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20722             }
20723         },this);
20724     }
20725     
20726 });
20727  
20728 Roo.rtf.Ctrl = function(opts)
20729 {
20730     this.value = opts.value;
20731     this.param = opts.param;
20732 };
20733 /**
20734  *
20735  *
20736  * based on this https://github.com/iarna/rtf-parser
20737  * it's really only designed to extract pict from pasted RTF 
20738  *
20739  * usage:
20740  *
20741  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20742  *  
20743  *
20744  */
20745
20746  
20747
20748
20749
20750 Roo.rtf.Parser = function(text) {
20751     //super({objectMode: true})
20752     this.text = '';
20753     this.parserState = this.parseText;
20754     
20755     // these are for interpeter...
20756     this.doc = {};
20757     ///this.parserState = this.parseTop
20758     this.groupStack = [];
20759     this.hexStore = [];
20760     this.doc = false;
20761     
20762     this.groups = []; // where we put the return.
20763     
20764     for (var ii = 0; ii < text.length; ++ii) {
20765         ++this.cpos;
20766         
20767         if (text[ii] === '\n') {
20768             ++this.row;
20769             this.col = 1;
20770         } else {
20771             ++this.col;
20772         }
20773         this.parserState(text[ii]);
20774     }
20775     
20776     
20777     
20778 };
20779 Roo.rtf.Parser.prototype = {
20780     text : '', // string being parsed..
20781     controlWord : '',
20782     controlWordParam :  '',
20783     hexChar : '',
20784     doc : false,
20785     group: false,
20786     groupStack : false,
20787     hexStore : false,
20788     
20789     
20790     cpos : 0, 
20791     row : 1, // reportin?
20792     col : 1, //
20793
20794      
20795     push : function (el)
20796     {
20797         var m = 'cmd'+ el.type;
20798         if (typeof(this[m]) == 'undefined') {
20799             Roo.log('invalid cmd:' + el.type);
20800             return;
20801         }
20802         this[m](el);
20803         //Roo.log(el);
20804     },
20805     flushHexStore : function()
20806     {
20807         if (this.hexStore.length < 1) {
20808             return;
20809         }
20810         var hexstr = this.hexStore.map(
20811             function(cmd) {
20812                 return cmd.value;
20813         }).join('');
20814         
20815         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20816               
20817             
20818         this.hexStore.splice(0)
20819         
20820     },
20821     
20822     cmdgroupstart : function()
20823     {
20824         this.flushHexStore();
20825         if (this.group) {
20826             this.groupStack.push(this.group);
20827         }
20828          // parent..
20829         if (this.doc === false) {
20830             this.group = this.doc = new Roo.rtf.Document();
20831             return;
20832             
20833         }
20834         this.group = new Roo.rtf.Group(this.group);
20835     },
20836     cmdignorable : function()
20837     {
20838         this.flushHexStore();
20839         this.group.ignorable = true;
20840     },
20841     cmdendparagraph : function()
20842     {
20843         this.flushHexStore();
20844         this.group.addContent(new Roo.rtf.Paragraph());
20845     },
20846     cmdgroupend : function ()
20847     {
20848         this.flushHexStore();
20849         var endingGroup = this.group;
20850         
20851         
20852         this.group = this.groupStack.pop();
20853         if (this.group) {
20854             this.group.addChild(endingGroup);
20855         }
20856         
20857         
20858         
20859         var doc = this.group || this.doc;
20860         //if (endingGroup instanceof FontTable) {
20861         //  doc.fonts = endingGroup.table
20862         //} else if (endingGroup instanceof ColorTable) {
20863         //  doc.colors = endingGroup.table
20864         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20865         if (endingGroup.ignorable === false) {
20866             //code
20867             this.groups.push(endingGroup);
20868            // Roo.log( endingGroup );
20869         }
20870             //Roo.each(endingGroup.content, function(item)) {
20871             //    doc.addContent(item);
20872             //}
20873             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20874         //}
20875     },
20876     cmdtext : function (cmd)
20877     {
20878         this.flushHexStore();
20879         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20880             //this.group = this.doc
20881             return;  // we really don't care about stray text...
20882         }
20883         this.group.addContent(new Roo.rtf.Span(cmd));
20884     },
20885     cmdcontrolword : function (cmd)
20886     {
20887         this.flushHexStore();
20888         if (!this.group.type) {
20889             this.group.type = cmd.value;
20890             return;
20891         }
20892         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20893         // we actually don't care about ctrl words...
20894         return ;
20895         /*
20896         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20897         if (this[method]) {
20898             this[method](cmd.param)
20899         } else {
20900             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20901         }
20902         */
20903     },
20904     cmdhexchar : function(cmd) {
20905         this.hexStore.push(cmd);
20906     },
20907     cmderror : function(cmd) {
20908         throw cmd.value;
20909     },
20910     
20911     /*
20912       _flush (done) {
20913         if (this.text !== '\u0000') this.emitText()
20914         done()
20915       }
20916       */
20917       
20918       
20919     parseText : function(c)
20920     {
20921         if (c === '\\') {
20922             this.parserState = this.parseEscapes;
20923         } else if (c === '{') {
20924             this.emitStartGroup();
20925         } else if (c === '}') {
20926             this.emitEndGroup();
20927         } else if (c === '\x0A' || c === '\x0D') {
20928             // cr/lf are noise chars
20929         } else {
20930             this.text += c;
20931         }
20932     },
20933     
20934     parseEscapes: function (c)
20935     {
20936         if (c === '\\' || c === '{' || c === '}') {
20937             this.text += c;
20938             this.parserState = this.parseText;
20939         } else {
20940             this.parserState = this.parseControlSymbol;
20941             this.parseControlSymbol(c);
20942         }
20943     },
20944     parseControlSymbol: function(c)
20945     {
20946         if (c === '~') {
20947             this.text += '\u00a0'; // nbsp
20948             this.parserState = this.parseText
20949         } else if (c === '-') {
20950              this.text += '\u00ad'; // soft hyphen
20951         } else if (c === '_') {
20952             this.text += '\u2011'; // non-breaking hyphen
20953         } else if (c === '*') {
20954             this.emitIgnorable();
20955             this.parserState = this.parseText;
20956         } else if (c === "'") {
20957             this.parserState = this.parseHexChar;
20958         } else if (c === '|') { // formula cacter
20959             this.emitFormula();
20960             this.parserState = this.parseText;
20961         } else if (c === ':') { // subentry in an index entry
20962             this.emitIndexSubEntry();
20963             this.parserState = this.parseText;
20964         } else if (c === '\x0a') {
20965             this.emitEndParagraph();
20966             this.parserState = this.parseText;
20967         } else if (c === '\x0d') {
20968             this.emitEndParagraph();
20969             this.parserState = this.parseText;
20970         } else {
20971             this.parserState = this.parseControlWord;
20972             this.parseControlWord(c);
20973         }
20974     },
20975     parseHexChar: function (c)
20976     {
20977         if (/^[A-Fa-f0-9]$/.test(c)) {
20978             this.hexChar += c;
20979             if (this.hexChar.length >= 2) {
20980               this.emitHexChar();
20981               this.parserState = this.parseText;
20982             }
20983             return;
20984         }
20985         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20986         this.parserState = this.parseText;
20987         
20988     },
20989     parseControlWord : function(c)
20990     {
20991         if (c === ' ') {
20992             this.emitControlWord();
20993             this.parserState = this.parseText;
20994         } else if (/^[-\d]$/.test(c)) {
20995             this.parserState = this.parseControlWordParam;
20996             this.controlWordParam += c;
20997         } else if (/^[A-Za-z]$/.test(c)) {
20998           this.controlWord += c;
20999         } else {
21000           this.emitControlWord();
21001           this.parserState = this.parseText;
21002           this.parseText(c);
21003         }
21004     },
21005     parseControlWordParam : function (c) {
21006         if (/^\d$/.test(c)) {
21007           this.controlWordParam += c;
21008         } else if (c === ' ') {
21009           this.emitControlWord();
21010           this.parserState = this.parseText;
21011         } else {
21012           this.emitControlWord();
21013           this.parserState = this.parseText;
21014           this.parseText(c);
21015         }
21016     },
21017     
21018     
21019     
21020     
21021     emitText : function () {
21022         if (this.text === '') {
21023             return;
21024         }
21025         this.push({
21026             type: 'text',
21027             value: this.text,
21028             pos: this.cpos,
21029             row: this.row,
21030             col: this.col
21031         });
21032         this.text = ''
21033     },
21034     emitControlWord : function ()
21035     {
21036         this.emitText();
21037         if (this.controlWord === '') {
21038             // do we want to track this - it seems just to cause problems.
21039             //this.emitError('empty control word');
21040         } else {
21041             this.push({
21042                   type: 'controlword',
21043                   value: this.controlWord,
21044                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21045                   pos: this.cpos,
21046                   row: this.row,
21047                   col: this.col
21048             });
21049         }
21050         this.controlWord = '';
21051         this.controlWordParam = '';
21052     },
21053     emitStartGroup : function ()
21054     {
21055         this.emitText();
21056         this.push({
21057             type: 'groupstart',
21058             pos: this.cpos,
21059             row: this.row,
21060             col: this.col
21061         });
21062     },
21063     emitEndGroup : function ()
21064     {
21065         this.emitText();
21066         this.push({
21067             type: 'groupend',
21068             pos: this.cpos,
21069             row: this.row,
21070             col: this.col
21071         });
21072     },
21073     emitIgnorable : function ()
21074     {
21075         this.emitText();
21076         this.push({
21077             type: 'ignorable',
21078             pos: this.cpos,
21079             row: this.row,
21080             col: this.col
21081         });
21082     },
21083     emitHexChar : function ()
21084     {
21085         this.emitText();
21086         this.push({
21087             type: 'hexchar',
21088             value: this.hexChar,
21089             pos: this.cpos,
21090             row: this.row,
21091             col: this.col
21092         });
21093         this.hexChar = ''
21094     },
21095     emitError : function (message)
21096     {
21097       this.emitText();
21098       this.push({
21099             type: 'error',
21100             value: message,
21101             row: this.row,
21102             col: this.col,
21103             char: this.cpos //,
21104             //stack: new Error().stack
21105         });
21106     },
21107     emitEndParagraph : function () {
21108         this.emitText();
21109         this.push({
21110             type: 'endparagraph',
21111             pos: this.cpos,
21112             row: this.row,
21113             col: this.col
21114         });
21115     }
21116      
21117 } ;
21118 Roo.htmleditor = {};
21119  
21120 /**
21121  * @class Roo.htmleditor.Filter
21122  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21123  * @cfg {DomElement} node The node to iterate and filter
21124  * @cfg {boolean|String|Array} tag Tags to replace 
21125  * @constructor
21126  * Create a new Filter.
21127  * @param {Object} config Configuration options
21128  */
21129
21130
21131
21132 Roo.htmleditor.Filter = function(cfg) {
21133     Roo.apply(this.cfg);
21134     // this does not actually call walk as it's really just a abstract class
21135 }
21136
21137
21138 Roo.htmleditor.Filter.prototype = {
21139     
21140     node: false,
21141     
21142     tag: false,
21143
21144     // overrride to do replace comments.
21145     replaceComment : false,
21146     
21147     // overrride to do replace or do stuff with tags..
21148     replaceTag : false,
21149     
21150     walk : function(dom)
21151     {
21152         Roo.each( Array.from(dom.childNodes), function( e ) {
21153             switch(true) {
21154                 
21155                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21156                     this.replaceComment(e);
21157                     return;
21158                 
21159                 case e.nodeType != 1: //not a node.
21160                     return;
21161                 
21162                 case this.tag === true: // everything
21163                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
21164                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
21165                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21166                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21167                     if (this.replaceTag && false === this.replaceTag(e)) {
21168                         return;
21169                     }
21170                     if (e.hasChildNodes()) {
21171                         this.walk(e);
21172                     }
21173                     return;
21174                 
21175                 default:    // tags .. that do not match.
21176                     if (e.hasChildNodes()) {
21177                         this.walk(e);
21178                     }
21179             }
21180             
21181         }, this);
21182         
21183     }
21184 }; 
21185
21186 /**
21187  * @class Roo.htmleditor.FilterAttributes
21188  * clean attributes and  styles including http:// etc.. in attribute
21189  * @constructor
21190 * Run a new Attribute Filter
21191 * @param {Object} config Configuration options
21192  */
21193 Roo.htmleditor.FilterAttributes = function(cfg)
21194 {
21195     Roo.apply(this, cfg);
21196     this.attrib_black = this.attrib_black || [];
21197     this.attrib_white = this.attrib_white || [];
21198
21199     this.attrib_clean = this.attrib_clean || [];
21200     this.style_white = this.style_white || [];
21201     this.style_black = this.style_black || [];
21202     this.walk(cfg.node);
21203 }
21204
21205 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21206 {
21207     tag: true, // all tags
21208     
21209     attrib_black : false, // array
21210     attrib_clean : false,
21211     attrib_white : false,
21212
21213     style_white : false,
21214     style_black : false,
21215      
21216      
21217     replaceTag : function(node)
21218     {
21219         if (!node.attributes || !node.attributes.length) {
21220             return true;
21221         }
21222         
21223         for (var i = node.attributes.length-1; i > -1 ; i--) {
21224             var a = node.attributes[i];
21225             //console.log(a);
21226             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21227                 node.removeAttribute(a.name);
21228                 continue;
21229             }
21230             
21231             
21232             
21233             if (a.name.toLowerCase().substr(0,2)=='on')  {
21234                 node.removeAttribute(a.name);
21235                 continue;
21236             }
21237             
21238             
21239             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21240                 node.removeAttribute(a.name);
21241                 continue;
21242             }
21243             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21244                 this.cleanAttr(node,a.name,a.value); // fixme..
21245                 continue;
21246             }
21247             if (a.name == 'style') {
21248                 this.cleanStyle(node,a.name,a.value);
21249                 continue;
21250             }
21251             /// clean up MS crap..
21252             // tecnically this should be a list of valid class'es..
21253             
21254             
21255             if (a.name == 'class') {
21256                 if (a.value.match(/^Mso/)) {
21257                     node.removeAttribute('class');
21258                 }
21259                 
21260                 if (a.value.match(/^body$/)) {
21261                     node.removeAttribute('class');
21262                 }
21263                 continue;
21264             }
21265             
21266             
21267             // style cleanup!?
21268             // class cleanup?
21269             
21270         }
21271         return true; // clean children
21272     },
21273         
21274     cleanAttr: function(node, n,v)
21275     {
21276         
21277         if (v.match(/^\./) || v.match(/^\//)) {
21278             return;
21279         }
21280         if (v.match(/^(http|https):\/\//)
21281             || v.match(/^mailto:/) 
21282             || v.match(/^ftp:/)
21283             || v.match(/^data:/)
21284             ) {
21285             return;
21286         }
21287         if (v.match(/^#/)) {
21288             return;
21289         }
21290         if (v.match(/^\{/)) { // allow template editing.
21291             return;
21292         }
21293 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21294         node.removeAttribute(n);
21295         
21296     },
21297     cleanStyle : function(node,  n,v)
21298     {
21299         if (v.match(/expression/)) { //XSS?? should we even bother..
21300             node.removeAttribute(n);
21301             return;
21302         }
21303         
21304         var parts = v.split(/;/);
21305         var clean = [];
21306         
21307         Roo.each(parts, function(p) {
21308             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21309             if (!p.length) {
21310                 return true;
21311             }
21312             var l = p.split(':').shift().replace(/\s+/g,'');
21313             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21314             
21315             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21316                 return true;
21317             }
21318             //Roo.log()
21319             // only allow 'c whitelisted system attributes'
21320             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21321                 return true;
21322             }
21323             
21324             
21325             clean.push(p);
21326             return true;
21327         },this);
21328         if (clean.length) { 
21329             node.setAttribute(n, clean.join(';'));
21330         } else {
21331             node.removeAttribute(n);
21332         }
21333         
21334     }
21335         
21336         
21337         
21338     
21339 });/**
21340  * @class Roo.htmleditor.FilterBlack
21341  * remove blacklisted elements.
21342  * @constructor
21343  * Run a new Blacklisted Filter
21344  * @param {Object} config Configuration options
21345  */
21346
21347 Roo.htmleditor.FilterBlack = function(cfg)
21348 {
21349     Roo.apply(this, cfg);
21350     this.walk(cfg.node);
21351 }
21352
21353 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21354 {
21355     tag : true, // all elements.
21356    
21357     replaceTag : function(n)
21358     {
21359         n.parentNode.removeChild(n);
21360     }
21361 });
21362 /**
21363  * @class Roo.htmleditor.FilterComment
21364  * remove comments.
21365  * @constructor
21366 * Run a new Comments Filter
21367 * @param {Object} config Configuration options
21368  */
21369 Roo.htmleditor.FilterComment = function(cfg)
21370 {
21371     this.walk(cfg.node);
21372 }
21373
21374 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21375 {
21376   
21377     replaceComment : function(n)
21378     {
21379         n.parentNode.removeChild(n);
21380     }
21381 });/**
21382  * @class Roo.htmleditor.FilterKeepChildren
21383  * remove tags but keep children
21384  * @constructor
21385  * Run a new Keep Children Filter
21386  * @param {Object} config Configuration options
21387  */
21388
21389 Roo.htmleditor.FilterKeepChildren = function(cfg)
21390 {
21391     Roo.apply(this, cfg);
21392     if (this.tag === false) {
21393         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21394     }
21395     this.walk(cfg.node);
21396 }
21397
21398 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21399 {
21400     
21401   
21402     replaceTag : function(node)
21403     {
21404         // walk children...
21405         //Roo.log(node);
21406         var ar = Array.from(node.childNodes);
21407         //remove first..
21408         for (var i = 0; i < ar.length; i++) {
21409             if (ar[i].nodeType == 1) {
21410                 if (
21411                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21412                     || // array and it matches
21413                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21414                 ) {
21415                     this.replaceTag(ar[i]); // child is blacklisted as well...
21416                     continue;
21417                 }
21418             }
21419         }  
21420         ar = Array.from(node.childNodes);
21421         for (var i = 0; i < ar.length; i++) {
21422          
21423             node.removeChild(ar[i]);
21424             // what if we need to walk these???
21425             node.parentNode.insertBefore(ar[i], node);
21426             if (this.tag !== false) {
21427                 this.walk(ar[i]);
21428                 
21429             }
21430         }
21431         node.parentNode.removeChild(node);
21432         return false; // don't walk children
21433         
21434         
21435     }
21436 });/**
21437  * @class Roo.htmleditor.FilterParagraph
21438  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21439  * like on 'push' to remove the <p> tags and replace them with line breaks.
21440  * @constructor
21441  * Run a new Paragraph Filter
21442  * @param {Object} config Configuration options
21443  */
21444
21445 Roo.htmleditor.FilterParagraph = function(cfg)
21446 {
21447     // no need to apply config.
21448     this.walk(cfg.node);
21449 }
21450
21451 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21452 {
21453     
21454      
21455     tag : 'P',
21456     
21457      
21458     replaceTag : function(node)
21459     {
21460         
21461         if (node.childNodes.length == 1 &&
21462             node.childNodes[0].nodeType == 3 &&
21463             node.childNodes[0].textContent.trim().length < 1
21464             ) {
21465             // remove and replace with '<BR>';
21466             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21467             return false; // no need to walk..
21468         }
21469         var ar = Array.from(node.childNodes);
21470         for (var i = 0; i < ar.length; i++) {
21471             node.removeChild(ar[i]);
21472             // what if we need to walk these???
21473             node.parentNode.insertBefore(ar[i], node);
21474         }
21475         // now what about this?
21476         // <p> &nbsp; </p>
21477         
21478         // double BR.
21479         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21480         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21481         node.parentNode.removeChild(node);
21482         
21483         return false;
21484
21485     }
21486     
21487 });/**
21488  * @class Roo.htmleditor.FilterSpan
21489  * filter span's with no attributes out..
21490  * @constructor
21491  * Run a new Span Filter
21492  * @param {Object} config Configuration options
21493  */
21494
21495 Roo.htmleditor.FilterSpan = function(cfg)
21496 {
21497     // no need to apply config.
21498     this.walk(cfg.node);
21499 }
21500
21501 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21502 {
21503      
21504     tag : 'SPAN',
21505      
21506  
21507     replaceTag : function(node)
21508     {
21509         if (node.attributes && node.attributes.length > 0) {
21510             return true; // walk if there are any.
21511         }
21512         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21513         return false;
21514      
21515     }
21516     
21517 });/**
21518  * @class Roo.htmleditor.FilterTableWidth
21519   try and remove table width data - as that frequently messes up other stuff.
21520  * 
21521  *      was cleanTableWidths.
21522  *
21523  * Quite often pasting from word etc.. results in tables with column and widths.
21524  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21525  *
21526  * @constructor
21527  * Run a new Table Filter
21528  * @param {Object} config Configuration options
21529  */
21530
21531 Roo.htmleditor.FilterTableWidth = function(cfg)
21532 {
21533     // no need to apply config.
21534     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21535     this.walk(cfg.node);
21536 }
21537
21538 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21539 {
21540      
21541      
21542     
21543     replaceTag: function(node) {
21544         
21545         
21546       
21547         if (node.hasAttribute('width')) {
21548             node.removeAttribute('width');
21549         }
21550         
21551          
21552         if (node.hasAttribute("style")) {
21553             // pretty basic...
21554             
21555             var styles = node.getAttribute("style").split(";");
21556             var nstyle = [];
21557             Roo.each(styles, function(s) {
21558                 if (!s.match(/:/)) {
21559                     return;
21560                 }
21561                 var kv = s.split(":");
21562                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21563                     return;
21564                 }
21565                 // what ever is left... we allow.
21566                 nstyle.push(s);
21567             });
21568             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21569             if (!nstyle.length) {
21570                 node.removeAttribute('style');
21571             }
21572         }
21573         
21574         return true; // continue doing children..
21575     }
21576 });/**
21577  * @class Roo.htmleditor.FilterWord
21578  * try and clean up all the mess that Word generates.
21579  * 
21580  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21581  
21582  * @constructor
21583  * Run a new Span Filter
21584  * @param {Object} config Configuration options
21585  */
21586
21587 Roo.htmleditor.FilterWord = function(cfg)
21588 {
21589     // no need to apply config.
21590     this.replaceDocBullets(cfg.node);
21591     
21592     // this is disabled as the removal is done by other filters;
21593    // this.walk(cfg.node);
21594     
21595     
21596 }
21597
21598 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21599 {
21600     tag: true,
21601      
21602     
21603     /**
21604      * Clean up MS wordisms...
21605      */
21606     replaceTag : function(node)
21607     {
21608          
21609         // no idea what this does - span with text, replaceds with just text.
21610         if(
21611                 node.nodeName == 'SPAN' &&
21612                 !node.hasAttributes() &&
21613                 node.childNodes.length == 1 &&
21614                 node.firstChild.nodeName == "#text"  
21615         ) {
21616             var textNode = node.firstChild;
21617             node.removeChild(textNode);
21618             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21619                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21620             }
21621             node.parentNode.insertBefore(textNode, node);
21622             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21623                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21624             }
21625             
21626             node.parentNode.removeChild(node);
21627             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21628         }
21629         
21630    
21631         
21632         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21633             node.parentNode.removeChild(node);
21634             return false; // dont do chidlren
21635         }
21636         //Roo.log(node.tagName);
21637         // remove - but keep children..
21638         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21639             //Roo.log('-- removed');
21640             while (node.childNodes.length) {
21641                 var cn = node.childNodes[0];
21642                 node.removeChild(cn);
21643                 node.parentNode.insertBefore(cn, node);
21644                 // move node to parent - and clean it..
21645                 if (cn.nodeType == 1) {
21646                     this.replaceTag(cn);
21647                 }
21648                 
21649             }
21650             node.parentNode.removeChild(node);
21651             /// no need to iterate chidlren = it's got none..
21652             //this.iterateChildren(node, this.cleanWord);
21653             return false; // no need to iterate children.
21654         }
21655         // clean styles
21656         if (node.className.length) {
21657             
21658             var cn = node.className.split(/\W+/);
21659             var cna = [];
21660             Roo.each(cn, function(cls) {
21661                 if (cls.match(/Mso[a-zA-Z]+/)) {
21662                     return;
21663                 }
21664                 cna.push(cls);
21665             });
21666             node.className = cna.length ? cna.join(' ') : '';
21667             if (!cna.length) {
21668                 node.removeAttribute("class");
21669             }
21670         }
21671         
21672         if (node.hasAttribute("lang")) {
21673             node.removeAttribute("lang");
21674         }
21675         
21676         if (node.hasAttribute("style")) {
21677             
21678             var styles = node.getAttribute("style").split(";");
21679             var nstyle = [];
21680             Roo.each(styles, function(s) {
21681                 if (!s.match(/:/)) {
21682                     return;
21683                 }
21684                 var kv = s.split(":");
21685                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21686                     return;
21687                 }
21688                 // what ever is left... we allow.
21689                 nstyle.push(s);
21690             });
21691             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21692             if (!nstyle.length) {
21693                 node.removeAttribute('style');
21694             }
21695         }
21696         return true; // do children
21697         
21698         
21699         
21700     },
21701     
21702     styleToObject: function(node)
21703     {
21704         var styles = (node.getAttribute("style") || '').split(";");
21705         var ret = {};
21706         Roo.each(styles, function(s) {
21707             if (!s.match(/:/)) {
21708                 return;
21709             }
21710             var kv = s.split(":");
21711              
21712             // what ever is left... we allow.
21713             ret[kv[0].trim()] = kv[1];
21714         });
21715         return ret;
21716     },
21717     
21718     
21719     replaceDocBullets : function(doc)
21720     {
21721         // this is a bit odd - but it appears some indents use ql-indent-1
21722         Roo.log(doc.innerHTML);
21723         
21724         var listpara = doc.getElementsByClassName('MsoListParagraphCxSpFirst');
21725         for( var i = 0; i < listpara.length; i ++) {
21726             listpara.item(i).className = "MsoListParagraph";
21727         }
21728         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
21729         var htwo = doc.getElementByTagName('h2');
21730         for( var i = 0; i < htwo.length; i ++) {
21731             if (htwo.item(i).getAttribute('style').match(/mso-list:/)) {
21732                 htwo.item(i).className = "MsoListParagraph";
21733             }
21734         }
21735         listpara = doc.getElementsByClassName('ql-indent-1');
21736         while(listpara.length) {
21737             this.replaceDocBullet(listpara.item(0));
21738         }
21739         listpara = doc.getElementsByClassName('MsoListParagraph');
21740         while(listpara.length) {
21741             this.replaceDocBullet(listpara.item(0));
21742         }
21743       
21744     },
21745     
21746     replaceDocBullet : function(p)
21747     {
21748         // gather all the siblings.
21749         var ns = p,
21750             parent = p.parentNode,
21751             doc = parent.ownerDocument,
21752             items = [];
21753             
21754             
21755         while (ns) {
21756             if (ns.nodeType != 1) {
21757                 ns = ns.nextSibling;
21758                 continue;
21759             }
21760             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
21761                 break;
21762             }
21763             items.push(ns);
21764             ns = ns.nextSibling;
21765         }
21766         
21767         
21768         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
21769         parent.insertBefore(ul, p);
21770         var lvl = 0;
21771         var stack = [ ul ];
21772         var last_li = false;
21773         
21774         items.forEach(function(n, ipos) {
21775             //Roo.log("got innertHMLT=" + n.innerHTML);
21776             
21777             var spans = n.getElementsByTagName('span');
21778             if (!spans.length) {
21779                 //Roo.log("No spans found");
21780
21781                 parent.removeChild(n);
21782                 return; // skip it...
21783             }
21784            
21785                 
21786             
21787             var style = {};
21788             for(var i = 0; i < spans.length; i++) {
21789             
21790                 style = this.styleToObject(spans[i]);
21791                 if (typeof(style['mso-list']) == 'undefined') {
21792                     continue;
21793                 }
21794                 
21795                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
21796                 break;
21797             }
21798             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
21799             style = this.styleToObject(n); // mo-list is from the parent node.
21800             if (typeof(style['mso-list']) == 'undefined') {
21801                 //Roo.log("parent is missing level");
21802                 parent.removeChild(n);
21803                 return;
21804             }
21805             
21806             var nlvl =   (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1  ;
21807             
21808             
21809             
21810             if (nlvl > lvl) {
21811                 //new indent
21812                 var nul = doc.createElement('ul'); // what about number lists...
21813                 if (!last_li) {
21814                     last_li = doc.createElement('li');
21815                     stack[lvl].appendChild(last_li);
21816                 }
21817                 last_li.appendChild(nul);
21818                 stack[nlvl] = nul;
21819                 
21820             }
21821             lvl = nlvl;
21822             
21823             var nli = stack[nlvl].appendChild(doc.createElement('li'));
21824             last_li = nli;
21825             nli.innerHTML = n.innerHTML;
21826             //Roo.log("innerHTML = " + n.innerHTML);
21827             parent.removeChild(n);
21828             
21829             // copy children of p into nli
21830             /*while(n.firstChild) {
21831                 var fc = n.firstChild;
21832                 n.removeChild(fc);
21833                 nli.appendChild(fc);
21834             }*/
21835              
21836             
21837         },this);
21838         
21839         
21840         
21841         
21842     }
21843     
21844     
21845     
21846 });
21847 /**
21848  * @class Roo.htmleditor.FilterStyleToTag
21849  * part of the word stuff... - certain 'styles' should be converted to tags.
21850  * eg.
21851  *   font-weight: bold -> bold
21852  *   ?? super / subscrit etc..
21853  * 
21854  * @constructor
21855 * Run a new style to tag filter.
21856 * @param {Object} config Configuration options
21857  */
21858 Roo.htmleditor.FilterStyleToTag = function(cfg)
21859 {
21860     
21861     this.tags = {
21862         B  : [ 'fontWeight' , 'bold'],
21863         I :  [ 'fontStyle' , 'italic'],
21864         //pre :  [ 'font-style' , 'italic'],
21865         // h1.. h6 ?? font-size?
21866         SUP : [ 'verticalAlign' , 'super' ],
21867         SUB : [ 'verticalAlign' , 'sub' ]
21868         
21869         
21870     };
21871     
21872     Roo.apply(this, cfg);
21873      
21874     
21875     this.walk(cfg.node);
21876     
21877     
21878     
21879 }
21880
21881
21882 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21883 {
21884     tag: true, // all tags
21885     
21886     tags : false,
21887     
21888     
21889     replaceTag : function(node)
21890     {
21891         
21892         
21893         if (node.getAttribute("style") === null) {
21894             return true;
21895         }
21896         var inject = [];
21897         for (var k in this.tags) {
21898             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21899                 inject.push(k);
21900                 node.style.removeProperty(this.tags[k][0]);
21901             }
21902         }
21903         if (!inject.length) {
21904             return true; 
21905         }
21906         var cn = Array.from(node.childNodes);
21907         var nn = node;
21908         Roo.each(inject, function(t) {
21909             var nc = node.ownerDocument.createElement(t);
21910             nn.appendChild(nc);
21911             nn = nc;
21912         });
21913         for(var i = 0;i < cn.length;cn++) {
21914             node.removeChild(cn[i]);
21915             nn.appendChild(cn[i]);
21916         }
21917         return true /// iterate thru
21918     }
21919     
21920 })/**
21921  * @class Roo.htmleditor.FilterLongBr
21922  * BR/BR/BR - keep a maximum of 2...
21923  * @constructor
21924  * Run a new Long BR Filter
21925  * @param {Object} config Configuration options
21926  */
21927
21928 Roo.htmleditor.FilterLongBr = function(cfg)
21929 {
21930     // no need to apply config.
21931     this.walk(cfg.node);
21932 }
21933
21934 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21935 {
21936     
21937      
21938     tag : 'BR',
21939     
21940      
21941     replaceTag : function(node)
21942     {
21943         
21944         var ps = node.nextSibling;
21945         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21946             ps = ps.nextSibling;
21947         }
21948         
21949         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21950             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21951             return false;
21952         }
21953         
21954         if (!ps || ps.nodeType != 1) {
21955             return false;
21956         }
21957         
21958         if (!ps || ps.tagName != 'BR') {
21959            
21960             return false;
21961         }
21962         
21963         
21964         
21965         
21966         
21967         if (!node.previousSibling) {
21968             return false;
21969         }
21970         var ps = node.previousSibling;
21971         
21972         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21973             ps = ps.previousSibling;
21974         }
21975         if (!ps || ps.nodeType != 1) {
21976             return false;
21977         }
21978         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21979         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21980             return false;
21981         }
21982         
21983         node.parentNode.removeChild(node); // remove me...
21984         
21985         return false; // no need to do children
21986
21987     }
21988     
21989 }); 
21990
21991 /**
21992  * @class Roo.htmleditor.FilterBlock
21993  * removes id / data-block and contenteditable that are associated with blocks
21994  * usage should be done on a cloned copy of the dom
21995  * @constructor
21996 * Run a new Attribute Filter { node : xxxx }}
21997 * @param {Object} config Configuration options
21998  */
21999 Roo.htmleditor.FilterBlock = function(cfg)
22000 {
22001     Roo.apply(this, cfg);
22002     var qa = cfg.node.querySelectorAll;
22003     this.removeAttributes('data-block');
22004     this.removeAttributes('contenteditable');
22005     this.removeAttributes('id');
22006     
22007 }
22008
22009 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
22010 {
22011     node: true, // all tags
22012      
22013      
22014     removeAttributes : function(attr)
22015     {
22016         var ar = this.node.querySelectorAll('*[' + attr + ']');
22017         for (var i =0;i<ar.length;i++) {
22018             ar[i].removeAttribute(attr);
22019         }
22020     }
22021         
22022         
22023         
22024     
22025 });
22026 /***
22027  * This is based loosely on tinymce 
22028  * @class Roo.htmleditor.TidySerializer
22029  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22030  * @constructor
22031  * @method Serializer
22032  * @param {Object} settings Name/value settings object.
22033  */
22034
22035
22036 Roo.htmleditor.TidySerializer = function(settings)
22037 {
22038     Roo.apply(this, settings);
22039     
22040     this.writer = new Roo.htmleditor.TidyWriter(settings);
22041     
22042     
22043
22044 };
22045 Roo.htmleditor.TidySerializer.prototype = {
22046     
22047     /**
22048      * @param {boolean} inner do the inner of the node.
22049      */
22050     inner : false,
22051     
22052     writer : false,
22053     
22054     /**
22055     * Serializes the specified node into a string.
22056     *
22057     * @example
22058     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
22059     * @method serialize
22060     * @param {DomElement} node Node instance to serialize.
22061     * @return {String} String with HTML based on DOM tree.
22062     */
22063     serialize : function(node) {
22064         
22065         // = settings.validate;
22066         var writer = this.writer;
22067         var self  = this;
22068         this.handlers = {
22069             // #text
22070             3: function(node) {
22071                 
22072                 writer.text(node.nodeValue, node);
22073             },
22074             // #comment
22075             8: function(node) {
22076                 writer.comment(node.nodeValue);
22077             },
22078             // Processing instruction
22079             7: function(node) {
22080                 writer.pi(node.name, node.nodeValue);
22081             },
22082             // Doctype
22083             10: function(node) {
22084                 writer.doctype(node.nodeValue);
22085             },
22086             // CDATA
22087             4: function(node) {
22088                 writer.cdata(node.nodeValue);
22089             },
22090             // Document fragment
22091             11: function(node) {
22092                 node = node.firstChild;
22093                 if (!node) {
22094                     return;
22095                 }
22096                 while(node) {
22097                     self.walk(node);
22098                     node = node.nextSibling
22099                 }
22100             }
22101         };
22102         writer.reset();
22103         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
22104         return writer.getContent();
22105     },
22106
22107     walk: function(node)
22108     {
22109         var attrName, attrValue, sortedAttrs, i, l, elementRule,
22110             handler = this.handlers[node.nodeType];
22111             
22112         if (handler) {
22113             handler(node);
22114             return;
22115         }
22116     
22117         var name = node.nodeName;
22118         var isEmpty = node.childNodes.length < 1;
22119       
22120         var writer = this.writer;
22121         var attrs = node.attributes;
22122         // Sort attributes
22123         
22124         writer.start(node.nodeName, attrs, isEmpty, node);
22125         if (isEmpty) {
22126             return;
22127         }
22128         node = node.firstChild;
22129         if (!node) {
22130             writer.end(name);
22131             return;
22132         }
22133         while (node) {
22134             this.walk(node);
22135             node = node.nextSibling;
22136         }
22137         writer.end(name);
22138         
22139     
22140     }
22141     // Serialize element and treat all non elements as fragments
22142    
22143 }; 
22144
22145 /***
22146  * This is based loosely on tinymce 
22147  * @class Roo.htmleditor.TidyWriter
22148  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22149  *
22150  * Known issues?
22151  * - not tested much with 'PRE' formated elements.
22152  * 
22153  *
22154  *
22155  */
22156
22157 Roo.htmleditor.TidyWriter = function(settings)
22158 {
22159     
22160     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
22161     Roo.apply(this, settings);
22162     this.html = [];
22163     this.state = [];
22164      
22165     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
22166   
22167 }
22168 Roo.htmleditor.TidyWriter.prototype = {
22169
22170  
22171     state : false,
22172     
22173     indent :  '  ',
22174     
22175     // part of state...
22176     indentstr : '',
22177     in_pre: false,
22178     in_inline : false,
22179     last_inline : false,
22180     encode : false,
22181      
22182     
22183             /**
22184     * Writes the a start element such as <p id="a">.
22185     *
22186     * @method start
22187     * @param {String} name Name of the element.
22188     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22189     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22190     */
22191     start: function(name, attrs, empty, node)
22192     {
22193         var i, l, attr, value;
22194         
22195         // there are some situations where adding line break && indentation will not work. will not work.
22196         // <span / b / i ... formating?
22197         
22198         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22199         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22200         
22201         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22202         
22203         var add_lb = name == 'BR' ? false : in_inline;
22204         
22205         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22206             i_inline = false;
22207         }
22208
22209         var indentstr =  this.indentstr;
22210         
22211         // e_inline = elements that can be inline, but still allow \n before and after?
22212         // only 'BR' ??? any others?
22213         
22214         // ADD LINE BEFORE tage
22215         if (!this.in_pre) {
22216             if (in_inline) {
22217                 //code
22218                 if (name == 'BR') {
22219                     this.addLine();
22220                 } else if (this.lastElementEndsWS()) {
22221                     this.addLine();
22222                 } else{
22223                     // otherwise - no new line. (and dont indent.)
22224                     indentstr = '';
22225                 }
22226                 
22227             } else {
22228                 this.addLine();
22229             }
22230         } else {
22231             indentstr = '';
22232         }
22233         
22234         this.html.push(indentstr + '<', name.toLowerCase());
22235         
22236         if (attrs) {
22237             for (i = 0, l = attrs.length; i < l; i++) {
22238                 attr = attrs[i];
22239                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22240             }
22241         }
22242      
22243         if (empty) {
22244             if (is_short) {
22245                 this.html[this.html.length] = '/>';
22246             } else {
22247                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22248             }
22249             var e_inline = name == 'BR' ? false : this.in_inline;
22250             
22251             if (!e_inline && !this.in_pre) {
22252                 this.addLine();
22253             }
22254             return;
22255         
22256         }
22257         // not empty..
22258         this.html[this.html.length] = '>';
22259         
22260         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22261         /*
22262         if (!in_inline && !in_pre) {
22263             var cn = node.firstChild;
22264             while(cn) {
22265                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22266                     in_inline = true
22267                     break;
22268                 }
22269                 cn = cn.nextSibling;
22270             }
22271              
22272         }
22273         */
22274         
22275         
22276         this.pushState({
22277             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22278             in_pre : in_pre,
22279             in_inline :  in_inline
22280         });
22281         // add a line after if we are not in a
22282         
22283         if (!in_inline && !in_pre) {
22284             this.addLine();
22285         }
22286         
22287             
22288          
22289         
22290     },
22291     
22292     lastElementEndsWS : function()
22293     {
22294         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22295         if (value === false) {
22296             return true;
22297         }
22298         return value.match(/\s+$/);
22299         
22300     },
22301     
22302     /**
22303      * Writes the a end element such as </p>.
22304      *
22305      * @method end
22306      * @param {String} name Name of the element.
22307      */
22308     end: function(name) {
22309         var value;
22310         this.popState();
22311         var indentstr = '';
22312         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22313         
22314         if (!this.in_pre && !in_inline) {
22315             this.addLine();
22316             indentstr  = this.indentstr;
22317         }
22318         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22319         this.last_inline = in_inline;
22320         
22321         // pop the indent state..
22322     },
22323     /**
22324      * Writes a text node.
22325      *
22326      * In pre - we should not mess with the contents.
22327      * 
22328      *
22329      * @method text
22330      * @param {String} text String to write out.
22331      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22332      */
22333     text: function(in_text, node)
22334     {
22335         // if not in whitespace critical
22336         if (in_text.length < 1) {
22337             return;
22338         }
22339         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
22340         
22341         if (this.in_pre) {
22342             this.html[this.html.length] =  text;
22343             return;   
22344         }
22345         
22346         if (this.in_inline) {
22347             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22348             if (text != ' ') {
22349                 text = text.replace(/\s+/,' ');  // all white space to single white space
22350                 
22351                     
22352                 // if next tag is '<BR>', then we can trim right..
22353                 if (node.nextSibling &&
22354                     node.nextSibling.nodeType == 1 &&
22355                     node.nextSibling.nodeName == 'BR' )
22356                 {
22357                     text = text.replace(/\s+$/g,'');
22358                 }
22359                 // if previous tag was a BR, we can also trim..
22360                 if (node.previousSibling &&
22361                     node.previousSibling.nodeType == 1 &&
22362                     node.previousSibling.nodeName == 'BR' )
22363                 {
22364                     text = this.indentstr +  text.replace(/^\s+/g,'');
22365                 }
22366                 if (text.match(/\n/)) {
22367                     text = text.replace(
22368                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22369                     );
22370                     // remoeve the last whitespace / line break.
22371                     text = text.replace(/\n\s+$/,'');
22372                 }
22373                 // repace long lines
22374                 
22375             }
22376              
22377             this.html[this.html.length] =  text;
22378             return;   
22379         }
22380         // see if previous element was a inline element.
22381         var indentstr = this.indentstr;
22382    
22383         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22384         
22385         // should trim left?
22386         if (node.previousSibling &&
22387             node.previousSibling.nodeType == 1 &&
22388             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22389         {
22390             indentstr = '';
22391             
22392         } else {
22393             this.addLine();
22394             text = text.replace(/^\s+/,''); // trim left
22395           
22396         }
22397         // should trim right?
22398         if (node.nextSibling &&
22399             node.nextSibling.nodeType == 1 &&
22400             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22401         {
22402           // noop
22403             
22404         }  else {
22405             text = text.replace(/\s+$/,''); // trim right
22406         }
22407          
22408               
22409         
22410         
22411         
22412         if (text.length < 1) {
22413             return;
22414         }
22415         if (!text.match(/\n/)) {
22416             this.html.push(indentstr + text);
22417             return;
22418         }
22419         
22420         text = this.indentstr + text.replace(
22421             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22422         );
22423         // remoeve the last whitespace / line break.
22424         text = text.replace(/\s+$/,''); 
22425         
22426         this.html.push(text);
22427         
22428         // split and indent..
22429         
22430         
22431     },
22432     /**
22433      * Writes a cdata node such as <![CDATA[data]]>.
22434      *
22435      * @method cdata
22436      * @param {String} text String to write out inside the cdata.
22437      */
22438     cdata: function(text) {
22439         this.html.push('<![CDATA[', text, ']]>');
22440     },
22441     /**
22442     * Writes a comment node such as <!-- Comment -->.
22443     *
22444     * @method cdata
22445     * @param {String} text String to write out inside the comment.
22446     */
22447    comment: function(text) {
22448        this.html.push('<!--', text, '-->');
22449    },
22450     /**
22451      * Writes a PI node such as <?xml attr="value" ?>.
22452      *
22453      * @method pi
22454      * @param {String} name Name of the pi.
22455      * @param {String} text String to write out inside the pi.
22456      */
22457     pi: function(name, text) {
22458         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22459         this.indent != '' && this.html.push('\n');
22460     },
22461     /**
22462      * Writes a doctype node such as <!DOCTYPE data>.
22463      *
22464      * @method doctype
22465      * @param {String} text String to write out inside the doctype.
22466      */
22467     doctype: function(text) {
22468         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22469     },
22470     /**
22471      * Resets the internal buffer if one wants to reuse the writer.
22472      *
22473      * @method reset
22474      */
22475     reset: function() {
22476         this.html.length = 0;
22477         this.state = [];
22478         this.pushState({
22479             indentstr : '',
22480             in_pre : false, 
22481             in_inline : false
22482         })
22483     },
22484     /**
22485      * Returns the contents that got serialized.
22486      *
22487      * @method getContent
22488      * @return {String} HTML contents that got written down.
22489      */
22490     getContent: function() {
22491         return this.html.join('').replace(/\n$/, '');
22492     },
22493     
22494     pushState : function(cfg)
22495     {
22496         this.state.push(cfg);
22497         Roo.apply(this, cfg);
22498     },
22499     
22500     popState : function()
22501     {
22502         if (this.state.length < 1) {
22503             return; // nothing to push
22504         }
22505         var cfg = {
22506             in_pre: false,
22507             indentstr : ''
22508         };
22509         this.state.pop();
22510         if (this.state.length > 0) {
22511             cfg = this.state[this.state.length-1]; 
22512         }
22513         Roo.apply(this, cfg);
22514     },
22515     
22516     addLine: function()
22517     {
22518         if (this.html.length < 1) {
22519             return;
22520         }
22521         
22522         
22523         var value = this.html[this.html.length - 1];
22524         if (value.length > 0 && '\n' !== value) {
22525             this.html.push('\n');
22526         }
22527     }
22528     
22529     
22530 //'pre script noscript style textarea video audio iframe object code'
22531 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22532 // inline 
22533 };
22534
22535 Roo.htmleditor.TidyWriter.inline_elements = [
22536         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22537         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22538 ];
22539 Roo.htmleditor.TidyWriter.shortend_elements = [
22540     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22541     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22542 ];
22543
22544 Roo.htmleditor.TidyWriter.whitespace_elements = [
22545     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22546 ];/***
22547  * This is based loosely on tinymce 
22548  * @class Roo.htmleditor.TidyEntities
22549  * @static
22550  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22551  *
22552  * Not 100% sure this is actually used or needed.
22553  */
22554
22555 Roo.htmleditor.TidyEntities = {
22556     
22557     /**
22558      * initialize data..
22559      */
22560     init : function (){
22561      
22562         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22563        
22564     },
22565
22566
22567     buildEntitiesLookup: function(items, radix) {
22568         var i, chr, entity, lookup = {};
22569         if (!items) {
22570             return {};
22571         }
22572         items = typeof(items) == 'string' ? items.split(',') : items;
22573         radix = radix || 10;
22574         // Build entities lookup table
22575         for (i = 0; i < items.length; i += 2) {
22576             chr = String.fromCharCode(parseInt(items[i], radix));
22577             // Only add non base entities
22578             if (!this.baseEntities[chr]) {
22579                 entity = '&' + items[i + 1] + ';';
22580                 lookup[chr] = entity;
22581                 lookup[entity] = chr;
22582             }
22583         }
22584         return lookup;
22585         
22586     },
22587     
22588     asciiMap : {
22589             128: '€',
22590             130: '‚',
22591             131: 'ƒ',
22592             132: '„',
22593             133: '…',
22594             134: '†',
22595             135: '‡',
22596             136: 'ˆ',
22597             137: '‰',
22598             138: 'Š',
22599             139: '‹',
22600             140: 'Œ',
22601             142: 'Ž',
22602             145: '‘',
22603             146: '’',
22604             147: '“',
22605             148: '”',
22606             149: '•',
22607             150: '–',
22608             151: '—',
22609             152: '˜',
22610             153: '™',
22611             154: 'š',
22612             155: '›',
22613             156: 'œ',
22614             158: 'ž',
22615             159: 'Ÿ'
22616     },
22617     // Raw entities
22618     baseEntities : {
22619         '"': '&quot;',
22620         // Needs to be escaped since the YUI compressor would otherwise break the code
22621         '\'': '&#39;',
22622         '<': '&lt;',
22623         '>': '&gt;',
22624         '&': '&amp;',
22625         '`': '&#96;'
22626     },
22627     // Reverse lookup table for raw entities
22628     reverseEntities : {
22629         '&lt;': '<',
22630         '&gt;': '>',
22631         '&amp;': '&',
22632         '&quot;': '"',
22633         '&apos;': '\''
22634     },
22635     
22636     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22637     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22638     rawCharsRegExp : /[<>&\"\']/g,
22639     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22640     namedEntities  : false,
22641     namedEntitiesData : [ 
22642         '50',
22643         'nbsp',
22644         '51',
22645         'iexcl',
22646         '52',
22647         'cent',
22648         '53',
22649         'pound',
22650         '54',
22651         'curren',
22652         '55',
22653         'yen',
22654         '56',
22655         'brvbar',
22656         '57',
22657         'sect',
22658         '58',
22659         'uml',
22660         '59',
22661         'copy',
22662         '5a',
22663         'ordf',
22664         '5b',
22665         'laquo',
22666         '5c',
22667         'not',
22668         '5d',
22669         'shy',
22670         '5e',
22671         'reg',
22672         '5f',
22673         'macr',
22674         '5g',
22675         'deg',
22676         '5h',
22677         'plusmn',
22678         '5i',
22679         'sup2',
22680         '5j',
22681         'sup3',
22682         '5k',
22683         'acute',
22684         '5l',
22685         'micro',
22686         '5m',
22687         'para',
22688         '5n',
22689         'middot',
22690         '5o',
22691         'cedil',
22692         '5p',
22693         'sup1',
22694         '5q',
22695         'ordm',
22696         '5r',
22697         'raquo',
22698         '5s',
22699         'frac14',
22700         '5t',
22701         'frac12',
22702         '5u',
22703         'frac34',
22704         '5v',
22705         'iquest',
22706         '60',
22707         'Agrave',
22708         '61',
22709         'Aacute',
22710         '62',
22711         'Acirc',
22712         '63',
22713         'Atilde',
22714         '64',
22715         'Auml',
22716         '65',
22717         'Aring',
22718         '66',
22719         'AElig',
22720         '67',
22721         'Ccedil',
22722         '68',
22723         'Egrave',
22724         '69',
22725         'Eacute',
22726         '6a',
22727         'Ecirc',
22728         '6b',
22729         'Euml',
22730         '6c',
22731         'Igrave',
22732         '6d',
22733         'Iacute',
22734         '6e',
22735         'Icirc',
22736         '6f',
22737         'Iuml',
22738         '6g',
22739         'ETH',
22740         '6h',
22741         'Ntilde',
22742         '6i',
22743         'Ograve',
22744         '6j',
22745         'Oacute',
22746         '6k',
22747         'Ocirc',
22748         '6l',
22749         'Otilde',
22750         '6m',
22751         'Ouml',
22752         '6n',
22753         'times',
22754         '6o',
22755         'Oslash',
22756         '6p',
22757         'Ugrave',
22758         '6q',
22759         'Uacute',
22760         '6r',
22761         'Ucirc',
22762         '6s',
22763         'Uuml',
22764         '6t',
22765         'Yacute',
22766         '6u',
22767         'THORN',
22768         '6v',
22769         'szlig',
22770         '70',
22771         'agrave',
22772         '71',
22773         'aacute',
22774         '72',
22775         'acirc',
22776         '73',
22777         'atilde',
22778         '74',
22779         'auml',
22780         '75',
22781         'aring',
22782         '76',
22783         'aelig',
22784         '77',
22785         'ccedil',
22786         '78',
22787         'egrave',
22788         '79',
22789         'eacute',
22790         '7a',
22791         'ecirc',
22792         '7b',
22793         'euml',
22794         '7c',
22795         'igrave',
22796         '7d',
22797         'iacute',
22798         '7e',
22799         'icirc',
22800         '7f',
22801         'iuml',
22802         '7g',
22803         'eth',
22804         '7h',
22805         'ntilde',
22806         '7i',
22807         'ograve',
22808         '7j',
22809         'oacute',
22810         '7k',
22811         'ocirc',
22812         '7l',
22813         'otilde',
22814         '7m',
22815         'ouml',
22816         '7n',
22817         'divide',
22818         '7o',
22819         'oslash',
22820         '7p',
22821         'ugrave',
22822         '7q',
22823         'uacute',
22824         '7r',
22825         'ucirc',
22826         '7s',
22827         'uuml',
22828         '7t',
22829         'yacute',
22830         '7u',
22831         'thorn',
22832         '7v',
22833         'yuml',
22834         'ci',
22835         'fnof',
22836         'sh',
22837         'Alpha',
22838         'si',
22839         'Beta',
22840         'sj',
22841         'Gamma',
22842         'sk',
22843         'Delta',
22844         'sl',
22845         'Epsilon',
22846         'sm',
22847         'Zeta',
22848         'sn',
22849         'Eta',
22850         'so',
22851         'Theta',
22852         'sp',
22853         'Iota',
22854         'sq',
22855         'Kappa',
22856         'sr',
22857         'Lambda',
22858         'ss',
22859         'Mu',
22860         'st',
22861         'Nu',
22862         'su',
22863         'Xi',
22864         'sv',
22865         'Omicron',
22866         't0',
22867         'Pi',
22868         't1',
22869         'Rho',
22870         't3',
22871         'Sigma',
22872         't4',
22873         'Tau',
22874         't5',
22875         'Upsilon',
22876         't6',
22877         'Phi',
22878         't7',
22879         'Chi',
22880         't8',
22881         'Psi',
22882         't9',
22883         'Omega',
22884         'th',
22885         'alpha',
22886         'ti',
22887         'beta',
22888         'tj',
22889         'gamma',
22890         'tk',
22891         'delta',
22892         'tl',
22893         'epsilon',
22894         'tm',
22895         'zeta',
22896         'tn',
22897         'eta',
22898         'to',
22899         'theta',
22900         'tp',
22901         'iota',
22902         'tq',
22903         'kappa',
22904         'tr',
22905         'lambda',
22906         'ts',
22907         'mu',
22908         'tt',
22909         'nu',
22910         'tu',
22911         'xi',
22912         'tv',
22913         'omicron',
22914         'u0',
22915         'pi',
22916         'u1',
22917         'rho',
22918         'u2',
22919         'sigmaf',
22920         'u3',
22921         'sigma',
22922         'u4',
22923         'tau',
22924         'u5',
22925         'upsilon',
22926         'u6',
22927         'phi',
22928         'u7',
22929         'chi',
22930         'u8',
22931         'psi',
22932         'u9',
22933         'omega',
22934         'uh',
22935         'thetasym',
22936         'ui',
22937         'upsih',
22938         'um',
22939         'piv',
22940         '812',
22941         'bull',
22942         '816',
22943         'hellip',
22944         '81i',
22945         'prime',
22946         '81j',
22947         'Prime',
22948         '81u',
22949         'oline',
22950         '824',
22951         'frasl',
22952         '88o',
22953         'weierp',
22954         '88h',
22955         'image',
22956         '88s',
22957         'real',
22958         '892',
22959         'trade',
22960         '89l',
22961         'alefsym',
22962         '8cg',
22963         'larr',
22964         '8ch',
22965         'uarr',
22966         '8ci',
22967         'rarr',
22968         '8cj',
22969         'darr',
22970         '8ck',
22971         'harr',
22972         '8dl',
22973         'crarr',
22974         '8eg',
22975         'lArr',
22976         '8eh',
22977         'uArr',
22978         '8ei',
22979         'rArr',
22980         '8ej',
22981         'dArr',
22982         '8ek',
22983         'hArr',
22984         '8g0',
22985         'forall',
22986         '8g2',
22987         'part',
22988         '8g3',
22989         'exist',
22990         '8g5',
22991         'empty',
22992         '8g7',
22993         'nabla',
22994         '8g8',
22995         'isin',
22996         '8g9',
22997         'notin',
22998         '8gb',
22999         'ni',
23000         '8gf',
23001         'prod',
23002         '8gh',
23003         'sum',
23004         '8gi',
23005         'minus',
23006         '8gn',
23007         'lowast',
23008         '8gq',
23009         'radic',
23010         '8gt',
23011         'prop',
23012         '8gu',
23013         'infin',
23014         '8h0',
23015         'ang',
23016         '8h7',
23017         'and',
23018         '8h8',
23019         'or',
23020         '8h9',
23021         'cap',
23022         '8ha',
23023         'cup',
23024         '8hb',
23025         'int',
23026         '8hk',
23027         'there4',
23028         '8hs',
23029         'sim',
23030         '8i5',
23031         'cong',
23032         '8i8',
23033         'asymp',
23034         '8j0',
23035         'ne',
23036         '8j1',
23037         'equiv',
23038         '8j4',
23039         'le',
23040         '8j5',
23041         'ge',
23042         '8k2',
23043         'sub',
23044         '8k3',
23045         'sup',
23046         '8k4',
23047         'nsub',
23048         '8k6',
23049         'sube',
23050         '8k7',
23051         'supe',
23052         '8kl',
23053         'oplus',
23054         '8kn',
23055         'otimes',
23056         '8l5',
23057         'perp',
23058         '8m5',
23059         'sdot',
23060         '8o8',
23061         'lceil',
23062         '8o9',
23063         'rceil',
23064         '8oa',
23065         'lfloor',
23066         '8ob',
23067         'rfloor',
23068         '8p9',
23069         'lang',
23070         '8pa',
23071         'rang',
23072         '9ea',
23073         'loz',
23074         '9j0',
23075         'spades',
23076         '9j3',
23077         'clubs',
23078         '9j5',
23079         'hearts',
23080         '9j6',
23081         'diams',
23082         'ai',
23083         'OElig',
23084         'aj',
23085         'oelig',
23086         'b0',
23087         'Scaron',
23088         'b1',
23089         'scaron',
23090         'bo',
23091         'Yuml',
23092         'm6',
23093         'circ',
23094         'ms',
23095         'tilde',
23096         '802',
23097         'ensp',
23098         '803',
23099         'emsp',
23100         '809',
23101         'thinsp',
23102         '80c',
23103         'zwnj',
23104         '80d',
23105         'zwj',
23106         '80e',
23107         'lrm',
23108         '80f',
23109         'rlm',
23110         '80j',
23111         'ndash',
23112         '80k',
23113         'mdash',
23114         '80o',
23115         'lsquo',
23116         '80p',
23117         'rsquo',
23118         '80q',
23119         'sbquo',
23120         '80s',
23121         'ldquo',
23122         '80t',
23123         'rdquo',
23124         '80u',
23125         'bdquo',
23126         '810',
23127         'dagger',
23128         '811',
23129         'Dagger',
23130         '81g',
23131         'permil',
23132         '81p',
23133         'lsaquo',
23134         '81q',
23135         'rsaquo',
23136         '85c',
23137         'euro'
23138     ],
23139
23140          
23141     /**
23142      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
23143      *
23144      * @method encodeRaw
23145      * @param {String} text Text to encode.
23146      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23147      * @return {String} Entity encoded text.
23148      */
23149     encodeRaw: function(text, attr)
23150     {
23151         var t = this;
23152         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23153             return t.baseEntities[chr] || chr;
23154         });
23155     },
23156     /**
23157      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
23158      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
23159      * and is exposed as the DOMUtils.encode function.
23160      *
23161      * @method encodeAllRaw
23162      * @param {String} text Text to encode.
23163      * @return {String} Entity encoded text.
23164      */
23165     encodeAllRaw: function(text) {
23166         var t = this;
23167         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
23168             return t.baseEntities[chr] || chr;
23169         });
23170     },
23171     /**
23172      * Encodes the specified string using numeric entities. The core entities will be
23173      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
23174      *
23175      * @method encodeNumeric
23176      * @param {String} text Text to encode.
23177      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23178      * @return {String} Entity encoded text.
23179      */
23180     encodeNumeric: function(text, attr) {
23181         var t = this;
23182         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23183             // Multi byte sequence convert it to a single entity
23184             if (chr.length > 1) {
23185                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23186             }
23187             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23188         });
23189     },
23190     /**
23191      * Encodes the specified string using named entities. The core entities will be encoded
23192      * as named ones but all non lower ascii characters will be encoded into named entities.
23193      *
23194      * @method encodeNamed
23195      * @param {String} text Text to encode.
23196      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23197      * @param {Object} entities Optional parameter with entities to use.
23198      * @return {String} Entity encoded text.
23199      */
23200     encodeNamed: function(text, attr, entities) {
23201         var t = this;
23202         entities = entities || this.namedEntities;
23203         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23204             return t.baseEntities[chr] || entities[chr] || chr;
23205         });
23206     },
23207     /**
23208      * Returns an encode function based on the name(s) and it's optional entities.
23209      *
23210      * @method getEncodeFunc
23211      * @param {String} name Comma separated list of encoders for example named,numeric.
23212      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23213      * @return {function} Encode function to be used.
23214      */
23215     getEncodeFunc: function(name, entities) {
23216         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23217         var t = this;
23218         function encodeNamedAndNumeric(text, attr) {
23219             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23220                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23221             });
23222         }
23223
23224         function encodeCustomNamed(text, attr) {
23225             return t.encodeNamed(text, attr, entities);
23226         }
23227         // Replace + with , to be compatible with previous TinyMCE versions
23228         name = this.makeMap(name.replace(/\+/g, ','));
23229         // Named and numeric encoder
23230         if (name.named && name.numeric) {
23231             return this.encodeNamedAndNumeric;
23232         }
23233         // Named encoder
23234         if (name.named) {
23235             // Custom names
23236             if (entities) {
23237                 return encodeCustomNamed;
23238             }
23239             return this.encodeNamed;
23240         }
23241         // Numeric
23242         if (name.numeric) {
23243             return this.encodeNumeric;
23244         }
23245         // Raw encoder
23246         return this.encodeRaw;
23247     },
23248     /**
23249      * Decodes the specified string, this will replace entities with raw UTF characters.
23250      *
23251      * @method decode
23252      * @param {String} text Text to entity decode.
23253      * @return {String} Entity decoded string.
23254      */
23255     decode: function(text)
23256     {
23257         var  t = this;
23258         return text.replace(this.entityRegExp, function(all, numeric) {
23259             if (numeric) {
23260                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23261                 // Support upper UTF
23262                 if (numeric > 65535) {
23263                     numeric -= 65536;
23264                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23265                 }
23266                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23267             }
23268             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23269         });
23270     },
23271     nativeDecode : function (text) {
23272         return text;
23273     },
23274     makeMap : function (items, delim, map) {
23275                 var i;
23276                 items = items || [];
23277                 delim = delim || ',';
23278                 if (typeof items == "string") {
23279                         items = items.split(delim);
23280                 }
23281                 map = map || {};
23282                 i = items.length;
23283                 while (i--) {
23284                         map[items[i]] = {};
23285                 }
23286                 return map;
23287         }
23288 };
23289     
23290     
23291     
23292 Roo.htmleditor.TidyEntities.init();
23293 /**
23294  * @class Roo.htmleditor.KeyEnter
23295  * Handle Enter press..
23296  * @cfg {Roo.HtmlEditorCore} core the editor.
23297  * @constructor
23298  * Create a new Filter.
23299  * @param {Object} config Configuration options
23300  */
23301
23302
23303
23304
23305
23306 Roo.htmleditor.KeyEnter = function(cfg) {
23307     Roo.apply(this, cfg);
23308     // this does not actually call walk as it's really just a abstract class
23309  
23310     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23311 }
23312
23313 //Roo.htmleditor.KeyEnter.i = 0;
23314
23315
23316 Roo.htmleditor.KeyEnter.prototype = {
23317     
23318     core : false,
23319     
23320     keypress : function(e)
23321     {
23322         if (e.charCode != 13 && e.charCode != 10) {
23323             Roo.log([e.charCode,e]);
23324             return true;
23325         }
23326         e.preventDefault();
23327         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23328         var doc = this.core.doc;
23329           //add a new line
23330        
23331     
23332         var sel = this.core.getSelection();
23333         var range = sel.getRangeAt(0);
23334         var n = range.commonAncestorContainer;
23335         var pc = range.closest([ 'ol', 'ul']);
23336         var pli = range.closest('li');
23337         if (!pc || e.ctrlKey) {
23338             // on it list, or ctrl pressed.
23339             if (!e.ctrlKey) {
23340                 sel.insertNode('br', 'after'); 
23341             } else {
23342                 // only do this if we have ctrl key..
23343                 var br = doc.createElement('br');
23344                 br.className = 'clear';
23345                 br.setAttribute('style', 'clear: both');
23346                 sel.insertNode(br, 'after'); 
23347             }
23348             
23349          
23350             this.core.undoManager.addEvent();
23351             this.core.fireEditorEvent(e);
23352             return false;
23353         }
23354         
23355         // deal with <li> insetion
23356         if (pli.innerText.trim() == '' &&
23357             pli.previousSibling &&
23358             pli.previousSibling.nodeName == 'LI' &&
23359             pli.previousSibling.innerText.trim() ==  '') {
23360             pli.parentNode.removeChild(pli.previousSibling);
23361             sel.cursorAfter(pc);
23362             this.core.undoManager.addEvent();
23363             this.core.fireEditorEvent(e);
23364             return false;
23365         }
23366     
23367         var li = doc.createElement('LI');
23368         li.innerHTML = '&nbsp;';
23369         if (!pli || !pli.firstSibling) {
23370             pc.appendChild(li);
23371         } else {
23372             pli.parentNode.insertBefore(li, pli.firstSibling);
23373         }
23374         sel.cursorText (li.firstChild);
23375       
23376         this.core.undoManager.addEvent();
23377         this.core.fireEditorEvent(e);
23378
23379         return false;
23380         
23381     
23382         
23383         
23384          
23385     }
23386 };
23387      
23388 /**
23389  * @class Roo.htmleditor.Block
23390  * Base class for html editor blocks - do not use it directly .. extend it..
23391  * @cfg {DomElement} node The node to apply stuff to.
23392  * @cfg {String} friendly_name the name that appears in the context bar about this block
23393  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23394  
23395  * @constructor
23396  * Create a new Filter.
23397  * @param {Object} config Configuration options
23398  */
23399
23400 Roo.htmleditor.Block  = function(cfg)
23401 {
23402     // do nothing .. should not be called really.
23403 }
23404 /**
23405  * factory method to get the block from an element (using cache if necessary)
23406  * @static
23407  * @param {HtmlElement} the dom element
23408  */
23409 Roo.htmleditor.Block.factory = function(node)
23410 {
23411     var cc = Roo.htmleditor.Block.cache;
23412     var id = Roo.get(node).id;
23413     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23414         Roo.htmleditor.Block.cache[id].readElement(node);
23415         return Roo.htmleditor.Block.cache[id];
23416     }
23417     var db  = node.getAttribute('data-block');
23418     if (!db) {
23419         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23420     }
23421     var cls = Roo.htmleditor['Block' + db];
23422     if (typeof(cls) == 'undefined') {
23423         //Roo.log(node.getAttribute('data-block'));
23424         Roo.log("OOps missing block : " + 'Block' + db);
23425         return false;
23426     }
23427     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23428     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23429 };
23430
23431 /**
23432  * initalize all Elements from content that are 'blockable'
23433  * @static
23434  * @param the body element
23435  */
23436 Roo.htmleditor.Block.initAll = function(body, type)
23437 {
23438     if (typeof(type) == 'undefined') {
23439         var ia = Roo.htmleditor.Block.initAll;
23440         ia(body,'table');
23441         ia(body,'td');
23442         ia(body,'figure');
23443         return;
23444     }
23445     Roo.each(Roo.get(body).query(type), function(e) {
23446         Roo.htmleditor.Block.factory(e);    
23447     },this);
23448 };
23449 // question goes here... do we need to clear out this cache sometimes?
23450 // or show we make it relivant to the htmleditor.
23451 Roo.htmleditor.Block.cache = {};
23452
23453 Roo.htmleditor.Block.prototype = {
23454     
23455     node : false,
23456     
23457      // used by context menu
23458     friendly_name : 'Based Block',
23459     
23460     // text for button to delete this element
23461     deleteTitle : false,
23462     
23463     context : false,
23464     /**
23465      * Update a node with values from this object
23466      * @param {DomElement} node
23467      */
23468     updateElement : function(node)
23469     {
23470         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23471     },
23472      /**
23473      * convert to plain HTML for calling insertAtCursor..
23474      */
23475     toHTML : function()
23476     {
23477         return Roo.DomHelper.markup(this.toObject());
23478     },
23479     /**
23480      * used by readEleemnt to extract data from a node
23481      * may need improving as it's pretty basic
23482      
23483      * @param {DomElement} node
23484      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23485      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23486      * @param {String} style the style property - eg. text-align
23487      */
23488     getVal : function(node, tag, attr, style)
23489     {
23490         var n = node;
23491         if (tag !== true && n.tagName != tag.toUpperCase()) {
23492             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23493             // but kiss for now.
23494             n = node.getElementsByTagName(tag).item(0);
23495         }
23496         if (!n) {
23497             return '';
23498         }
23499         if (attr === false) {
23500             return n;
23501         }
23502         if (attr == 'html') {
23503             return n.innerHTML;
23504         }
23505         if (attr == 'style') {
23506             return n.style[style]; 
23507         }
23508         
23509         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23510             
23511     },
23512     /**
23513      * create a DomHelper friendly object - for use with 
23514      * Roo.DomHelper.markup / overwrite / etc..
23515      * (override this)
23516      */
23517     toObject : function()
23518     {
23519         return {};
23520     },
23521       /**
23522      * Read a node that has a 'data-block' property - and extract the values from it.
23523      * @param {DomElement} node - the node
23524      */
23525     readElement : function(node)
23526     {
23527         
23528     } 
23529     
23530     
23531 };
23532
23533  
23534
23535 /**
23536  * @class Roo.htmleditor.BlockFigure
23537  * Block that has an image and a figcaption
23538  * @cfg {String} image_src the url for the image
23539  * @cfg {String} align (left|right) alignment for the block default left
23540  * @cfg {String} caption the text to appear below  (and in the alt tag)
23541  * @cfg {String} caption_display (block|none) display or not the caption
23542  * @cfg {String|number} image_width the width of the image number or %?
23543  * @cfg {String|number} image_height the height of the image number or %?
23544  * 
23545  * @constructor
23546  * Create a new Filter.
23547  * @param {Object} config Configuration options
23548  */
23549
23550 Roo.htmleditor.BlockFigure = function(cfg)
23551 {
23552     if (cfg.node) {
23553         this.readElement(cfg.node);
23554         this.updateElement(cfg.node);
23555     }
23556     Roo.apply(this, cfg);
23557 }
23558 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23559  
23560     
23561     // setable values.
23562     image_src: '',
23563     align: 'center',
23564     caption : '',
23565     caption_display : 'block',
23566     width : '100%',
23567     cls : '',
23568     href: '',
23569     video_url : '',
23570     
23571     // margin: '2%', not used
23572     
23573     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23574
23575     
23576     // used by context menu
23577     friendly_name : 'Image with caption',
23578     deleteTitle : "Delete Image and Caption",
23579     
23580     contextMenu : function(toolbar)
23581     {
23582         
23583         var block = function() {
23584             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23585         };
23586         
23587         
23588         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23589         
23590         var syncValue = toolbar.editorcore.syncValue;
23591         
23592         var fields = {};
23593         
23594         return [
23595              {
23596                 xtype : 'TextItem',
23597                 text : "Source: ",
23598                 xns : rooui.Toolbar  //Boostrap?
23599             },
23600             {
23601                 xtype : 'Button',
23602                 text: 'Change Image URL',
23603                  
23604                 listeners : {
23605                     click: function (btn, state)
23606                     {
23607                         var b = block();
23608                         
23609                         Roo.MessageBox.show({
23610                             title : "Image Source URL",
23611                             msg : "Enter the url for the image",
23612                             buttons: Roo.MessageBox.OKCANCEL,
23613                             fn: function(btn, val){
23614                                 if (btn != 'ok') {
23615                                     return;
23616                                 }
23617                                 b.image_src = val;
23618                                 b.updateElement();
23619                                 syncValue();
23620                                 toolbar.editorcore.onEditorEvent();
23621                             },
23622                             minWidth:250,
23623                             prompt:true,
23624                             //multiline: multiline,
23625                             modal : true,
23626                             value : b.image_src
23627                         });
23628                     }
23629                 },
23630                 xns : rooui.Toolbar
23631             },
23632          
23633             {
23634                 xtype : 'Button',
23635                 text: 'Change Link URL',
23636                  
23637                 listeners : {
23638                     click: function (btn, state)
23639                     {
23640                         var b = block();
23641                         
23642                         Roo.MessageBox.show({
23643                             title : "Link URL",
23644                             msg : "Enter the url for the link - leave blank to have no link",
23645                             buttons: Roo.MessageBox.OKCANCEL,
23646                             fn: function(btn, val){
23647                                 if (btn != 'ok') {
23648                                     return;
23649                                 }
23650                                 b.href = val;
23651                                 b.updateElement();
23652                                 syncValue();
23653                                 toolbar.editorcore.onEditorEvent();
23654                             },
23655                             minWidth:250,
23656                             prompt:true,
23657                             //multiline: multiline,
23658                             modal : true,
23659                             value : b.href
23660                         });
23661                     }
23662                 },
23663                 xns : rooui.Toolbar
23664             },
23665             {
23666                 xtype : 'Button',
23667                 text: 'Show Video URL',
23668                  
23669                 listeners : {
23670                     click: function (btn, state)
23671                     {
23672                         Roo.MessageBox.alert("Video URL",
23673                             block().video_url == '' ? 'This image is not linked ot a video' :
23674                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23675                     }
23676                 },
23677                 xns : rooui.Toolbar
23678             },
23679             
23680             
23681             {
23682                 xtype : 'TextItem',
23683                 text : "Width: ",
23684                 xns : rooui.Toolbar  //Boostrap?
23685             },
23686             {
23687                 xtype : 'ComboBox',
23688                 allowBlank : false,
23689                 displayField : 'val',
23690                 editable : true,
23691                 listWidth : 100,
23692                 triggerAction : 'all',
23693                 typeAhead : true,
23694                 valueField : 'val',
23695                 width : 70,
23696                 name : 'width',
23697                 listeners : {
23698                     select : function (combo, r, index)
23699                     {
23700                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23701                         var b = block();
23702                         b.width = r.get('val');
23703                         b.updateElement();
23704                         syncValue();
23705                         toolbar.editorcore.onEditorEvent();
23706                     }
23707                 },
23708                 xns : rooui.form,
23709                 store : {
23710                     xtype : 'SimpleStore',
23711                     data : [
23712                         ['100%'],
23713                         ['80%'],
23714                         ['50%'],
23715                         ['20%'],
23716                         ['10%']
23717                     ],
23718                     fields : [ 'val'],
23719                     xns : Roo.data
23720                 }
23721             },
23722             {
23723                 xtype : 'TextItem',
23724                 text : "Align: ",
23725                 xns : rooui.Toolbar  //Boostrap?
23726             },
23727             {
23728                 xtype : 'ComboBox',
23729                 allowBlank : false,
23730                 displayField : 'val',
23731                 editable : true,
23732                 listWidth : 100,
23733                 triggerAction : 'all',
23734                 typeAhead : true,
23735                 valueField : 'val',
23736                 width : 70,
23737                 name : 'align',
23738                 listeners : {
23739                     select : function (combo, r, index)
23740                     {
23741                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23742                         var b = block();
23743                         b.align = r.get('val');
23744                         b.updateElement();
23745                         syncValue();
23746                         toolbar.editorcore.onEditorEvent();
23747                     }
23748                 },
23749                 xns : rooui.form,
23750                 store : {
23751                     xtype : 'SimpleStore',
23752                     data : [
23753                         ['left'],
23754                         ['right'],
23755                         ['center']
23756                     ],
23757                     fields : [ 'val'],
23758                     xns : Roo.data
23759                 }
23760             },
23761             
23762             
23763             {
23764                 xtype : 'Button',
23765                 text: 'Hide Caption',
23766                 name : 'caption_display',
23767                 pressed : false,
23768                 enableToggle : true,
23769                 setValue : function(v) {
23770                     // this trigger toggle.
23771                      
23772                     this.setText(v ? "Hide Caption" : "Show Caption");
23773                     this.setPressed(v != 'block');
23774                 },
23775                 listeners : {
23776                     toggle: function (btn, state)
23777                     {
23778                         var b  = block();
23779                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23780                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23781                         b.updateElement();
23782                         syncValue();
23783                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23784                         toolbar.editorcore.onEditorEvent();
23785                     }
23786                 },
23787                 xns : rooui.Toolbar
23788             }
23789         ];
23790         
23791     },
23792     /**
23793      * create a DomHelper friendly object - for use with
23794      * Roo.DomHelper.markup / overwrite / etc..
23795      */
23796     toObject : function()
23797     {
23798         var d = document.createElement('div');
23799         d.innerHTML = this.caption;
23800         
23801         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
23802         
23803         var iw = this.align == 'center' ? this.width : '100%';
23804         var img =   {
23805             tag : 'img',
23806             contenteditable : 'false',
23807             src : this.image_src,
23808             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23809             style: {
23810                 width : iw,
23811                 maxWidth : iw + ' !important', // this is not getting rendered?
23812                 margin : m  
23813                 
23814             }
23815         };
23816         /*
23817         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23818                     '<a href="{2}">' + 
23819                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23820                     '</a>' + 
23821                 '</div>',
23822         */
23823                 
23824         if (this.href.length > 0) {
23825             img = {
23826                 tag : 'a',
23827                 href: this.href,
23828                 contenteditable : 'true',
23829                 cn : [
23830                     img
23831                 ]
23832             };
23833         }
23834         
23835         
23836         if (this.video_url.length > 0) {
23837             img = {
23838                 tag : 'div',
23839                 cls : this.cls,
23840                 frameborder : 0,
23841                 allowfullscreen : true,
23842                 width : 420,  // these are for video tricks - that we replace the outer
23843                 height : 315,
23844                 src : this.video_url,
23845                 cn : [
23846                     img
23847                 ]
23848             };
23849         }
23850         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
23851         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
23852         
23853   
23854         var ret =   {
23855             tag: 'figure',
23856             'data-block' : 'Figure',
23857             'data-width' : this.width, 
23858             contenteditable : 'false',
23859             
23860             style : {
23861                 display: 'block',
23862                 float :  this.align ,
23863                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
23864                 width : this.align == 'center' ? '100%' : this.width,
23865                 margin:  '0px',
23866                 padding: this.align == 'center' ? '0' : '0 10px' ,
23867                 textAlign : this.align   // seems to work for email..
23868                 
23869             },
23870            
23871             
23872             align : this.align,
23873             cn : [
23874                 img,
23875               
23876                 {
23877                     tag: 'figcaption',
23878                     'data-display' : this.caption_display,
23879                     style : {
23880                         textAlign : 'left',
23881                         fontSize : '16px',
23882                         lineHeight : '24px',
23883                         display : this.caption_display,
23884                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
23885                         margin: m,
23886                         width: this.align == 'center' ?  this.width : '100%' 
23887                     
23888                          
23889                     },
23890                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23891                     cn : [
23892                         {
23893                             tag: 'div',
23894                             style  : {
23895                                 marginTop : '16px',
23896                                 textAlign : 'left'
23897                             },
23898                             align: 'left',
23899                             cn : [
23900                                 {
23901                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23902                                     tag : 'i',
23903                                     contenteditable : true,
23904                                     html : captionhtml
23905                                 }
23906                                 
23907                             ]
23908                         }
23909                         
23910                     ]
23911                     
23912                 }
23913             ]
23914         };
23915         return ret;
23916          
23917     },
23918     
23919     readElement : function(node)
23920     {
23921         // this should not really come from the link...
23922         this.video_url = this.getVal(node, 'div', 'src');
23923         this.cls = this.getVal(node, 'div', 'class');
23924         this.href = this.getVal(node, 'a', 'href');
23925         
23926         
23927         this.image_src = this.getVal(node, 'img', 'src');
23928          
23929         this.align = this.getVal(node, 'figure', 'align');
23930         var figcaption = this.getVal(node, 'figcaption', false);
23931         if (figcaption !== '') {
23932             this.caption = this.getVal(figcaption, 'i', 'html');
23933         }
23934         
23935
23936         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
23937         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23938         this.width = this.getVal(node, true, 'data-width');
23939         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23940         
23941     },
23942     removeNode : function()
23943     {
23944         return this.node;
23945     }
23946     
23947   
23948    
23949      
23950     
23951     
23952     
23953     
23954 })
23955
23956  
23957
23958 /**
23959  * @class Roo.htmleditor.BlockTable
23960  * Block that manages a table
23961  * 
23962  * @constructor
23963  * Create a new Filter.
23964  * @param {Object} config Configuration options
23965  */
23966
23967 Roo.htmleditor.BlockTable = function(cfg)
23968 {
23969     if (cfg.node) {
23970         this.readElement(cfg.node);
23971         this.updateElement(cfg.node);
23972     }
23973     Roo.apply(this, cfg);
23974     if (!cfg.node) {
23975         this.rows = [];
23976         for(var r = 0; r < this.no_row; r++) {
23977             this.rows[r] = [];
23978             for(var c = 0; c < this.no_col; c++) {
23979                 this.rows[r][c] = this.emptyCell();
23980             }
23981         }
23982     }
23983     
23984     
23985 }
23986 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23987  
23988     rows : false,
23989     no_col : 1,
23990     no_row : 1,
23991     
23992     
23993     width: '100%',
23994     
23995     // used by context menu
23996     friendly_name : 'Table',
23997     deleteTitle : 'Delete Table',
23998     // context menu is drawn once..
23999     
24000     contextMenu : function(toolbar)
24001     {
24002         
24003         var block = function() {
24004             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24005         };
24006         
24007         
24008         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24009         
24010         var syncValue = toolbar.editorcore.syncValue;
24011         
24012         var fields = {};
24013         
24014         return [
24015             {
24016                 xtype : 'TextItem',
24017                 text : "Width: ",
24018                 xns : rooui.Toolbar  //Boostrap?
24019             },
24020             {
24021                 xtype : 'ComboBox',
24022                 allowBlank : false,
24023                 displayField : 'val',
24024                 editable : true,
24025                 listWidth : 100,
24026                 triggerAction : 'all',
24027                 typeAhead : true,
24028                 valueField : 'val',
24029                 width : 100,
24030                 name : 'width',
24031                 listeners : {
24032                     select : function (combo, r, index)
24033                     {
24034                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24035                         var b = block();
24036                         b.width = r.get('val');
24037                         b.updateElement();
24038                         syncValue();
24039                         toolbar.editorcore.onEditorEvent();
24040                     }
24041                 },
24042                 xns : rooui.form,
24043                 store : {
24044                     xtype : 'SimpleStore',
24045                     data : [
24046                         ['100%'],
24047                         ['auto']
24048                     ],
24049                     fields : [ 'val'],
24050                     xns : Roo.data
24051                 }
24052             },
24053             // -------- Cols
24054             
24055             {
24056                 xtype : 'TextItem',
24057                 text : "Columns: ",
24058                 xns : rooui.Toolbar  //Boostrap?
24059             },
24060          
24061             {
24062                 xtype : 'Button',
24063                 text: '-',
24064                 listeners : {
24065                     click : function (_self, e)
24066                     {
24067                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24068                         block().removeColumn();
24069                         syncValue();
24070                         toolbar.editorcore.onEditorEvent();
24071                     }
24072                 },
24073                 xns : rooui.Toolbar
24074             },
24075             {
24076                 xtype : 'Button',
24077                 text: '+',
24078                 listeners : {
24079                     click : function (_self, e)
24080                     {
24081                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24082                         block().addColumn();
24083                         syncValue();
24084                         toolbar.editorcore.onEditorEvent();
24085                     }
24086                 },
24087                 xns : rooui.Toolbar
24088             },
24089             // -------- ROWS
24090             {
24091                 xtype : 'TextItem',
24092                 text : "Rows: ",
24093                 xns : rooui.Toolbar  //Boostrap?
24094             },
24095          
24096             {
24097                 xtype : 'Button',
24098                 text: '-',
24099                 listeners : {
24100                     click : function (_self, e)
24101                     {
24102                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24103                         block().removeRow();
24104                         syncValue();
24105                         toolbar.editorcore.onEditorEvent();
24106                     }
24107                 },
24108                 xns : rooui.Toolbar
24109             },
24110             {
24111                 xtype : 'Button',
24112                 text: '+',
24113                 listeners : {
24114                     click : function (_self, e)
24115                     {
24116                         block().addRow();
24117                         syncValue();
24118                         toolbar.editorcore.onEditorEvent();
24119                     }
24120                 },
24121                 xns : rooui.Toolbar
24122             },
24123             // -------- ROWS
24124             {
24125                 xtype : 'Button',
24126                 text: 'Reset Column Widths',
24127                 listeners : {
24128                     
24129                     click : function (_self, e)
24130                     {
24131                         block().resetWidths();
24132                         syncValue();
24133                         toolbar.editorcore.onEditorEvent();
24134                     }
24135                 },
24136                 xns : rooui.Toolbar
24137             } 
24138             
24139             
24140             
24141         ];
24142         
24143     },
24144     
24145     
24146   /**
24147      * create a DomHelper friendly object - for use with
24148      * Roo.DomHelper.markup / overwrite / etc..
24149      * ?? should it be called with option to hide all editing features?
24150      */
24151     toObject : function()
24152     {
24153         
24154         var ret = {
24155             tag : 'table',
24156             contenteditable : 'false', // this stops cell selection from picking the table.
24157             'data-block' : 'Table',
24158             style : {
24159                 width:  this.width,
24160                 border : 'solid 1px #000', // ??? hard coded?
24161                 'border-collapse' : 'collapse' 
24162             },
24163             cn : [
24164                 { tag : 'tbody' , cn : [] }
24165             ]
24166         };
24167         
24168         // do we have a head = not really 
24169         var ncols = 0;
24170         Roo.each(this.rows, function( row ) {
24171             var tr = {
24172                 tag: 'tr',
24173                 style : {
24174                     margin: '6px',
24175                     border : 'solid 1px #000',
24176                     textAlign : 'left' 
24177                 },
24178                 cn : [ ]
24179             };
24180             
24181             ret.cn[0].cn.push(tr);
24182             // does the row have any properties? ?? height?
24183             var nc = 0;
24184             Roo.each(row, function( cell ) {
24185                 
24186                 var td = {
24187                     tag : 'td',
24188                     contenteditable :  'true',
24189                     'data-block' : 'Td',
24190                     html : cell.html,
24191                     style : cell.style
24192                 };
24193                 if (cell.colspan > 1) {
24194                     td.colspan = cell.colspan ;
24195                     nc += cell.colspan;
24196                 } else {
24197                     nc++;
24198                 }
24199                 if (cell.rowspan > 1) {
24200                     td.rowspan = cell.rowspan ;
24201                 }
24202                 
24203                 
24204                 // widths ?
24205                 tr.cn.push(td);
24206                     
24207                 
24208             }, this);
24209             ncols = Math.max(nc, ncols);
24210             
24211             
24212         }, this);
24213         // add the header row..
24214         
24215         ncols++;
24216          
24217         
24218         return ret;
24219          
24220     },
24221     
24222     readElement : function(node)
24223     {
24224         node  = node ? node : this.node ;
24225         this.width = this.getVal(node, true, 'style', 'width') || '100%';
24226         
24227         this.rows = [];
24228         this.no_row = 0;
24229         var trs = Array.from(node.rows);
24230         trs.forEach(function(tr) {
24231             var row =  [];
24232             this.rows.push(row);
24233             
24234             this.no_row++;
24235             var no_column = 0;
24236             Array.from(tr.cells).forEach(function(td) {
24237                 
24238                 var add = {
24239                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24240                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24241                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24242                     html : td.innerHTML
24243                 };
24244                 no_column += add.colspan;
24245                      
24246                 
24247                 row.push(add);
24248                 
24249                 
24250             },this);
24251             this.no_col = Math.max(this.no_col, no_column);
24252             
24253             
24254         },this);
24255         
24256         
24257     },
24258     normalizeRows: function()
24259     {
24260         var ret= [];
24261         var rid = -1;
24262         this.rows.forEach(function(row) {
24263             rid++;
24264             ret[rid] = [];
24265             row = this.normalizeRow(row);
24266             var cid = 0;
24267             row.forEach(function(c) {
24268                 while (typeof(ret[rid][cid]) != 'undefined') {
24269                     cid++;
24270                 }
24271                 if (typeof(ret[rid]) == 'undefined') {
24272                     ret[rid] = [];
24273                 }
24274                 ret[rid][cid] = c;
24275                 c.row = rid;
24276                 c.col = cid;
24277                 if (c.rowspan < 2) {
24278                     return;
24279                 }
24280                 
24281                 for(var i = 1 ;i < c.rowspan; i++) {
24282                     if (typeof(ret[rid+i]) == 'undefined') {
24283                         ret[rid+i] = [];
24284                     }
24285                     ret[rid+i][cid] = c;
24286                 }
24287             });
24288         }, this);
24289         return ret;
24290     
24291     },
24292     
24293     normalizeRow: function(row)
24294     {
24295         var ret= [];
24296         row.forEach(function(c) {
24297             if (c.colspan < 2) {
24298                 ret.push(c);
24299                 return;
24300             }
24301             for(var i =0 ;i < c.colspan; i++) {
24302                 ret.push(c);
24303             }
24304         });
24305         return ret;
24306     
24307     },
24308     
24309     deleteColumn : function(sel)
24310     {
24311         if (!sel || sel.type != 'col') {
24312             return;
24313         }
24314         if (this.no_col < 2) {
24315             return;
24316         }
24317         
24318         this.rows.forEach(function(row) {
24319             var cols = this.normalizeRow(row);
24320             var col = cols[sel.col];
24321             if (col.colspan > 1) {
24322                 col.colspan --;
24323             } else {
24324                 row.remove(col);
24325             }
24326             
24327         }, this);
24328         this.no_col--;
24329         
24330     },
24331     removeColumn : function()
24332     {
24333         this.deleteColumn({
24334             type: 'col',
24335             col : this.no_col-1
24336         });
24337         this.updateElement();
24338     },
24339     
24340      
24341     addColumn : function()
24342     {
24343         
24344         this.rows.forEach(function(row) {
24345             row.push(this.emptyCell());
24346            
24347         }, this);
24348         this.updateElement();
24349     },
24350     
24351     deleteRow : function(sel)
24352     {
24353         if (!sel || sel.type != 'row') {
24354             return;
24355         }
24356         
24357         if (this.no_row < 2) {
24358             return;
24359         }
24360         
24361         var rows = this.normalizeRows();
24362         
24363         
24364         rows[sel.row].forEach(function(col) {
24365             if (col.rowspan > 1) {
24366                 col.rowspan--;
24367             } else {
24368                 col.remove = 1; // flage it as removed.
24369             }
24370             
24371         }, this);
24372         var newrows = [];
24373         this.rows.forEach(function(row) {
24374             newrow = [];
24375             row.forEach(function(c) {
24376                 if (typeof(c.remove) == 'undefined') {
24377                     newrow.push(c);
24378                 }
24379                 
24380             });
24381             if (newrow.length > 0) {
24382                 newrows.push(row);
24383             }
24384         });
24385         this.rows =  newrows;
24386         
24387         
24388         
24389         this.no_row--;
24390         this.updateElement();
24391         
24392     },
24393     removeRow : function()
24394     {
24395         this.deleteRow({
24396             type: 'row',
24397             row : this.no_row-1
24398         });
24399         
24400     },
24401     
24402      
24403     addRow : function()
24404     {
24405         
24406         var row = [];
24407         for (var i = 0; i < this.no_col; i++ ) {
24408             
24409             row.push(this.emptyCell());
24410            
24411         }
24412         this.rows.push(row);
24413         this.updateElement();
24414         
24415     },
24416      
24417     // the default cell object... at present...
24418     emptyCell : function() {
24419         return (new Roo.htmleditor.BlockTd({})).toObject();
24420         
24421      
24422     },
24423     
24424     removeNode : function()
24425     {
24426         return this.node;
24427     },
24428     
24429     
24430     
24431     resetWidths : function()
24432     {
24433         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24434             var nn = Roo.htmleditor.Block.factory(n);
24435             nn.width = '';
24436             nn.updateElement(n);
24437         });
24438     }
24439     
24440     
24441     
24442     
24443 })
24444
24445 /**
24446  *
24447  * editing a TD?
24448  *
24449  * since selections really work on the table cell, then editing really should work from there
24450  *
24451  * The original plan was to support merging etc... - but that may not be needed yet..
24452  *
24453  * So this simple version will support:
24454  *   add/remove cols
24455  *   adjust the width +/-
24456  *   reset the width...
24457  *   
24458  *
24459  */
24460
24461
24462  
24463
24464 /**
24465  * @class Roo.htmleditor.BlockTable
24466  * Block that manages a table
24467  * 
24468  * @constructor
24469  * Create a new Filter.
24470  * @param {Object} config Configuration options
24471  */
24472
24473 Roo.htmleditor.BlockTd = function(cfg)
24474 {
24475     if (cfg.node) {
24476         this.readElement(cfg.node);
24477         this.updateElement(cfg.node);
24478     }
24479     Roo.apply(this, cfg);
24480      
24481     
24482     
24483 }
24484 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24485  
24486     node : false,
24487     
24488     width: '',
24489     textAlign : 'left',
24490     valign : 'top',
24491     
24492     colspan : 1,
24493     rowspan : 1,
24494     
24495     
24496     // used by context menu
24497     friendly_name : 'Table Cell',
24498     deleteTitle : false, // use our customer delete
24499     
24500     // context menu is drawn once..
24501     
24502     contextMenu : function(toolbar)
24503     {
24504         
24505         var cell = function() {
24506             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24507         };
24508         
24509         var table = function() {
24510             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24511         };
24512         
24513         var lr = false;
24514         var saveSel = function()
24515         {
24516             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24517         }
24518         var restoreSel = function()
24519         {
24520             if (lr) {
24521                 (function() {
24522                     toolbar.editorcore.focus();
24523                     var cr = toolbar.editorcore.getSelection();
24524                     cr.removeAllRanges();
24525                     cr.addRange(lr);
24526                     toolbar.editorcore.onEditorEvent();
24527                 }).defer(10, this);
24528                 
24529                 
24530             }
24531         }
24532         
24533         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24534         
24535         var syncValue = toolbar.editorcore.syncValue;
24536         
24537         var fields = {};
24538         
24539         return [
24540             {
24541                 xtype : 'Button',
24542                 text : 'Edit Table',
24543                 listeners : {
24544                     click : function() {
24545                         var t = toolbar.tb.selectedNode.closest('table');
24546                         toolbar.editorcore.selectNode(t);
24547                         toolbar.editorcore.onEditorEvent();                        
24548                     }
24549                 }
24550                 
24551             },
24552               
24553            
24554              
24555             {
24556                 xtype : 'TextItem',
24557                 text : "Column Width: ",
24558                  xns : rooui.Toolbar 
24559                
24560             },
24561             {
24562                 xtype : 'Button',
24563                 text: '-',
24564                 listeners : {
24565                     click : function (_self, e)
24566                     {
24567                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24568                         cell().shrinkColumn();
24569                         syncValue();
24570                          toolbar.editorcore.onEditorEvent();
24571                     }
24572                 },
24573                 xns : rooui.Toolbar
24574             },
24575             {
24576                 xtype : 'Button',
24577                 text: '+',
24578                 listeners : {
24579                     click : function (_self, e)
24580                     {
24581                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24582                         cell().growColumn();
24583                         syncValue();
24584                         toolbar.editorcore.onEditorEvent();
24585                     }
24586                 },
24587                 xns : rooui.Toolbar
24588             },
24589             
24590             {
24591                 xtype : 'TextItem',
24592                 text : "Vertical Align: ",
24593                 xns : rooui.Toolbar  //Boostrap?
24594             },
24595             {
24596                 xtype : 'ComboBox',
24597                 allowBlank : false,
24598                 displayField : 'val',
24599                 editable : true,
24600                 listWidth : 100,
24601                 triggerAction : 'all',
24602                 typeAhead : true,
24603                 valueField : 'val',
24604                 width : 100,
24605                 name : 'valign',
24606                 listeners : {
24607                     select : function (combo, r, index)
24608                     {
24609                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24610                         var b = cell();
24611                         b.valign = r.get('val');
24612                         b.updateElement();
24613                         syncValue();
24614                         toolbar.editorcore.onEditorEvent();
24615                     }
24616                 },
24617                 xns : rooui.form,
24618                 store : {
24619                     xtype : 'SimpleStore',
24620                     data : [
24621                         ['top'],
24622                         ['middle'],
24623                         ['bottom'] // there are afew more... 
24624                     ],
24625                     fields : [ 'val'],
24626                     xns : Roo.data
24627                 }
24628             },
24629             
24630             {
24631                 xtype : 'TextItem',
24632                 text : "Merge Cells: ",
24633                  xns : rooui.Toolbar 
24634                
24635             },
24636             
24637             
24638             {
24639                 xtype : 'Button',
24640                 text: 'Right',
24641                 listeners : {
24642                     click : function (_self, e)
24643                     {
24644                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24645                         cell().mergeRight();
24646                         //block().growColumn();
24647                         syncValue();
24648                         toolbar.editorcore.onEditorEvent();
24649                     }
24650                 },
24651                 xns : rooui.Toolbar
24652             },
24653              
24654             {
24655                 xtype : 'Button',
24656                 text: 'Below',
24657                 listeners : {
24658                     click : function (_self, e)
24659                     {
24660                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24661                         cell().mergeBelow();
24662                         //block().growColumn();
24663                         syncValue();
24664                         toolbar.editorcore.onEditorEvent();
24665                     }
24666                 },
24667                 xns : rooui.Toolbar
24668             },
24669             {
24670                 xtype : 'TextItem',
24671                 text : "| ",
24672                  xns : rooui.Toolbar 
24673                
24674             },
24675             
24676             {
24677                 xtype : 'Button',
24678                 text: 'Split',
24679                 listeners : {
24680                     click : function (_self, e)
24681                     {
24682                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24683                         cell().split();
24684                         syncValue();
24685                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24686                         toolbar.editorcore.onEditorEvent();
24687                                              
24688                     }
24689                 },
24690                 xns : rooui.Toolbar
24691             },
24692             {
24693                 xtype : 'Fill',
24694                 xns : rooui.Toolbar 
24695                
24696             },
24697         
24698           
24699             {
24700                 xtype : 'Button',
24701                 text: 'Delete',
24702                  
24703                 xns : rooui.Toolbar,
24704                 menu : {
24705                     xtype : 'Menu',
24706                     xns : rooui.menu,
24707                     items : [
24708                         {
24709                             xtype : 'Item',
24710                             html: 'Column',
24711                             listeners : {
24712                                 click : function (_self, e)
24713                                 {
24714                                     var t = table();
24715                                     
24716                                     cell().deleteColumn();
24717                                     syncValue();
24718                                     toolbar.editorcore.selectNode(t.node);
24719                                     toolbar.editorcore.onEditorEvent();   
24720                                 }
24721                             },
24722                             xns : rooui.menu
24723                         },
24724                         {
24725                             xtype : 'Item',
24726                             html: 'Row',
24727                             listeners : {
24728                                 click : function (_self, e)
24729                                 {
24730                                     var t = table();
24731                                     cell().deleteRow();
24732                                     syncValue();
24733                                     
24734                                     toolbar.editorcore.selectNode(t.node);
24735                                     toolbar.editorcore.onEditorEvent();   
24736                                                          
24737                                 }
24738                             },
24739                             xns : rooui.menu
24740                         },
24741                        {
24742                             xtype : 'Separator',
24743                             xns : rooui.menu
24744                         },
24745                         {
24746                             xtype : 'Item',
24747                             html: 'Table',
24748                             listeners : {
24749                                 click : function (_self, e)
24750                                 {
24751                                     var t = table();
24752                                     var nn = t.node.nextSibling || t.node.previousSibling;
24753                                     t.node.parentNode.removeChild(t.node);
24754                                     if (nn) { 
24755                                         toolbar.editorcore.selectNode(nn, true);
24756                                     }
24757                                     toolbar.editorcore.onEditorEvent();   
24758                                                          
24759                                 }
24760                             },
24761                             xns : rooui.menu
24762                         }
24763                     ]
24764                 }
24765             }
24766             
24767             // align... << fixme
24768             
24769         ];
24770         
24771     },
24772     
24773     
24774   /**
24775      * create a DomHelper friendly object - for use with
24776      * Roo.DomHelper.markup / overwrite / etc..
24777      * ?? should it be called with option to hide all editing features?
24778      */
24779  /**
24780      * create a DomHelper friendly object - for use with
24781      * Roo.DomHelper.markup / overwrite / etc..
24782      * ?? should it be called with option to hide all editing features?
24783      */
24784     toObject : function()
24785     {
24786         
24787         var ret = {
24788             tag : 'td',
24789             contenteditable : 'true', // this stops cell selection from picking the table.
24790             'data-block' : 'Td',
24791             valign : this.valign,
24792             style : {  
24793                 'text-align' :  this.textAlign,
24794                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24795                 'border-collapse' : 'collapse',
24796                 padding : '6px', // 8 for desktop / 4 for mobile
24797                 'vertical-align': this.valign
24798             },
24799             html : this.html
24800         };
24801         if (this.width != '') {
24802             ret.width = this.width;
24803             ret.style.width = this.width;
24804         }
24805         
24806         
24807         if (this.colspan > 1) {
24808             ret.colspan = this.colspan ;
24809         } 
24810         if (this.rowspan > 1) {
24811             ret.rowspan = this.rowspan ;
24812         }
24813         
24814            
24815         
24816         return ret;
24817          
24818     },
24819     
24820     readElement : function(node)
24821     {
24822         node  = node ? node : this.node ;
24823         this.width = node.style.width;
24824         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24825         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24826         this.html = node.innerHTML;
24827         
24828         
24829     },
24830      
24831     // the default cell object... at present...
24832     emptyCell : function() {
24833         return {
24834             colspan :  1,
24835             rowspan :  1,
24836             textAlign : 'left',
24837             html : "&nbsp;" // is this going to be editable now?
24838         };
24839      
24840     },
24841     
24842     removeNode : function()
24843     {
24844         return this.node.closest('table');
24845          
24846     },
24847     
24848     cellData : false,
24849     
24850     colWidths : false,
24851     
24852     toTableArray  : function()
24853     {
24854         var ret = [];
24855         var tab = this.node.closest('tr').closest('table');
24856         Array.from(tab.rows).forEach(function(r, ri){
24857             ret[ri] = [];
24858         });
24859         var rn = 0;
24860         this.colWidths = [];
24861         var all_auto = true;
24862         Array.from(tab.rows).forEach(function(r, ri){
24863             
24864             var cn = 0;
24865             Array.from(r.cells).forEach(function(ce, ci){
24866                 var c =  {
24867                     cell : ce,
24868                     row : rn,
24869                     col: cn,
24870                     colspan : ce.colSpan,
24871                     rowspan : ce.rowSpan
24872                 };
24873                 if (ce.isEqualNode(this.node)) {
24874                     this.cellData = c;
24875                 }
24876                 // if we have been filled up by a row?
24877                 if (typeof(ret[rn][cn]) != 'undefined') {
24878                     while(typeof(ret[rn][cn]) != 'undefined') {
24879                         cn++;
24880                     }
24881                     c.col = cn;
24882                 }
24883                 
24884                 if (typeof(this.colWidths[cn]) == 'undefined') {
24885                     this.colWidths[cn] =   ce.style.width;
24886                     if (this.colWidths[cn] != '') {
24887                         all_auto = false;
24888                     }
24889                 }
24890                 
24891                 
24892                 if (c.colspan < 2 && c.rowspan < 2 ) {
24893                     ret[rn][cn] = c;
24894                     cn++;
24895                     return;
24896                 }
24897                 for(var j = 0; j < c.rowspan; j++) {
24898                     if (typeof(ret[rn+j]) == 'undefined') {
24899                         continue; // we have a problem..
24900                     }
24901                     ret[rn+j][cn] = c;
24902                     for(var i = 0; i < c.colspan; i++) {
24903                         ret[rn+j][cn+i] = c;
24904                     }
24905                 }
24906                 
24907                 cn += c.colspan;
24908             }, this);
24909             rn++;
24910         }, this);
24911         
24912         // initalize widths.?
24913         // either all widths or no widths..
24914         if (all_auto) {
24915             this.colWidths[0] = false; // no widths flag.
24916         }
24917         
24918         
24919         return ret;
24920         
24921     },
24922     
24923     
24924     
24925     
24926     mergeRight: function()
24927     {
24928          
24929         // get the contents of the next cell along..
24930         var tr = this.node.closest('tr');
24931         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24932         if (i >= tr.childNodes.length - 1) {
24933             return; // no cells on right to merge with.
24934         }
24935         var table = this.toTableArray();
24936         
24937         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24938             return; // nothing right?
24939         }
24940         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24941         // right cell - must be same rowspan and on the same row.
24942         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24943             return; // right hand side is not same rowspan.
24944         }
24945         
24946         
24947         
24948         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24949         tr.removeChild(rc.cell);
24950         this.colspan += rc.colspan;
24951         this.node.setAttribute('colspan', this.colspan);
24952
24953     },
24954     
24955     
24956     mergeBelow : function()
24957     {
24958         var table = this.toTableArray();
24959         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24960             return; // no row below
24961         }
24962         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24963             return; // nothing right?
24964         }
24965         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24966         
24967         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24968             return; // right hand side is not same rowspan.
24969         }
24970         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24971         rc.cell.parentNode.removeChild(rc.cell);
24972         this.rowspan += rc.rowspan;
24973         this.node.setAttribute('rowspan', this.rowspan);
24974     },
24975     
24976     split: function()
24977     {
24978         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24979             return;
24980         }
24981         var table = this.toTableArray();
24982         var cd = this.cellData;
24983         this.rowspan = 1;
24984         this.colspan = 1;
24985         
24986         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24987             
24988             
24989             
24990             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24991                 if (r == cd.row && c == cd.col) {
24992                     this.node.removeAttribute('rowspan');
24993                     this.node.removeAttribute('colspan');
24994                     continue;
24995                 }
24996                  
24997                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24998                 ntd.removeAttribute('id'); //
24999                 //ntd.style.width  = '';
25000                 ntd.innerHTML = '';
25001                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
25002             }
25003             
25004         }
25005         this.redrawAllCells(table);
25006         
25007          
25008         
25009     },
25010     
25011     
25012     
25013     redrawAllCells: function(table)
25014     {
25015         
25016          
25017         var tab = this.node.closest('tr').closest('table');
25018         var ctr = tab.rows[0].parentNode;
25019         Array.from(tab.rows).forEach(function(r, ri){
25020             
25021             Array.from(r.cells).forEach(function(ce, ci){
25022                 ce.parentNode.removeChild(ce);
25023             });
25024             r.parentNode.removeChild(r);
25025         });
25026         for(var r = 0 ; r < table.length; r++) {
25027             var re = tab.rows[r];
25028             
25029             var re = tab.ownerDocument.createElement('tr');
25030             ctr.appendChild(re);
25031             for(var c = 0 ; c < table[r].length; c++) {
25032                 if (table[r][c].cell === false) {
25033                     continue;
25034                 }
25035                 
25036                 re.appendChild(table[r][c].cell);
25037                  
25038                 table[r][c].cell = false;
25039             }
25040         }
25041         
25042     },
25043     updateWidths : function(table)
25044     {
25045         for(var r = 0 ; r < table.length; r++) {
25046            
25047             for(var c = 0 ; c < table[r].length; c++) {
25048                 if (table[r][c].cell === false) {
25049                     continue;
25050                 }
25051                 
25052                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
25053                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25054                     el.width = Math.floor(this.colWidths[c])  +'%';
25055                     el.updateElement(el.node);
25056                 }
25057                 table[r][c].cell = false; // done
25058             }
25059         }
25060     },
25061     normalizeWidths : function(table)
25062     {
25063     
25064         if (this.colWidths[0] === false) {
25065             var nw = 100.0 / this.colWidths.length;
25066             this.colWidths.forEach(function(w,i) {
25067                 this.colWidths[i] = nw;
25068             },this);
25069             return;
25070         }
25071     
25072         var t = 0, missing = [];
25073         
25074         this.colWidths.forEach(function(w,i) {
25075             //if you mix % and
25076             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
25077             var add =  this.colWidths[i];
25078             if (add > 0) {
25079                 t+=add;
25080                 return;
25081             }
25082             missing.push(i);
25083             
25084             
25085         },this);
25086         var nc = this.colWidths.length;
25087         if (missing.length) {
25088             var mult = (nc - missing.length) / (1.0 * nc);
25089             var t = mult * t;
25090             var ew = (100 -t) / (1.0 * missing.length);
25091             this.colWidths.forEach(function(w,i) {
25092                 if (w > 0) {
25093                     this.colWidths[i] = w * mult;
25094                     return;
25095                 }
25096                 
25097                 this.colWidths[i] = ew;
25098             }, this);
25099             // have to make up numbers..
25100              
25101         }
25102         // now we should have all the widths..
25103         
25104     
25105     },
25106     
25107     shrinkColumn : function()
25108     {
25109         var table = this.toTableArray();
25110         this.normalizeWidths(table);
25111         var col = this.cellData.col;
25112         var nw = this.colWidths[col] * 0.8;
25113         if (nw < 5) {
25114             return;
25115         }
25116         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25117         this.colWidths.forEach(function(w,i) {
25118             if (i == col) {
25119                  this.colWidths[i] = nw;
25120                 return;
25121             }
25122             this.colWidths[i] += otherAdd
25123         }, this);
25124         this.updateWidths(table);
25125          
25126     },
25127     growColumn : function()
25128     {
25129         var table = this.toTableArray();
25130         this.normalizeWidths(table);
25131         var col = this.cellData.col;
25132         var nw = this.colWidths[col] * 1.2;
25133         if (nw > 90) {
25134             return;
25135         }
25136         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25137         this.colWidths.forEach(function(w,i) {
25138             if (i == col) {
25139                 this.colWidths[i] = nw;
25140                 return;
25141             }
25142             this.colWidths[i] -= otherSub
25143         }, this);
25144         this.updateWidths(table);
25145          
25146     },
25147     deleteRow : function()
25148     {
25149         // delete this rows 'tr'
25150         // if any of the cells in this row have a rowspan > 1 && row!= this row..
25151         // then reduce the rowspan.
25152         var table = this.toTableArray();
25153         // this.cellData.row;
25154         for (var i =0;i< table[this.cellData.row].length ; i++) {
25155             var c = table[this.cellData.row][i];
25156             if (c.row != this.cellData.row) {
25157                 
25158                 c.rowspan--;
25159                 c.cell.setAttribute('rowspan', c.rowspan);
25160                 continue;
25161             }
25162             if (c.rowspan > 1) {
25163                 c.rowspan--;
25164                 c.cell.setAttribute('rowspan', c.rowspan);
25165             }
25166         }
25167         table.splice(this.cellData.row,1);
25168         this.redrawAllCells(table);
25169         
25170     },
25171     deleteColumn : function()
25172     {
25173         var table = this.toTableArray();
25174         
25175         for (var i =0;i< table.length ; i++) {
25176             var c = table[i][this.cellData.col];
25177             if (c.col != this.cellData.col) {
25178                 table[i][this.cellData.col].colspan--;
25179             } else if (c.colspan > 1) {
25180                 c.colspan--;
25181                 c.cell.setAttribute('colspan', c.colspan);
25182             }
25183             table[i].splice(this.cellData.col,1);
25184         }
25185         
25186         this.redrawAllCells(table);
25187     }
25188     
25189     
25190     
25191     
25192 })
25193
25194 //<script type="text/javascript">
25195
25196 /*
25197  * Based  Ext JS Library 1.1.1
25198  * Copyright(c) 2006-2007, Ext JS, LLC.
25199  * LGPL
25200  *
25201  */
25202  
25203 /**
25204  * @class Roo.HtmlEditorCore
25205  * @extends Roo.Component
25206  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25207  *
25208  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25209  */
25210
25211 Roo.HtmlEditorCore = function(config){
25212     
25213     
25214     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25215     
25216     
25217     this.addEvents({
25218         /**
25219          * @event initialize
25220          * Fires when the editor is fully initialized (including the iframe)
25221          * @param {Roo.HtmlEditorCore} this
25222          */
25223         initialize: true,
25224         /**
25225          * @event activate
25226          * Fires when the editor is first receives the focus. Any insertion must wait
25227          * until after this event.
25228          * @param {Roo.HtmlEditorCore} this
25229          */
25230         activate: true,
25231          /**
25232          * @event beforesync
25233          * Fires before the textarea is updated with content from the editor iframe. Return false
25234          * to cancel the sync.
25235          * @param {Roo.HtmlEditorCore} this
25236          * @param {String} html
25237          */
25238         beforesync: true,
25239          /**
25240          * @event beforepush
25241          * Fires before the iframe editor is updated with content from the textarea. Return false
25242          * to cancel the push.
25243          * @param {Roo.HtmlEditorCore} this
25244          * @param {String} html
25245          */
25246         beforepush: true,
25247          /**
25248          * @event sync
25249          * Fires when the textarea is updated with content from the editor iframe.
25250          * @param {Roo.HtmlEditorCore} this
25251          * @param {String} html
25252          */
25253         sync: true,
25254          /**
25255          * @event push
25256          * Fires when the iframe editor is updated with content from the textarea.
25257          * @param {Roo.HtmlEditorCore} this
25258          * @param {String} html
25259          */
25260         push: true,
25261         
25262         /**
25263          * @event editorevent
25264          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25265          * @param {Roo.HtmlEditorCore} this
25266          */
25267         editorevent: true 
25268          
25269         
25270     });
25271     
25272     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25273     
25274     // defaults : white / black...
25275     this.applyBlacklists();
25276     
25277     
25278     
25279 };
25280
25281
25282 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25283
25284
25285      /**
25286      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25287      */
25288     
25289     owner : false,
25290     
25291      /**
25292      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25293      *                        Roo.resizable.
25294      */
25295     resizable : false,
25296      /**
25297      * @cfg {Number} height (in pixels)
25298      */   
25299     height: 300,
25300    /**
25301      * @cfg {Number} width (in pixels)
25302      */   
25303     width: 500,
25304      /**
25305      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25306      *         if you are doing an email editor, this probably needs disabling, it's designed
25307      */
25308     autoClean: true,
25309     
25310     /**
25311      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25312      */
25313     enableBlocks : true,
25314     /**
25315      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25316      * 
25317      */
25318     stylesheets: false,
25319      /**
25320      * @cfg {String} language default en - language of text (usefull for rtl languages)
25321      * 
25322      */
25323     language: 'en',
25324     
25325     /**
25326      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25327      *          - by default they are stripped - if you are editing email you may need this.
25328      */
25329     allowComments: false,
25330     // id of frame..
25331     frameId: false,
25332     
25333     // private properties
25334     validationEvent : false,
25335     deferHeight: true,
25336     initialized : false,
25337     activated : false,
25338     sourceEditMode : false,
25339     onFocus : Roo.emptyFn,
25340     iframePad:3,
25341     hideMode:'offsets',
25342     
25343     clearUp: true,
25344     
25345     // blacklist + whitelisted elements..
25346     black: false,
25347     white: false,
25348      
25349     bodyCls : '',
25350
25351     
25352     undoManager : false,
25353     /**
25354      * Protected method that will not generally be called directly. It
25355      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25356      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25357      */
25358     getDocMarkup : function(){
25359         // body styles..
25360         var st = '';
25361         
25362         // inherit styels from page...?? 
25363         if (this.stylesheets === false) {
25364             
25365             Roo.get(document.head).select('style').each(function(node) {
25366                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25367             });
25368             
25369             Roo.get(document.head).select('link').each(function(node) { 
25370                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25371             });
25372             
25373         } else if (!this.stylesheets.length) {
25374                 // simple..
25375                 st = '<style type="text/css">' +
25376                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25377                    '</style>';
25378         } else {
25379             for (var i in this.stylesheets) {
25380                 if (typeof(this.stylesheets[i]) != 'string') {
25381                     continue;
25382                 }
25383                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25384             }
25385             
25386         }
25387         
25388         st +=  '<style type="text/css">' +
25389             'IMG { cursor: pointer } ' +
25390         '</style>';
25391         
25392         st += '<meta name="google" content="notranslate">';
25393         
25394         var cls = 'notranslate roo-htmleditor-body';
25395         
25396         if(this.bodyCls.length){
25397             cls += ' ' + this.bodyCls;
25398         }
25399         
25400         return '<html  class="notranslate" translate="no"><head>' + st  +
25401             //<style type="text/css">' +
25402             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25403             //'</style>' +
25404             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25405     },
25406
25407     // private
25408     onRender : function(ct, position)
25409     {
25410         var _t = this;
25411         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25412         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25413         
25414         
25415         this.el.dom.style.border = '0 none';
25416         this.el.dom.setAttribute('tabIndex', -1);
25417         this.el.addClass('x-hidden hide');
25418         
25419         
25420         
25421         if(Roo.isIE){ // fix IE 1px bogus margin
25422             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25423         }
25424        
25425         
25426         this.frameId = Roo.id();
25427         
25428          
25429         
25430         var iframe = this.owner.wrap.createChild({
25431             tag: 'iframe',
25432             cls: 'form-control', // bootstrap..
25433             id: this.frameId,
25434             name: this.frameId,
25435             frameBorder : 'no',
25436             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25437         }, this.el
25438         );
25439         
25440         
25441         this.iframe = iframe.dom;
25442
25443         this.assignDocWin();
25444         
25445         this.doc.designMode = 'on';
25446        
25447         this.doc.open();
25448         this.doc.write(this.getDocMarkup());
25449         this.doc.close();
25450
25451         
25452         var task = { // must defer to wait for browser to be ready
25453             run : function(){
25454                 //console.log("run task?" + this.doc.readyState);
25455                 this.assignDocWin();
25456                 if(this.doc.body || this.doc.readyState == 'complete'){
25457                     try {
25458                         this.doc.designMode="on";
25459                         
25460                     } catch (e) {
25461                         return;
25462                     }
25463                     Roo.TaskMgr.stop(task);
25464                     this.initEditor.defer(10, this);
25465                 }
25466             },
25467             interval : 10,
25468             duration: 10000,
25469             scope: this
25470         };
25471         Roo.TaskMgr.start(task);
25472
25473     },
25474
25475     // private
25476     onResize : function(w, h)
25477     {
25478          Roo.log('resize: ' +w + ',' + h );
25479         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25480         if(!this.iframe){
25481             return;
25482         }
25483         if(typeof w == 'number'){
25484             
25485             this.iframe.style.width = w + 'px';
25486         }
25487         if(typeof h == 'number'){
25488             
25489             this.iframe.style.height = h + 'px';
25490             if(this.doc){
25491                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25492             }
25493         }
25494         
25495     },
25496
25497     /**
25498      * Toggles the editor between standard and source edit mode.
25499      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25500      */
25501     toggleSourceEdit : function(sourceEditMode){
25502         
25503         this.sourceEditMode = sourceEditMode === true;
25504         
25505         if(this.sourceEditMode){
25506  
25507             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25508             
25509         }else{
25510             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25511             //this.iframe.className = '';
25512             this.deferFocus();
25513         }
25514         //this.setSize(this.owner.wrap.getSize());
25515         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25516     },
25517
25518     
25519   
25520
25521     /**
25522      * Protected method that will not generally be called directly. If you need/want
25523      * custom HTML cleanup, this is the method you should override.
25524      * @param {String} html The HTML to be cleaned
25525      * return {String} The cleaned HTML
25526      */
25527     cleanHtml : function(html)
25528     {
25529         html = String(html);
25530         if(html.length > 5){
25531             if(Roo.isSafari){ // strip safari nonsense
25532                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25533             }
25534         }
25535         if(html == '&nbsp;'){
25536             html = '';
25537         }
25538         return html;
25539     },
25540
25541     /**
25542      * HTML Editor -> Textarea
25543      * Protected method that will not generally be called directly. Syncs the contents
25544      * of the editor iframe with the textarea.
25545      */
25546     syncValue : function()
25547     {
25548         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25549         if(this.initialized){
25550             
25551             if (this.undoManager) {
25552                 this.undoManager.addEvent();
25553             }
25554
25555             
25556             var bd = (this.doc.body || this.doc.documentElement);
25557            
25558             
25559             var sel = this.win.getSelection();
25560             
25561             var div = document.createElement('div');
25562             div.innerHTML = bd.innerHTML;
25563             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25564             if (gtx.length > 0) {
25565                 var rm = gtx.item(0).parentNode;
25566                 rm.parentNode.removeChild(rm);
25567             }
25568             
25569            
25570             if (this.enableBlocks) {
25571                 new Roo.htmleditor.FilterBlock({ node : div });
25572             }
25573             //?? tidy?
25574             var tidy = new Roo.htmleditor.TidySerializer({
25575                 inner:  true
25576             });
25577             var html  = tidy.serialize(div);
25578             
25579             
25580             if(Roo.isSafari){
25581                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25582                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25583                 if(m && m[1]){
25584                     html = '<div style="'+m[0]+'">' + html + '</div>';
25585                 }
25586             }
25587             html = this.cleanHtml(html);
25588             // fix up the special chars.. normaly like back quotes in word...
25589             // however we do not want to do this with chinese..
25590             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25591                 
25592                 var cc = match.charCodeAt();
25593
25594                 // Get the character value, handling surrogate pairs
25595                 if (match.length == 2) {
25596                     // It's a surrogate pair, calculate the Unicode code point
25597                     var high = match.charCodeAt(0) - 0xD800;
25598                     var low  = match.charCodeAt(1) - 0xDC00;
25599                     cc = (high * 0x400) + low + 0x10000;
25600                 }  else if (
25601                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25602                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25603                     (cc >= 0xf900 && cc < 0xfb00 )
25604                 ) {
25605                         return match;
25606                 }  
25607          
25608                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25609                 return "&#" + cc + ";";
25610                 
25611                 
25612             });
25613             
25614             
25615              
25616             if(this.owner.fireEvent('beforesync', this, html) !== false){
25617                 this.el.dom.value = html;
25618                 this.owner.fireEvent('sync', this, html);
25619             }
25620         }
25621     },
25622
25623     /**
25624      * TEXTAREA -> EDITABLE
25625      * Protected method that will not generally be called directly. Pushes the value of the textarea
25626      * into the iframe editor.
25627      */
25628     pushValue : function()
25629     {
25630         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25631         if(this.initialized){
25632             var v = this.el.dom.value.trim();
25633             
25634             
25635             if(this.owner.fireEvent('beforepush', this, v) !== false){
25636                 var d = (this.doc.body || this.doc.documentElement);
25637                 d.innerHTML = v;
25638                  
25639                 this.el.dom.value = d.innerHTML;
25640                 this.owner.fireEvent('push', this, v);
25641             }
25642             if (this.autoClean) {
25643                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25644                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25645             }
25646             if (this.enableBlocks) {
25647                 Roo.htmleditor.Block.initAll(this.doc.body);
25648             }
25649             
25650             this.updateLanguage();
25651             
25652             var lc = this.doc.body.lastChild;
25653             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25654                 // add an extra line at the end.
25655                 this.doc.body.appendChild(this.doc.createElement('br'));
25656             }
25657             
25658             
25659         }
25660     },
25661
25662     // private
25663     deferFocus : function(){
25664         this.focus.defer(10, this);
25665     },
25666
25667     // doc'ed in Field
25668     focus : function(){
25669         if(this.win && !this.sourceEditMode){
25670             this.win.focus();
25671         }else{
25672             this.el.focus();
25673         }
25674     },
25675     
25676     assignDocWin: function()
25677     {
25678         var iframe = this.iframe;
25679         
25680          if(Roo.isIE){
25681             this.doc = iframe.contentWindow.document;
25682             this.win = iframe.contentWindow;
25683         } else {
25684 //            if (!Roo.get(this.frameId)) {
25685 //                return;
25686 //            }
25687 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25688 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25689             
25690             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25691                 return;
25692             }
25693             
25694             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25695             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25696         }
25697     },
25698     
25699     // private
25700     initEditor : function(){
25701         //console.log("INIT EDITOR");
25702         this.assignDocWin();
25703         
25704         
25705         
25706         this.doc.designMode="on";
25707         this.doc.open();
25708         this.doc.write(this.getDocMarkup());
25709         this.doc.close();
25710         
25711         var dbody = (this.doc.body || this.doc.documentElement);
25712         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25713         // this copies styles from the containing element into thsi one..
25714         // not sure why we need all of this..
25715         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25716         
25717         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25718         //ss['background-attachment'] = 'fixed'; // w3c
25719         dbody.bgProperties = 'fixed'; // ie
25720         dbody.setAttribute("translate", "no");
25721         
25722         //Roo.DomHelper.applyStyles(dbody, ss);
25723         Roo.EventManager.on(this.doc, {
25724              
25725             'mouseup': this.onEditorEvent,
25726             'dblclick': this.onEditorEvent,
25727             'click': this.onEditorEvent,
25728             'keyup': this.onEditorEvent,
25729             
25730             buffer:100,
25731             scope: this
25732         });
25733         Roo.EventManager.on(this.doc, {
25734             'paste': this.onPasteEvent,
25735             scope : this
25736         });
25737         if(Roo.isGecko){
25738             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25739         }
25740         //??? needed???
25741         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25742             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25743         }
25744         this.initialized = true;
25745
25746         
25747         // initialize special key events - enter
25748         new Roo.htmleditor.KeyEnter({core : this});
25749         
25750          
25751         
25752         this.owner.fireEvent('initialize', this);
25753         this.pushValue();
25754     },
25755     // this is to prevent a href clicks resulting in a redirect?
25756    
25757     onPasteEvent : function(e,v)
25758     {
25759         // I think we better assume paste is going to be a dirty load of rubish from word..
25760         
25761         // even pasting into a 'email version' of this widget will have to clean up that mess.
25762         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25763         
25764         // check what type of paste - if it's an image, then handle it differently.
25765         if (cd.files && cd.files.length > 0) {
25766             // pasting images?
25767             var urlAPI = (window.createObjectURL && window) || 
25768                 (window.URL && URL.revokeObjectURL && URL) || 
25769                 (window.webkitURL && webkitURL);
25770     
25771             var url = urlAPI.createObjectURL( cd.files[0]);
25772             this.insertAtCursor('<img src=" + url + ">');
25773             return false;
25774         }
25775         if (cd.types.indexOf('text/html') < 0 ) {
25776             return false;
25777         }
25778         var images = [];
25779         var html = cd.getData('text/html'); // clipboard event
25780         if (cd.types.indexOf('text/rtf') > -1) {
25781             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25782             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25783         }
25784         //Roo.log(images);
25785         //Roo.log(imgs);
25786         // fixme..
25787         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
25788                        .map(function(g) { return g.toDataURL(); })
25789                        .filter(function(g) { return g != 'about:blank'; });
25790         
25791         
25792         html = this.cleanWordChars(html);
25793         
25794         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25795         
25796         
25797         var sn = this.getParentElement();
25798         // check if d contains a table, and prevent nesting??
25799         //Roo.log(d.getElementsByTagName('table'));
25800         //Roo.log(sn);
25801         //Roo.log(sn.closest('table'));
25802         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25803             e.preventDefault();
25804             this.insertAtCursor("You can not nest tables");
25805             //Roo.log("prevent?"); // fixme - 
25806             return false;
25807         }
25808         
25809         if (images.length > 0) {
25810             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25811                 img.setAttribute('src', images[i]);
25812             });
25813         }
25814         if (this.autoClean) {
25815             new Roo.htmleditor.FilterWord({ node : d });
25816             
25817             new Roo.htmleditor.FilterStyleToTag({ node : d });
25818             new Roo.htmleditor.FilterAttributes({
25819                 node : d,
25820                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
25821                 attrib_clean : ['href', 'src' ] 
25822             });
25823             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25824             // should be fonts..
25825             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
25826             new Roo.htmleditor.FilterParagraph({ node : d });
25827             new Roo.htmleditor.FilterSpan({ node : d });
25828             new Roo.htmleditor.FilterLongBr({ node : d });
25829             new Roo.htmleditor.FilterComment({ node : d });
25830             
25831             
25832         }
25833         if (this.enableBlocks) {
25834                 
25835             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25836                 if (img.closest('figure')) { // assume!! that it's aready
25837                     return;
25838                 }
25839                 var fig  = new Roo.htmleditor.BlockFigure({
25840                     image_src  : img.src
25841                 });
25842                 fig.updateElement(img); // replace it..
25843                 
25844             });
25845         }
25846         
25847         
25848         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25849         if (this.enableBlocks) {
25850             Roo.htmleditor.Block.initAll(this.doc.body);
25851         }
25852          
25853         
25854         e.preventDefault();
25855         return false;
25856         // default behaveiour should be our local cleanup paste? (optional?)
25857         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25858         //this.owner.fireEvent('paste', e, v);
25859     },
25860     // private
25861     onDestroy : function(){
25862         
25863         
25864         
25865         if(this.rendered){
25866             
25867             //for (var i =0; i < this.toolbars.length;i++) {
25868             //    // fixme - ask toolbars for heights?
25869             //    this.toolbars[i].onDestroy();
25870            // }
25871             
25872             //this.wrap.dom.innerHTML = '';
25873             //this.wrap.remove();
25874         }
25875     },
25876
25877     // private
25878     onFirstFocus : function(){
25879         
25880         this.assignDocWin();
25881         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25882         
25883         this.activated = true;
25884          
25885     
25886         if(Roo.isGecko){ // prevent silly gecko errors
25887             this.win.focus();
25888             var s = this.win.getSelection();
25889             if(!s.focusNode || s.focusNode.nodeType != 3){
25890                 var r = s.getRangeAt(0);
25891                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25892                 r.collapse(true);
25893                 this.deferFocus();
25894             }
25895             try{
25896                 this.execCmd('useCSS', true);
25897                 this.execCmd('styleWithCSS', false);
25898             }catch(e){}
25899         }
25900         this.owner.fireEvent('activate', this);
25901     },
25902
25903     // private
25904     adjustFont: function(btn){
25905         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25906         //if(Roo.isSafari){ // safari
25907         //    adjust *= 2;
25908        // }
25909         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25910         if(Roo.isSafari){ // safari
25911             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25912             v =  (v < 10) ? 10 : v;
25913             v =  (v > 48) ? 48 : v;
25914             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25915             
25916         }
25917         
25918         
25919         v = Math.max(1, v+adjust);
25920         
25921         this.execCmd('FontSize', v  );
25922     },
25923
25924     onEditorEvent : function(e)
25925     {
25926          
25927         
25928         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25929             return; // we do not handle this.. (undo manager does..)
25930         }
25931         // in theory this detects if the last element is not a br, then we try and do that.
25932         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25933         if (e &&
25934             e.target.nodeName == 'BODY' &&
25935             e.type == "mouseup" &&
25936             this.doc.body.lastChild
25937            ) {
25938             var lc = this.doc.body.lastChild;
25939             // gtx-trans is google translate plugin adding crap.
25940             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25941                 lc = lc.previousSibling;
25942             }
25943             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25944             // if last element is <BR> - then dont do anything.
25945             
25946                 var ns = this.doc.createElement('br');
25947                 this.doc.body.appendChild(ns);
25948                 range = this.doc.createRange();
25949                 range.setStartAfter(ns);
25950                 range.collapse(true);
25951                 var sel = this.win.getSelection();
25952                 sel.removeAllRanges();
25953                 sel.addRange(range);
25954             }
25955         }
25956         
25957         
25958         
25959         this.fireEditorEvent(e);
25960       //  this.updateToolbar();
25961         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25962     },
25963     
25964     fireEditorEvent: function(e)
25965     {
25966         this.owner.fireEvent('editorevent', this, e);
25967     },
25968
25969     insertTag : function(tg)
25970     {
25971         // could be a bit smarter... -> wrap the current selected tRoo..
25972         if (tg.toLowerCase() == 'span' ||
25973             tg.toLowerCase() == 'code' ||
25974             tg.toLowerCase() == 'sup' ||
25975             tg.toLowerCase() == 'sub' 
25976             ) {
25977             
25978             range = this.createRange(this.getSelection());
25979             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25980             wrappingNode.appendChild(range.extractContents());
25981             range.insertNode(wrappingNode);
25982
25983             return;
25984             
25985             
25986             
25987         }
25988         this.execCmd("formatblock",   tg);
25989         this.undoManager.addEvent(); 
25990     },
25991     
25992     insertText : function(txt)
25993     {
25994         
25995         
25996         var range = this.createRange();
25997         range.deleteContents();
25998                //alert(Sender.getAttribute('label'));
25999                
26000         range.insertNode(this.doc.createTextNode(txt));
26001         this.undoManager.addEvent();
26002     } ,
26003     
26004      
26005
26006     /**
26007      * Executes a Midas editor command on the editor document and performs necessary focus and
26008      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26009      * @param {String} cmd The Midas command
26010      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26011      */
26012     relayCmd : function(cmd, value)
26013     {
26014         
26015         switch (cmd) {
26016             case 'justifyleft':
26017             case 'justifyright':
26018             case 'justifycenter':
26019                 // if we are in a cell, then we will adjust the
26020                 var n = this.getParentElement();
26021                 var td = n.closest('td');
26022                 if (td) {
26023                     var bl = Roo.htmleditor.Block.factory(td);
26024                     bl.textAlign = cmd.replace('justify','');
26025                     bl.updateElement();
26026                     this.owner.fireEvent('editorevent', this);
26027                     return;
26028                 }
26029                 this.execCmd('styleWithCSS', true); // 
26030                 break;
26031             case 'bold':
26032             case 'italic':
26033                 // if there is no selection, then we insert, and set the curson inside it..
26034                 this.execCmd('styleWithCSS', false); 
26035                 break;
26036                 
26037         
26038             default:
26039                 break;
26040         }
26041         
26042         
26043         this.win.focus();
26044         this.execCmd(cmd, value);
26045         this.owner.fireEvent('editorevent', this);
26046         //this.updateToolbar();
26047         this.owner.deferFocus();
26048     },
26049
26050     /**
26051      * Executes a Midas editor command directly on the editor document.
26052      * For visual commands, you should use {@link #relayCmd} instead.
26053      * <b>This should only be called after the editor is initialized.</b>
26054      * @param {String} cmd The Midas command
26055      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26056      */
26057     execCmd : function(cmd, value){
26058         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26059         this.syncValue();
26060     },
26061  
26062  
26063    
26064     /**
26065      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26066      * to insert tRoo.
26067      * @param {String} text | dom node.. 
26068      */
26069     insertAtCursor : function(text)
26070     {
26071         
26072         if(!this.activated){
26073             return;
26074         }
26075          
26076         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26077             this.win.focus();
26078             
26079             
26080             // from jquery ui (MIT licenced)
26081             var range, node;
26082             var win = this.win;
26083             
26084             if (win.getSelection && win.getSelection().getRangeAt) {
26085                 
26086                 // delete the existing?
26087                 
26088                 this.createRange(this.getSelection()).deleteContents();
26089                 range = win.getSelection().getRangeAt(0);
26090                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26091                 range.insertNode(node);
26092                 range = range.cloneRange();
26093                 range.collapse(false);
26094                  
26095                 win.getSelection().removeAllRanges();
26096                 win.getSelection().addRange(range);
26097                 
26098                 
26099                 
26100             } else if (win.document.selection && win.document.selection.createRange) {
26101                 // no firefox support
26102                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26103                 win.document.selection.createRange().pasteHTML(txt);
26104             
26105             } else {
26106                 // no firefox support
26107                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26108                 this.execCmd('InsertHTML', txt);
26109             } 
26110             this.syncValue();
26111             
26112             this.deferFocus();
26113         }
26114     },
26115  // private
26116     mozKeyPress : function(e){
26117         if(e.ctrlKey){
26118             var c = e.getCharCode(), cmd;
26119           
26120             if(c > 0){
26121                 c = String.fromCharCode(c).toLowerCase();
26122                 switch(c){
26123                     case 'b':
26124                         cmd = 'bold';
26125                         break;
26126                     case 'i':
26127                         cmd = 'italic';
26128                         break;
26129                     
26130                     case 'u':
26131                         cmd = 'underline';
26132                         break;
26133                     
26134                     //case 'v':
26135                       //  this.cleanUpPaste.defer(100, this);
26136                       //  return;
26137                         
26138                 }
26139                 if(cmd){
26140                     
26141                     this.relayCmd(cmd);
26142                     //this.win.focus();
26143                     //this.execCmd(cmd);
26144                     //this.deferFocus();
26145                     e.preventDefault();
26146                 }
26147                 
26148             }
26149         }
26150     },
26151
26152     // private
26153     fixKeys : function(){ // load time branching for fastest keydown performance
26154         
26155         
26156         if(Roo.isIE){
26157             return function(e){
26158                 var k = e.getKey(), r;
26159                 if(k == e.TAB){
26160                     e.stopEvent();
26161                     r = this.doc.selection.createRange();
26162                     if(r){
26163                         r.collapse(true);
26164                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26165                         this.deferFocus();
26166                     }
26167                     return;
26168                 }
26169                 /// this is handled by Roo.htmleditor.KeyEnter
26170                  /*
26171                 if(k == e.ENTER){
26172                     r = this.doc.selection.createRange();
26173                     if(r){
26174                         var target = r.parentElement();
26175                         if(!target || target.tagName.toLowerCase() != 'li'){
26176                             e.stopEvent();
26177                             r.pasteHTML('<br/>');
26178                             r.collapse(false);
26179                             r.select();
26180                         }
26181                     }
26182                 }
26183                 */
26184                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26185                 //    this.cleanUpPaste.defer(100, this);
26186                 //    return;
26187                 //}
26188                 
26189                 
26190             };
26191         }else if(Roo.isOpera){
26192             return function(e){
26193                 var k = e.getKey();
26194                 if(k == e.TAB){
26195                     e.stopEvent();
26196                     this.win.focus();
26197                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26198                     this.deferFocus();
26199                 }
26200                
26201                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26202                 //    this.cleanUpPaste.defer(100, this);
26203                  //   return;
26204                 //}
26205                 
26206             };
26207         }else if(Roo.isSafari){
26208             return function(e){
26209                 var k = e.getKey();
26210                 
26211                 if(k == e.TAB){
26212                     e.stopEvent();
26213                     this.execCmd('InsertText','\t');
26214                     this.deferFocus();
26215                     return;
26216                 }
26217                  this.mozKeyPress(e);
26218                 
26219                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26220                  //   this.cleanUpPaste.defer(100, this);
26221                  //   return;
26222                // }
26223                 
26224              };
26225         }
26226     }(),
26227     
26228     getAllAncestors: function()
26229     {
26230         var p = this.getSelectedNode();
26231         var a = [];
26232         if (!p) {
26233             a.push(p); // push blank onto stack..
26234             p = this.getParentElement();
26235         }
26236         
26237         
26238         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26239             a.push(p);
26240             p = p.parentNode;
26241         }
26242         a.push(this.doc.body);
26243         return a;
26244     },
26245     lastSel : false,
26246     lastSelNode : false,
26247     
26248     
26249     getSelection : function() 
26250     {
26251         this.assignDocWin();
26252         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26253     },
26254     /**
26255      * Select a dom node
26256      * @param {DomElement} node the node to select
26257      */
26258     selectNode : function(node, collapse)
26259     {
26260         var nodeRange = node.ownerDocument.createRange();
26261         try {
26262             nodeRange.selectNode(node);
26263         } catch (e) {
26264             nodeRange.selectNodeContents(node);
26265         }
26266         if (collapse === true) {
26267             nodeRange.collapse(true);
26268         }
26269         //
26270         var s = this.win.getSelection();
26271         s.removeAllRanges();
26272         s.addRange(nodeRange);
26273     },
26274     
26275     getSelectedNode: function() 
26276     {
26277         // this may only work on Gecko!!!
26278         
26279         // should we cache this!!!!
26280         
26281          
26282          
26283         var range = this.createRange(this.getSelection()).cloneRange();
26284         
26285         if (Roo.isIE) {
26286             var parent = range.parentElement();
26287             while (true) {
26288                 var testRange = range.duplicate();
26289                 testRange.moveToElementText(parent);
26290                 if (testRange.inRange(range)) {
26291                     break;
26292                 }
26293                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26294                     break;
26295                 }
26296                 parent = parent.parentElement;
26297             }
26298             return parent;
26299         }
26300         
26301         // is ancestor a text element.
26302         var ac =  range.commonAncestorContainer;
26303         if (ac.nodeType == 3) {
26304             ac = ac.parentNode;
26305         }
26306         
26307         var ar = ac.childNodes;
26308          
26309         var nodes = [];
26310         var other_nodes = [];
26311         var has_other_nodes = false;
26312         for (var i=0;i<ar.length;i++) {
26313             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26314                 continue;
26315             }
26316             // fullly contained node.
26317             
26318             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26319                 nodes.push(ar[i]);
26320                 continue;
26321             }
26322             
26323             // probably selected..
26324             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26325                 other_nodes.push(ar[i]);
26326                 continue;
26327             }
26328             // outer..
26329             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26330                 continue;
26331             }
26332             
26333             
26334             has_other_nodes = true;
26335         }
26336         if (!nodes.length && other_nodes.length) {
26337             nodes= other_nodes;
26338         }
26339         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26340             return false;
26341         }
26342         
26343         return nodes[0];
26344     },
26345     
26346     
26347     createRange: function(sel)
26348     {
26349         // this has strange effects when using with 
26350         // top toolbar - not sure if it's a great idea.
26351         //this.editor.contentWindow.focus();
26352         if (typeof sel != "undefined") {
26353             try {
26354                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26355             } catch(e) {
26356                 return this.doc.createRange();
26357             }
26358         } else {
26359             return this.doc.createRange();
26360         }
26361     },
26362     getParentElement: function()
26363     {
26364         
26365         this.assignDocWin();
26366         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26367         
26368         var range = this.createRange(sel);
26369          
26370         try {
26371             var p = range.commonAncestorContainer;
26372             while (p.nodeType == 3) { // text node
26373                 p = p.parentNode;
26374             }
26375             return p;
26376         } catch (e) {
26377             return null;
26378         }
26379     
26380     },
26381     /***
26382      *
26383      * Range intersection.. the hard stuff...
26384      *  '-1' = before
26385      *  '0' = hits..
26386      *  '1' = after.
26387      *         [ -- selected range --- ]
26388      *   [fail]                        [fail]
26389      *
26390      *    basically..
26391      *      if end is before start or  hits it. fail.
26392      *      if start is after end or hits it fail.
26393      *
26394      *   if either hits (but other is outside. - then it's not 
26395      *   
26396      *    
26397      **/
26398     
26399     
26400     // @see http://www.thismuchiknow.co.uk/?p=64.
26401     rangeIntersectsNode : function(range, node)
26402     {
26403         var nodeRange = node.ownerDocument.createRange();
26404         try {
26405             nodeRange.selectNode(node);
26406         } catch (e) {
26407             nodeRange.selectNodeContents(node);
26408         }
26409     
26410         var rangeStartRange = range.cloneRange();
26411         rangeStartRange.collapse(true);
26412     
26413         var rangeEndRange = range.cloneRange();
26414         rangeEndRange.collapse(false);
26415     
26416         var nodeStartRange = nodeRange.cloneRange();
26417         nodeStartRange.collapse(true);
26418     
26419         var nodeEndRange = nodeRange.cloneRange();
26420         nodeEndRange.collapse(false);
26421     
26422         return rangeStartRange.compareBoundaryPoints(
26423                  Range.START_TO_START, nodeEndRange) == -1 &&
26424                rangeEndRange.compareBoundaryPoints(
26425                  Range.START_TO_START, nodeStartRange) == 1;
26426         
26427          
26428     },
26429     rangeCompareNode : function(range, node)
26430     {
26431         var nodeRange = node.ownerDocument.createRange();
26432         try {
26433             nodeRange.selectNode(node);
26434         } catch (e) {
26435             nodeRange.selectNodeContents(node);
26436         }
26437         
26438         
26439         range.collapse(true);
26440     
26441         nodeRange.collapse(true);
26442      
26443         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26444         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26445          
26446         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26447         
26448         var nodeIsBefore   =  ss == 1;
26449         var nodeIsAfter    = ee == -1;
26450         
26451         if (nodeIsBefore && nodeIsAfter) {
26452             return 0; // outer
26453         }
26454         if (!nodeIsBefore && nodeIsAfter) {
26455             return 1; //right trailed.
26456         }
26457         
26458         if (nodeIsBefore && !nodeIsAfter) {
26459             return 2;  // left trailed.
26460         }
26461         // fully contined.
26462         return 3;
26463     },
26464  
26465     cleanWordChars : function(input) {// change the chars to hex code
26466         
26467        var swapCodes  = [ 
26468             [    8211, "&#8211;" ], 
26469             [    8212, "&#8212;" ], 
26470             [    8216,  "'" ],  
26471             [    8217, "'" ],  
26472             [    8220, '"' ],  
26473             [    8221, '"' ],  
26474             [    8226, "*" ],  
26475             [    8230, "..." ]
26476         ]; 
26477         var output = input;
26478         Roo.each(swapCodes, function(sw) { 
26479             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26480             
26481             output = output.replace(swapper, sw[1]);
26482         });
26483         
26484         return output;
26485     },
26486     
26487      
26488     
26489         
26490     
26491     cleanUpChild : function (node)
26492     {
26493         
26494         new Roo.htmleditor.FilterComment({node : node});
26495         new Roo.htmleditor.FilterAttributes({
26496                 node : node,
26497                 attrib_black : this.ablack,
26498                 attrib_clean : this.aclean,
26499                 style_white : this.cwhite,
26500                 style_black : this.cblack
26501         });
26502         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26503         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26504          
26505         
26506     },
26507     
26508     /**
26509      * Clean up MS wordisms...
26510      * @deprecated - use filter directly
26511      */
26512     cleanWord : function(node)
26513     {
26514         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26515         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
26516         
26517     },
26518    
26519     
26520     /**
26521
26522      * @deprecated - use filters
26523      */
26524     cleanTableWidths : function(node)
26525     {
26526         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26527         
26528  
26529     },
26530     
26531      
26532         
26533     applyBlacklists : function()
26534     {
26535         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26536         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26537         
26538         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26539         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26540         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26541         
26542         this.white = [];
26543         this.black = [];
26544         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26545             if (b.indexOf(tag) > -1) {
26546                 return;
26547             }
26548             this.white.push(tag);
26549             
26550         }, this);
26551         
26552         Roo.each(w, function(tag) {
26553             if (b.indexOf(tag) > -1) {
26554                 return;
26555             }
26556             if (this.white.indexOf(tag) > -1) {
26557                 return;
26558             }
26559             this.white.push(tag);
26560             
26561         }, this);
26562         
26563         
26564         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26565             if (w.indexOf(tag) > -1) {
26566                 return;
26567             }
26568             this.black.push(tag);
26569             
26570         }, this);
26571         
26572         Roo.each(b, function(tag) {
26573             if (w.indexOf(tag) > -1) {
26574                 return;
26575             }
26576             if (this.black.indexOf(tag) > -1) {
26577                 return;
26578             }
26579             this.black.push(tag);
26580             
26581         }, this);
26582         
26583         
26584         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26585         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26586         
26587         this.cwhite = [];
26588         this.cblack = [];
26589         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26590             if (b.indexOf(tag) > -1) {
26591                 return;
26592             }
26593             this.cwhite.push(tag);
26594             
26595         }, this);
26596         
26597         Roo.each(w, function(tag) {
26598             if (b.indexOf(tag) > -1) {
26599                 return;
26600             }
26601             if (this.cwhite.indexOf(tag) > -1) {
26602                 return;
26603             }
26604             this.cwhite.push(tag);
26605             
26606         }, this);
26607         
26608         
26609         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26610             if (w.indexOf(tag) > -1) {
26611                 return;
26612             }
26613             this.cblack.push(tag);
26614             
26615         }, this);
26616         
26617         Roo.each(b, function(tag) {
26618             if (w.indexOf(tag) > -1) {
26619                 return;
26620             }
26621             if (this.cblack.indexOf(tag) > -1) {
26622                 return;
26623             }
26624             this.cblack.push(tag);
26625             
26626         }, this);
26627     },
26628     
26629     setStylesheets : function(stylesheets)
26630     {
26631         if(typeof(stylesheets) == 'string'){
26632             Roo.get(this.iframe.contentDocument.head).createChild({
26633                 tag : 'link',
26634                 rel : 'stylesheet',
26635                 type : 'text/css',
26636                 href : stylesheets
26637             });
26638             
26639             return;
26640         }
26641         var _this = this;
26642      
26643         Roo.each(stylesheets, function(s) {
26644             if(!s.length){
26645                 return;
26646             }
26647             
26648             Roo.get(_this.iframe.contentDocument.head).createChild({
26649                 tag : 'link',
26650                 rel : 'stylesheet',
26651                 type : 'text/css',
26652                 href : s
26653             });
26654         });
26655
26656         
26657     },
26658     
26659     
26660     updateLanguage : function()
26661     {
26662         if (!this.iframe || !this.iframe.contentDocument) {
26663             return;
26664         }
26665         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26666     },
26667     
26668     
26669     removeStylesheets : function()
26670     {
26671         var _this = this;
26672         
26673         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26674             s.remove();
26675         });
26676     },
26677     
26678     setStyle : function(style)
26679     {
26680         Roo.get(this.iframe.contentDocument.head).createChild({
26681             tag : 'style',
26682             type : 'text/css',
26683             html : style
26684         });
26685
26686         return;
26687     }
26688     
26689     // hide stuff that is not compatible
26690     /**
26691      * @event blur
26692      * @hide
26693      */
26694     /**
26695      * @event change
26696      * @hide
26697      */
26698     /**
26699      * @event focus
26700      * @hide
26701      */
26702     /**
26703      * @event specialkey
26704      * @hide
26705      */
26706     /**
26707      * @cfg {String} fieldClass @hide
26708      */
26709     /**
26710      * @cfg {String} focusClass @hide
26711      */
26712     /**
26713      * @cfg {String} autoCreate @hide
26714      */
26715     /**
26716      * @cfg {String} inputType @hide
26717      */
26718     /**
26719      * @cfg {String} invalidClass @hide
26720      */
26721     /**
26722      * @cfg {String} invalidText @hide
26723      */
26724     /**
26725      * @cfg {String} msgFx @hide
26726      */
26727     /**
26728      * @cfg {String} validateOnBlur @hide
26729      */
26730 });
26731
26732 Roo.HtmlEditorCore.white = [
26733         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26734         
26735        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26736        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26737        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26738        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26739        'TABLE',   'UL',         'XMP', 
26740        
26741        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26742       'THEAD',   'TR', 
26743      
26744       'DIR', 'MENU', 'OL', 'UL', 'DL',
26745        
26746       'EMBED',  'OBJECT'
26747 ];
26748
26749
26750 Roo.HtmlEditorCore.black = [
26751     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26752         'APPLET', // 
26753         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26754         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26755         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26756         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26757         //'FONT' // CLEAN LATER..
26758         'COLGROUP', 'COL'   // messy tables.
26759         
26760         
26761 ];
26762 Roo.HtmlEditorCore.clean = [ // ?? needed???
26763      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26764 ];
26765 Roo.HtmlEditorCore.tag_remove = [
26766     'FONT', 'TBODY'  
26767 ];
26768 // attributes..
26769
26770 Roo.HtmlEditorCore.ablack = [
26771     'on'
26772 ];
26773     
26774 Roo.HtmlEditorCore.aclean = [ 
26775     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26776 ];
26777
26778 // protocols..
26779 Roo.HtmlEditorCore.pwhite= [
26780         'http',  'https',  'mailto'
26781 ];
26782
26783 // white listed style attributes.
26784 Roo.HtmlEditorCore.cwhite= [
26785       //  'text-align', /// default is to allow most things..
26786       
26787          
26788 //        'font-size'//??
26789 ];
26790
26791 // black listed style attributes.
26792 Roo.HtmlEditorCore.cblack= [
26793       //  'font-size' -- this can be set by the project 
26794 ];
26795
26796
26797
26798
26799     //<script type="text/javascript">
26800
26801 /*
26802  * Ext JS Library 1.1.1
26803  * Copyright(c) 2006-2007, Ext JS, LLC.
26804  * Licence LGPL
26805  * 
26806  */
26807  
26808  
26809 Roo.form.HtmlEditor = function(config){
26810     
26811     
26812     
26813     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26814     
26815     if (!this.toolbars) {
26816         this.toolbars = [];
26817     }
26818     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26819     
26820     
26821 };
26822
26823 /**
26824  * @class Roo.form.HtmlEditor
26825  * @extends Roo.form.Field
26826  * Provides a lightweight HTML Editor component.
26827  *
26828  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26829  * 
26830  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26831  * supported by this editor.</b><br/><br/>
26832  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26833  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26834  */
26835 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26836     /**
26837      * @cfg {Boolean} clearUp
26838      */
26839     clearUp : true,
26840       /**
26841      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26842      */
26843     toolbars : false,
26844    
26845      /**
26846      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26847      *                        Roo.resizable.
26848      */
26849     resizable : false,
26850      /**
26851      * @cfg {Number} height (in pixels)
26852      */   
26853     height: 300,
26854    /**
26855      * @cfg {Number} width (in pixels)
26856      */   
26857     width: 500,
26858     
26859     /**
26860      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26861      * 
26862      */
26863     stylesheets: false,
26864     
26865     
26866      /**
26867      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26868      * 
26869      */
26870     cblack: false,
26871     /**
26872      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26873      * 
26874      */
26875     cwhite: false,
26876     
26877      /**
26878      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26879      * 
26880      */
26881     black: false,
26882     /**
26883      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26884      * 
26885      */
26886     white: false,
26887     /**
26888      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26889      */
26890     allowComments: false,
26891     /**
26892      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26893      */
26894     enableBlocks : true,
26895     
26896     /**
26897      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26898      *         if you are doing an email editor, this probably needs disabling, it's designed
26899      */
26900     autoClean: true,
26901     /**
26902      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26903      */
26904     bodyCls : '',
26905     /**
26906      * @cfg {String} language default en - language of text (usefull for rtl languages)
26907      * 
26908      */
26909     language: 'en',
26910     
26911      
26912     // id of frame..
26913     frameId: false,
26914     
26915     // private properties
26916     validationEvent : false,
26917     deferHeight: true,
26918     initialized : false,
26919     activated : false,
26920     
26921     onFocus : Roo.emptyFn,
26922     iframePad:3,
26923     hideMode:'offsets',
26924     
26925     actionMode : 'container', // defaults to hiding it...
26926     
26927     defaultAutoCreate : { // modified by initCompnoent..
26928         tag: "textarea",
26929         style:"width:500px;height:300px;",
26930         autocomplete: "new-password"
26931     },
26932
26933     // private
26934     initComponent : function(){
26935         this.addEvents({
26936             /**
26937              * @event initialize
26938              * Fires when the editor is fully initialized (including the iframe)
26939              * @param {HtmlEditor} this
26940              */
26941             initialize: true,
26942             /**
26943              * @event activate
26944              * Fires when the editor is first receives the focus. Any insertion must wait
26945              * until after this event.
26946              * @param {HtmlEditor} this
26947              */
26948             activate: true,
26949              /**
26950              * @event beforesync
26951              * Fires before the textarea is updated with content from the editor iframe. Return false
26952              * to cancel the sync.
26953              * @param {HtmlEditor} this
26954              * @param {String} html
26955              */
26956             beforesync: true,
26957              /**
26958              * @event beforepush
26959              * Fires before the iframe editor is updated with content from the textarea. Return false
26960              * to cancel the push.
26961              * @param {HtmlEditor} this
26962              * @param {String} html
26963              */
26964             beforepush: true,
26965              /**
26966              * @event sync
26967              * Fires when the textarea is updated with content from the editor iframe.
26968              * @param {HtmlEditor} this
26969              * @param {String} html
26970              */
26971             sync: true,
26972              /**
26973              * @event push
26974              * Fires when the iframe editor is updated with content from the textarea.
26975              * @param {HtmlEditor} this
26976              * @param {String} html
26977              */
26978             push: true,
26979              /**
26980              * @event editmodechange
26981              * Fires when the editor switches edit modes
26982              * @param {HtmlEditor} this
26983              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26984              */
26985             editmodechange: true,
26986             /**
26987              * @event editorevent
26988              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26989              * @param {HtmlEditor} this
26990              */
26991             editorevent: true,
26992             /**
26993              * @event firstfocus
26994              * Fires when on first focus - needed by toolbars..
26995              * @param {HtmlEditor} this
26996              */
26997             firstfocus: true,
26998             /**
26999              * @event autosave
27000              * Auto save the htmlEditor value as a file into Events
27001              * @param {HtmlEditor} this
27002              */
27003             autosave: true,
27004             /**
27005              * @event savedpreview
27006              * preview the saved version of htmlEditor
27007              * @param {HtmlEditor} this
27008              */
27009             savedpreview: true,
27010             
27011             /**
27012             * @event stylesheetsclick
27013             * Fires when press the Sytlesheets button
27014             * @param {Roo.HtmlEditorCore} this
27015             */
27016             stylesheetsclick: true,
27017             /**
27018             * @event paste
27019             * Fires when press user pastes into the editor
27020             * @param {Roo.HtmlEditorCore} this
27021             */
27022             paste: true 
27023         });
27024         this.defaultAutoCreate =  {
27025             tag: "textarea",
27026             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
27027             autocomplete: "new-password"
27028         };
27029     },
27030
27031     /**
27032      * Protected method that will not generally be called directly. It
27033      * is called when the editor creates its toolbar. Override this method if you need to
27034      * add custom toolbar buttons.
27035      * @param {HtmlEditor} editor
27036      */
27037     createToolbar : function(editor){
27038         Roo.log("create toolbars");
27039         if (!editor.toolbars || !editor.toolbars.length) {
27040             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
27041         }
27042         
27043         for (var i =0 ; i < editor.toolbars.length;i++) {
27044             editor.toolbars[i] = Roo.factory(
27045                     typeof(editor.toolbars[i]) == 'string' ?
27046                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
27047                 Roo.form.HtmlEditor);
27048             editor.toolbars[i].init(editor);
27049         }
27050          
27051         
27052     },
27053     /**
27054      * get the Context selected node
27055      * @returns {DomElement|boolean} selected node if active or false if none
27056      * 
27057      */
27058     getSelectedNode : function()
27059     {
27060         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
27061             return false;
27062         }
27063         return this.toolbars[1].tb.selectedNode;
27064     
27065     },
27066     // private
27067     onRender : function(ct, position)
27068     {
27069         var _t = this;
27070         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27071         
27072         this.wrap = this.el.wrap({
27073             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27074         });
27075         
27076         this.editorcore.onRender(ct, position);
27077          
27078         if (this.resizable) {
27079             this.resizeEl = new Roo.Resizable(this.wrap, {
27080                 pinned : true,
27081                 wrap: true,
27082                 dynamic : true,
27083                 minHeight : this.height,
27084                 height: this.height,
27085                 handles : this.resizable,
27086                 width: this.width,
27087                 listeners : {
27088                     resize : function(r, w, h) {
27089                         _t.onResize(w,h); // -something
27090                     }
27091                 }
27092             });
27093             
27094         }
27095         this.createToolbar(this);
27096        
27097         
27098         if(!this.width){
27099             this.setSize(this.wrap.getSize());
27100         }
27101         if (this.resizeEl) {
27102             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27103             // should trigger onReize..
27104         }
27105         
27106         this.keyNav = new Roo.KeyNav(this.el, {
27107             
27108             "tab" : function(e){
27109                 e.preventDefault();
27110                 
27111                 var value = this.getValue();
27112                 
27113                 var start = this.el.dom.selectionStart;
27114                 var end = this.el.dom.selectionEnd;
27115                 
27116                 if(!e.shiftKey){
27117                     
27118                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
27119                     this.el.dom.setSelectionRange(end + 1, end + 1);
27120                     return;
27121                 }
27122                 
27123                 var f = value.substring(0, start).split("\t");
27124                 
27125                 if(f.pop().length != 0){
27126                     return;
27127                 }
27128                 
27129                 this.setValue(f.join("\t") + value.substring(end));
27130                 this.el.dom.setSelectionRange(start - 1, start - 1);
27131                 
27132             },
27133             
27134             "home" : function(e){
27135                 e.preventDefault();
27136                 
27137                 var curr = this.el.dom.selectionStart;
27138                 var lines = this.getValue().split("\n");
27139                 
27140                 if(!lines.length){
27141                     return;
27142                 }
27143                 
27144                 if(e.ctrlKey){
27145                     this.el.dom.setSelectionRange(0, 0);
27146                     return;
27147                 }
27148                 
27149                 var pos = 0;
27150                 
27151                 for (var i = 0; i < lines.length;i++) {
27152                     pos += lines[i].length;
27153                     
27154                     if(i != 0){
27155                         pos += 1;
27156                     }
27157                     
27158                     if(pos < curr){
27159                         continue;
27160                     }
27161                     
27162                     pos -= lines[i].length;
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 = pos;
27173                 this.el.dom.selectionEnd = curr;
27174             },
27175             
27176             "end" : function(e){
27177                 e.preventDefault();
27178                 
27179                 var curr = this.el.dom.selectionStart;
27180                 var lines = this.getValue().split("\n");
27181                 
27182                 if(!lines.length){
27183                     return;
27184                 }
27185                 
27186                 if(e.ctrlKey){
27187                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
27188                     return;
27189                 }
27190                 
27191                 var pos = 0;
27192                 
27193                 for (var i = 0; i < lines.length;i++) {
27194                     
27195                     pos += lines[i].length;
27196                     
27197                     if(i != 0){
27198                         pos += 1;
27199                     }
27200                     
27201                     if(pos < curr){
27202                         continue;
27203                     }
27204                     
27205                     break;
27206                 }
27207                 
27208                 if(!e.shiftKey){
27209                     this.el.dom.setSelectionRange(pos, pos);
27210                     return;
27211                 }
27212                 
27213                 this.el.dom.selectionStart = curr;
27214                 this.el.dom.selectionEnd = pos;
27215             },
27216
27217             scope : this,
27218
27219             doRelay : function(foo, bar, hname){
27220                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27221             },
27222
27223             forceKeyDown: true
27224         });
27225         
27226 //        if(this.autosave && this.w){
27227 //            this.autoSaveFn = setInterval(this.autosave, 1000);
27228 //        }
27229     },
27230
27231     // private
27232     onResize : function(w, h)
27233     {
27234         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27235         var ew = false;
27236         var eh = false;
27237         
27238         if(this.el ){
27239             if(typeof w == 'number'){
27240                 var aw = w - this.wrap.getFrameWidth('lr');
27241                 this.el.setWidth(this.adjustWidth('textarea', aw));
27242                 ew = aw;
27243             }
27244             if(typeof h == 'number'){
27245                 var tbh = 0;
27246                 for (var i =0; i < this.toolbars.length;i++) {
27247                     // fixme - ask toolbars for heights?
27248                     tbh += this.toolbars[i].tb.el.getHeight();
27249                     if (this.toolbars[i].footer) {
27250                         tbh += this.toolbars[i].footer.el.getHeight();
27251                     }
27252                 }
27253                 
27254                 
27255                 
27256                 
27257                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27258                 ah -= 5; // knock a few pixes off for look..
27259 //                Roo.log(ah);
27260                 this.el.setHeight(this.adjustWidth('textarea', ah));
27261                 var eh = ah;
27262             }
27263         }
27264         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27265         this.editorcore.onResize(ew,eh);
27266         
27267     },
27268
27269     /**
27270      * Toggles the editor between standard and source edit mode.
27271      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27272      */
27273     toggleSourceEdit : function(sourceEditMode)
27274     {
27275         this.editorcore.toggleSourceEdit(sourceEditMode);
27276         
27277         if(this.editorcore.sourceEditMode){
27278             Roo.log('editor - showing textarea');
27279             
27280 //            Roo.log('in');
27281 //            Roo.log(this.syncValue());
27282             this.editorcore.syncValue();
27283             this.el.removeClass('x-hidden');
27284             this.el.dom.removeAttribute('tabIndex');
27285             this.el.focus();
27286             this.el.dom.scrollTop = 0;
27287             
27288             
27289             for (var i = 0; i < this.toolbars.length; i++) {
27290                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27291                     this.toolbars[i].tb.hide();
27292                     this.toolbars[i].footer.hide();
27293                 }
27294             }
27295             
27296         }else{
27297             Roo.log('editor - hiding textarea');
27298 //            Roo.log('out')
27299 //            Roo.log(this.pushValue()); 
27300             this.editorcore.pushValue();
27301             
27302             this.el.addClass('x-hidden');
27303             this.el.dom.setAttribute('tabIndex', -1);
27304             
27305             for (var i = 0; i < this.toolbars.length; i++) {
27306                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27307                     this.toolbars[i].tb.show();
27308                     this.toolbars[i].footer.show();
27309                 }
27310             }
27311             
27312             //this.deferFocus();
27313         }
27314         
27315         this.setSize(this.wrap.getSize());
27316         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27317         
27318         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27319     },
27320  
27321     // private (for BoxComponent)
27322     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27323
27324     // private (for BoxComponent)
27325     getResizeEl : function(){
27326         return this.wrap;
27327     },
27328
27329     // private (for BoxComponent)
27330     getPositionEl : function(){
27331         return this.wrap;
27332     },
27333
27334     // private
27335     initEvents : function(){
27336         this.originalValue = this.getValue();
27337     },
27338
27339     /**
27340      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27341      * @method
27342      */
27343     markInvalid : Roo.emptyFn,
27344     /**
27345      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27346      * @method
27347      */
27348     clearInvalid : Roo.emptyFn,
27349
27350     setValue : function(v){
27351         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27352         this.editorcore.pushValue();
27353     },
27354
27355     /**
27356      * update the language in the body - really done by core
27357      * @param {String} language - eg. en / ar / zh-CN etc..
27358      */
27359     updateLanguage : function(lang)
27360     {
27361         this.language = lang;
27362         this.editorcore.language = lang;
27363         this.editorcore.updateLanguage();
27364      
27365     },
27366     // private
27367     deferFocus : function(){
27368         this.focus.defer(10, this);
27369     },
27370
27371     // doc'ed in Field
27372     focus : function(){
27373         this.editorcore.focus();
27374         
27375     },
27376       
27377
27378     // private
27379     onDestroy : function(){
27380         
27381         
27382         
27383         if(this.rendered){
27384             
27385             for (var i =0; i < this.toolbars.length;i++) {
27386                 // fixme - ask toolbars for heights?
27387                 this.toolbars[i].onDestroy();
27388             }
27389             
27390             this.wrap.dom.innerHTML = '';
27391             this.wrap.remove();
27392         }
27393     },
27394
27395     // private
27396     onFirstFocus : function(){
27397         //Roo.log("onFirstFocus");
27398         this.editorcore.onFirstFocus();
27399          for (var i =0; i < this.toolbars.length;i++) {
27400             this.toolbars[i].onFirstFocus();
27401         }
27402         
27403     },
27404     
27405     // private
27406     syncValue : function()
27407     {
27408         this.editorcore.syncValue();
27409     },
27410     
27411     pushValue : function()
27412     {
27413         this.editorcore.pushValue();
27414     },
27415     
27416     setStylesheets : function(stylesheets)
27417     {
27418         this.editorcore.setStylesheets(stylesheets);
27419     },
27420     
27421     removeStylesheets : function()
27422     {
27423         this.editorcore.removeStylesheets();
27424     }
27425      
27426     
27427     // hide stuff that is not compatible
27428     /**
27429      * @event blur
27430      * @hide
27431      */
27432     /**
27433      * @event change
27434      * @hide
27435      */
27436     /**
27437      * @event focus
27438      * @hide
27439      */
27440     /**
27441      * @event specialkey
27442      * @hide
27443      */
27444     /**
27445      * @cfg {String} fieldClass @hide
27446      */
27447     /**
27448      * @cfg {String} focusClass @hide
27449      */
27450     /**
27451      * @cfg {String} autoCreate @hide
27452      */
27453     /**
27454      * @cfg {String} inputType @hide
27455      */
27456     /**
27457      * @cfg {String} invalidClass @hide
27458      */
27459     /**
27460      * @cfg {String} invalidText @hide
27461      */
27462     /**
27463      * @cfg {String} msgFx @hide
27464      */
27465     /**
27466      * @cfg {String} validateOnBlur @hide
27467      */
27468 });
27469  
27470     /*
27471  * Based on
27472  * Ext JS Library 1.1.1
27473  * Copyright(c) 2006-2007, Ext JS, LLC.
27474  *  
27475  
27476  */
27477
27478 /**
27479  * @class Roo.form.HtmlEditor.ToolbarStandard
27480  * Basic Toolbar
27481
27482  * Usage:
27483  *
27484  new Roo.form.HtmlEditor({
27485     ....
27486     toolbars : [
27487         new Roo.form.HtmlEditorToolbar1({
27488             disable : { fonts: 1 , format: 1, ..., ... , ...],
27489             btns : [ .... ]
27490         })
27491     }
27492      
27493  * 
27494  * @cfg {Object} disable List of elements to disable..
27495  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27496  * 
27497  * 
27498  * NEEDS Extra CSS? 
27499  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27500  */
27501  
27502 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27503 {
27504     
27505     Roo.apply(this, config);
27506     
27507     // default disabled, based on 'good practice'..
27508     this.disable = this.disable || {};
27509     Roo.applyIf(this.disable, {
27510         fontSize : true,
27511         colors : true,
27512         specialElements : true
27513     });
27514     
27515     
27516     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27517     // dont call parent... till later.
27518 }
27519
27520 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27521     
27522     tb: false,
27523     
27524     rendered: false,
27525     
27526     editor : false,
27527     editorcore : false,
27528     /**
27529      * @cfg {Object} disable  List of toolbar elements to disable
27530          
27531      */
27532     disable : false,
27533     
27534     
27535      /**
27536      * @cfg {String} createLinkText The default text for the create link prompt
27537      */
27538     createLinkText : 'Please enter the URL for the link:',
27539     /**
27540      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27541      */
27542     defaultLinkValue : 'http:/'+'/',
27543    
27544     
27545       /**
27546      * @cfg {Array} fontFamilies An array of available font families
27547      */
27548     fontFamilies : [
27549         'Arial',
27550         'Courier New',
27551         'Tahoma',
27552         'Times New Roman',
27553         'Verdana'
27554     ],
27555     
27556     specialChars : [
27557            "&#169;",
27558           "&#174;",     
27559           "&#8482;",    
27560           "&#163;" ,    
27561          // "&#8212;",    
27562           "&#8230;",    
27563           "&#247;" ,    
27564         //  "&#225;" ,     ?? a acute?
27565            "&#8364;"    , //Euro
27566        //   "&#8220;"    ,
27567         //  "&#8221;"    ,
27568         //  "&#8226;"    ,
27569           "&#176;"  //   , // degrees
27570
27571          // "&#233;"     , // e ecute
27572          // "&#250;"     , // u ecute?
27573     ],
27574     
27575     specialElements : [
27576         {
27577             text: "Insert Table",
27578             xtype: 'MenuItem',
27579             xns : Roo.Menu,
27580             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27581                 
27582         },
27583         {    
27584             text: "Insert Image",
27585             xtype: 'MenuItem',
27586             xns : Roo.Menu,
27587             ihtml : '<img src="about:blank"/>'
27588             
27589         }
27590         
27591          
27592     ],
27593     
27594     
27595     inputElements : [ 
27596             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27597             "input:submit", "input:button", "select", "textarea", "label" ],
27598     formats : [
27599         ["p"] ,  
27600         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27601         ["pre"],[ "code"], 
27602         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27603         ['div'],['span'],
27604         ['sup'],['sub']
27605     ],
27606     
27607     cleanStyles : [
27608         "font-size"
27609     ],
27610      /**
27611      * @cfg {String} defaultFont default font to use.
27612      */
27613     defaultFont: 'tahoma',
27614    
27615     fontSelect : false,
27616     
27617     
27618     formatCombo : false,
27619     
27620     init : function(editor)
27621     {
27622         this.editor = editor;
27623         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27624         var editorcore = this.editorcore;
27625         
27626         var _t = this;
27627         
27628         var fid = editorcore.frameId;
27629         var etb = this;
27630         function btn(id, toggle, handler){
27631             var xid = fid + '-'+ id ;
27632             return {
27633                 id : xid,
27634                 cmd : id,
27635                 cls : 'x-btn-icon x-edit-'+id,
27636                 enableToggle:toggle !== false,
27637                 scope: _t, // was editor...
27638                 handler:handler||_t.relayBtnCmd,
27639                 clickEvent:'mousedown',
27640                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27641                 tabIndex:-1
27642             };
27643         }
27644         
27645         
27646         
27647         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27648         this.tb = tb;
27649          // stop form submits
27650         tb.el.on('click', function(e){
27651             e.preventDefault(); // what does this do?
27652         });
27653
27654         if(!this.disable.font) { // && !Roo.isSafari){
27655             /* why no safari for fonts 
27656             editor.fontSelect = tb.el.createChild({
27657                 tag:'select',
27658                 tabIndex: -1,
27659                 cls:'x-font-select',
27660                 html: this.createFontOptions()
27661             });
27662             
27663             editor.fontSelect.on('change', function(){
27664                 var font = editor.fontSelect.dom.value;
27665                 editor.relayCmd('fontname', font);
27666                 editor.deferFocus();
27667             }, editor);
27668             
27669             tb.add(
27670                 editor.fontSelect.dom,
27671                 '-'
27672             );
27673             */
27674             
27675         };
27676         if(!this.disable.formats){
27677             this.formatCombo = new Roo.form.ComboBox({
27678                 store: new Roo.data.SimpleStore({
27679                     id : 'tag',
27680                     fields: ['tag'],
27681                     data : this.formats // from states.js
27682                 }),
27683                 blockFocus : true,
27684                 name : '',
27685                 //autoCreate : {tag: "div",  size: "20"},
27686                 displayField:'tag',
27687                 typeAhead: false,
27688                 mode: 'local',
27689                 editable : false,
27690                 triggerAction: 'all',
27691                 emptyText:'Add tag',
27692                 selectOnFocus:true,
27693                 width:135,
27694                 listeners : {
27695                     'select': function(c, r, i) {
27696                         editorcore.insertTag(r.get('tag'));
27697                         editor.focus();
27698                     }
27699                 }
27700
27701             });
27702             tb.addField(this.formatCombo);
27703             
27704         }
27705         
27706         if(!this.disable.format){
27707             tb.add(
27708                 btn('bold'),
27709                 btn('italic'),
27710                 btn('underline'),
27711                 btn('strikethrough')
27712             );
27713         };
27714         if(!this.disable.fontSize){
27715             tb.add(
27716                 '-',
27717                 
27718                 
27719                 btn('increasefontsize', false, editorcore.adjustFont),
27720                 btn('decreasefontsize', false, editorcore.adjustFont)
27721             );
27722         };
27723         
27724         
27725         if(!this.disable.colors){
27726             tb.add(
27727                 '-', {
27728                     id:editorcore.frameId +'-forecolor',
27729                     cls:'x-btn-icon x-edit-forecolor',
27730                     clickEvent:'mousedown',
27731                     tooltip: this.buttonTips['forecolor'] || undefined,
27732                     tabIndex:-1,
27733                     menu : new Roo.menu.ColorMenu({
27734                         allowReselect: true,
27735                         focus: Roo.emptyFn,
27736                         value:'000000',
27737                         plain:true,
27738                         selectHandler: function(cp, color){
27739                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27740                             editor.deferFocus();
27741                         },
27742                         scope: editorcore,
27743                         clickEvent:'mousedown'
27744                     })
27745                 }, {
27746                     id:editorcore.frameId +'backcolor',
27747                     cls:'x-btn-icon x-edit-backcolor',
27748                     clickEvent:'mousedown',
27749                     tooltip: this.buttonTips['backcolor'] || undefined,
27750                     tabIndex:-1,
27751                     menu : new Roo.menu.ColorMenu({
27752                         focus: Roo.emptyFn,
27753                         value:'FFFFFF',
27754                         plain:true,
27755                         allowReselect: true,
27756                         selectHandler: function(cp, color){
27757                             if(Roo.isGecko){
27758                                 editorcore.execCmd('useCSS', false);
27759                                 editorcore.execCmd('hilitecolor', color);
27760                                 editorcore.execCmd('useCSS', true);
27761                                 editor.deferFocus();
27762                             }else{
27763                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27764                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27765                                 editor.deferFocus();
27766                             }
27767                         },
27768                         scope:editorcore,
27769                         clickEvent:'mousedown'
27770                     })
27771                 }
27772             );
27773         };
27774         // now add all the items...
27775         
27776
27777         if(!this.disable.alignments){
27778             tb.add(
27779                 '-',
27780                 btn('justifyleft'),
27781                 btn('justifycenter'),
27782                 btn('justifyright')
27783             );
27784         };
27785
27786         //if(!Roo.isSafari){
27787             if(!this.disable.links){
27788                 tb.add(
27789                     '-',
27790                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27791                 );
27792             };
27793
27794             if(!this.disable.lists){
27795                 tb.add(
27796                     '-',
27797                     btn('insertorderedlist'),
27798                     btn('insertunorderedlist')
27799                 );
27800             }
27801             if(!this.disable.sourceEdit){
27802                 tb.add(
27803                     '-',
27804                     btn('sourceedit', true, function(btn){
27805                         this.toggleSourceEdit(btn.pressed);
27806                     })
27807                 );
27808             }
27809         //}
27810         
27811         var smenu = { };
27812         // special menu.. - needs to be tidied up..
27813         if (!this.disable.special) {
27814             smenu = {
27815                 text: "&#169;",
27816                 cls: 'x-edit-none',
27817                 
27818                 menu : {
27819                     items : []
27820                 }
27821             };
27822             for (var i =0; i < this.specialChars.length; i++) {
27823                 smenu.menu.items.push({
27824                     
27825                     html: this.specialChars[i],
27826                     handler: function(a,b) {
27827                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27828                         //editor.insertAtCursor(a.html);
27829                         
27830                     },
27831                     tabIndex:-1
27832                 });
27833             }
27834             
27835             
27836             tb.add(smenu);
27837             
27838             
27839         }
27840         
27841         var cmenu = { };
27842         if (!this.disable.cleanStyles) {
27843             cmenu = {
27844                 cls: 'x-btn-icon x-btn-clear',
27845                 
27846                 menu : {
27847                     items : []
27848                 }
27849             };
27850             for (var i =0; i < this.cleanStyles.length; i++) {
27851                 cmenu.menu.items.push({
27852                     actiontype : this.cleanStyles[i],
27853                     html: 'Remove ' + this.cleanStyles[i],
27854                     handler: function(a,b) {
27855 //                        Roo.log(a);
27856 //                        Roo.log(b);
27857                         var c = Roo.get(editorcore.doc.body);
27858                         c.select('[style]').each(function(s) {
27859                             s.dom.style.removeProperty(a.actiontype);
27860                         });
27861                         editorcore.syncValue();
27862                     },
27863                     tabIndex:-1
27864                 });
27865             }
27866             cmenu.menu.items.push({
27867                 actiontype : 'tablewidths',
27868                 html: 'Remove Table Widths',
27869                 handler: function(a,b) {
27870                     editorcore.cleanTableWidths();
27871                     editorcore.syncValue();
27872                 },
27873                 tabIndex:-1
27874             });
27875             cmenu.menu.items.push({
27876                 actiontype : 'word',
27877                 html: 'Remove MS Word Formating',
27878                 handler: function(a,b) {
27879                     editorcore.cleanWord();
27880                     editorcore.syncValue();
27881                 },
27882                 tabIndex:-1
27883             });
27884             
27885             cmenu.menu.items.push({
27886                 actiontype : 'all',
27887                 html: 'Remove All Styles',
27888                 handler: function(a,b) {
27889                     
27890                     var c = Roo.get(editorcore.doc.body);
27891                     c.select('[style]').each(function(s) {
27892                         s.dom.removeAttribute('style');
27893                     });
27894                     editorcore.syncValue();
27895                 },
27896                 tabIndex:-1
27897             });
27898             
27899             cmenu.menu.items.push({
27900                 actiontype : 'all',
27901                 html: 'Remove All CSS Classes',
27902                 handler: function(a,b) {
27903                     
27904                     var c = Roo.get(editorcore.doc.body);
27905                     c.select('[class]').each(function(s) {
27906                         s.dom.removeAttribute('class');
27907                     });
27908                     editorcore.cleanWord();
27909                     editorcore.syncValue();
27910                 },
27911                 tabIndex:-1
27912             });
27913             
27914              cmenu.menu.items.push({
27915                 actiontype : 'tidy',
27916                 html: 'Tidy HTML Source',
27917                 handler: function(a,b) {
27918                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27919                     editorcore.syncValue();
27920                 },
27921                 tabIndex:-1
27922             });
27923             
27924             
27925             tb.add(cmenu);
27926         }
27927          
27928         if (!this.disable.specialElements) {
27929             var semenu = {
27930                 text: "Other;",
27931                 cls: 'x-edit-none',
27932                 menu : {
27933                     items : []
27934                 }
27935             };
27936             for (var i =0; i < this.specialElements.length; i++) {
27937                 semenu.menu.items.push(
27938                     Roo.apply({ 
27939                         handler: function(a,b) {
27940                             editor.insertAtCursor(this.ihtml);
27941                         }
27942                     }, this.specialElements[i])
27943                 );
27944                     
27945             }
27946             
27947             tb.add(semenu);
27948             
27949             
27950         }
27951          
27952         
27953         if (this.btns) {
27954             for(var i =0; i< this.btns.length;i++) {
27955                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27956                 b.cls =  'x-edit-none';
27957                 
27958                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27959                     b.cls += ' x-init-enable';
27960                 }
27961                 
27962                 b.scope = editorcore;
27963                 tb.add(b);
27964             }
27965         
27966         }
27967         
27968         
27969         
27970         // disable everything...
27971         
27972         this.tb.items.each(function(item){
27973             
27974            if(
27975                 item.id != editorcore.frameId+ '-sourceedit' && 
27976                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27977             ){
27978                 
27979                 item.disable();
27980             }
27981         });
27982         this.rendered = true;
27983         
27984         // the all the btns;
27985         editor.on('editorevent', this.updateToolbar, this);
27986         // other toolbars need to implement this..
27987         //editor.on('editmodechange', this.updateToolbar, this);
27988     },
27989     
27990     
27991     relayBtnCmd : function(btn) {
27992         this.editorcore.relayCmd(btn.cmd);
27993     },
27994     // private used internally
27995     createLink : function(){
27996         //Roo.log("create link?");
27997         var ec = this.editorcore;
27998         var ar = ec.getAllAncestors();
27999         var n = false;
28000         for(var i = 0;i< ar.length;i++) {
28001             if (ar[i] && ar[i].nodeName == 'A') {
28002                 n = ar[i];
28003                 break;
28004             }
28005         }
28006         
28007         (function() {
28008             
28009             Roo.MessageBox.show({
28010                 title : "Add / Edit Link URL",
28011                 msg : "Enter the url for the link",
28012                 buttons: Roo.MessageBox.OKCANCEL,
28013                 fn: function(btn, url){
28014                     if (btn != 'ok') {
28015                         return;
28016                     }
28017                     if(url && url != 'http:/'+'/'){
28018                         if (n) {
28019                             n.setAttribute('href', url);
28020                         } else {
28021                             ec.relayCmd('createlink', url);
28022                         }
28023                     }
28024                 },
28025                 minWidth:250,
28026                 prompt:true,
28027                 //multiline: multiline,
28028                 modal : true,
28029                 value :  n  ? n.getAttribute('href') : '' 
28030             });
28031             
28032              
28033         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
28034         
28035     },
28036
28037     
28038     /**
28039      * Protected method that will not generally be called directly. It triggers
28040      * a toolbar update by reading the markup state of the current selection in the editor.
28041      */
28042     updateToolbar: function(){
28043
28044         if(!this.editorcore.activated){
28045             this.editor.onFirstFocus();
28046             return;
28047         }
28048
28049         var btns = this.tb.items.map, 
28050             doc = this.editorcore.doc,
28051             frameId = this.editorcore.frameId;
28052
28053         if(!this.disable.font && !Roo.isSafari){
28054             /*
28055             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
28056             if(name != this.fontSelect.dom.value){
28057                 this.fontSelect.dom.value = name;
28058             }
28059             */
28060         }
28061         if(!this.disable.format){
28062             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
28063             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
28064             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
28065             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
28066         }
28067         if(!this.disable.alignments){
28068             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
28069             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
28070             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
28071         }
28072         if(!Roo.isSafari && !this.disable.lists){
28073             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
28074             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
28075         }
28076         
28077         var ans = this.editorcore.getAllAncestors();
28078         if (this.formatCombo) {
28079             
28080             
28081             var store = this.formatCombo.store;
28082             this.formatCombo.setValue("");
28083             for (var i =0; i < ans.length;i++) {
28084                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28085                     // select it..
28086                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28087                     break;
28088                 }
28089             }
28090         }
28091         
28092         
28093         
28094         // hides menus... - so this cant be on a menu...
28095         Roo.menu.MenuMgr.hideAll();
28096
28097         //this.editorsyncValue();
28098     },
28099    
28100     
28101     createFontOptions : function(){
28102         var buf = [], fs = this.fontFamilies, ff, lc;
28103         
28104         
28105         
28106         for(var i = 0, len = fs.length; i< len; i++){
28107             ff = fs[i];
28108             lc = ff.toLowerCase();
28109             buf.push(
28110                 '<option value="',lc,'" style="font-family:',ff,';"',
28111                     (this.defaultFont == lc ? ' selected="true">' : '>'),
28112                     ff,
28113                 '</option>'
28114             );
28115         }
28116         return buf.join('');
28117     },
28118     
28119     toggleSourceEdit : function(sourceEditMode){
28120         
28121         Roo.log("toolbar toogle");
28122         if(sourceEditMode === undefined){
28123             sourceEditMode = !this.sourceEditMode;
28124         }
28125         this.sourceEditMode = sourceEditMode === true;
28126         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
28127         // just toggle the button?
28128         if(btn.pressed !== this.sourceEditMode){
28129             btn.toggle(this.sourceEditMode);
28130             return;
28131         }
28132         
28133         if(sourceEditMode){
28134             Roo.log("disabling buttons");
28135             this.tb.items.each(function(item){
28136                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
28137                     item.disable();
28138                 }
28139             });
28140           
28141         }else{
28142             Roo.log("enabling buttons");
28143             if(this.editorcore.initialized){
28144                 this.tb.items.each(function(item){
28145                     item.enable();
28146                 });
28147                 // initialize 'blocks'
28148                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
28149                     Roo.htmleditor.Block.factory(e).updateElement(e);
28150                 },this);
28151             
28152             }
28153             
28154         }
28155         Roo.log("calling toggole on editor");
28156         // tell the editor that it's been pressed..
28157         this.editor.toggleSourceEdit(sourceEditMode);
28158        
28159     },
28160      /**
28161      * Object collection of toolbar tooltips for the buttons in the editor. The key
28162      * is the command id associated with that button and the value is a valid QuickTips object.
28163      * For example:
28164 <pre><code>
28165 {
28166     bold : {
28167         title: 'Bold (Ctrl+B)',
28168         text: 'Make the selected text bold.',
28169         cls: 'x-html-editor-tip'
28170     },
28171     italic : {
28172         title: 'Italic (Ctrl+I)',
28173         text: 'Make the selected text italic.',
28174         cls: 'x-html-editor-tip'
28175     },
28176     ...
28177 </code></pre>
28178     * @type Object
28179      */
28180     buttonTips : {
28181         bold : {
28182             title: 'Bold (Ctrl+B)',
28183             text: 'Make the selected text bold.',
28184             cls: 'x-html-editor-tip'
28185         },
28186         italic : {
28187             title: 'Italic (Ctrl+I)',
28188             text: 'Make the selected text italic.',
28189             cls: 'x-html-editor-tip'
28190         },
28191         underline : {
28192             title: 'Underline (Ctrl+U)',
28193             text: 'Underline the selected text.',
28194             cls: 'x-html-editor-tip'
28195         },
28196         strikethrough : {
28197             title: 'Strikethrough',
28198             text: 'Strikethrough the selected text.',
28199             cls: 'x-html-editor-tip'
28200         },
28201         increasefontsize : {
28202             title: 'Grow Text',
28203             text: 'Increase the font size.',
28204             cls: 'x-html-editor-tip'
28205         },
28206         decreasefontsize : {
28207             title: 'Shrink Text',
28208             text: 'Decrease the font size.',
28209             cls: 'x-html-editor-tip'
28210         },
28211         backcolor : {
28212             title: 'Text Highlight Color',
28213             text: 'Change the background color of the selected text.',
28214             cls: 'x-html-editor-tip'
28215         },
28216         forecolor : {
28217             title: 'Font Color',
28218             text: 'Change the color of the selected text.',
28219             cls: 'x-html-editor-tip'
28220         },
28221         justifyleft : {
28222             title: 'Align Text Left',
28223             text: 'Align text to the left.',
28224             cls: 'x-html-editor-tip'
28225         },
28226         justifycenter : {
28227             title: 'Center Text',
28228             text: 'Center text in the editor.',
28229             cls: 'x-html-editor-tip'
28230         },
28231         justifyright : {
28232             title: 'Align Text Right',
28233             text: 'Align text to the right.',
28234             cls: 'x-html-editor-tip'
28235         },
28236         insertunorderedlist : {
28237             title: 'Bullet List',
28238             text: 'Start a bulleted list.',
28239             cls: 'x-html-editor-tip'
28240         },
28241         insertorderedlist : {
28242             title: 'Numbered List',
28243             text: 'Start a numbered list.',
28244             cls: 'x-html-editor-tip'
28245         },
28246         createlink : {
28247             title: 'Hyperlink',
28248             text: 'Make the selected text a hyperlink.',
28249             cls: 'x-html-editor-tip'
28250         },
28251         sourceedit : {
28252             title: 'Source Edit',
28253             text: 'Switch to source editing mode.',
28254             cls: 'x-html-editor-tip'
28255         }
28256     },
28257     // private
28258     onDestroy : function(){
28259         if(this.rendered){
28260             
28261             this.tb.items.each(function(item){
28262                 if(item.menu){
28263                     item.menu.removeAll();
28264                     if(item.menu.el){
28265                         item.menu.el.destroy();
28266                     }
28267                 }
28268                 item.destroy();
28269             });
28270              
28271         }
28272     },
28273     onFirstFocus: function() {
28274         this.tb.items.each(function(item){
28275            item.enable();
28276         });
28277     }
28278 };
28279
28280
28281
28282
28283 // <script type="text/javascript">
28284 /*
28285  * Based on
28286  * Ext JS Library 1.1.1
28287  * Copyright(c) 2006-2007, Ext JS, LLC.
28288  *  
28289  
28290  */
28291
28292  
28293 /**
28294  * @class Roo.form.HtmlEditor.ToolbarContext
28295  * Context Toolbar
28296  * 
28297  * Usage:
28298  *
28299  new Roo.form.HtmlEditor({
28300     ....
28301     toolbars : [
28302         { xtype: 'ToolbarStandard', styles : {} }
28303         { xtype: 'ToolbarContext', disable : {} }
28304     ]
28305 })
28306
28307      
28308  * 
28309  * @config : {Object} disable List of elements to disable.. (not done yet.)
28310  * @config : {Object} styles  Map of styles available.
28311  * 
28312  */
28313
28314 Roo.form.HtmlEditor.ToolbarContext = function(config)
28315 {
28316     
28317     Roo.apply(this, config);
28318     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28319     // dont call parent... till later.
28320     this.styles = this.styles || {};
28321 }
28322
28323  
28324
28325 Roo.form.HtmlEditor.ToolbarContext.types = {
28326     'IMG' : [
28327         {
28328             name : 'width',
28329             title: "Width",
28330             width: 40
28331         },
28332         {
28333             name : 'height',
28334             title: "Height",
28335             width: 40
28336         },
28337         {
28338             name : 'align',
28339             title: "Align",
28340             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28341             width : 80
28342             
28343         },
28344         {
28345             name : 'border',
28346             title: "Border",
28347             width: 40
28348         },
28349         {
28350             name : 'alt',
28351             title: "Alt",
28352             width: 120
28353         },
28354         {
28355             name : 'src',
28356             title: "Src",
28357             width: 220
28358         }
28359         
28360     ],
28361     
28362     'FIGURE' : [
28363         {
28364             name : 'align',
28365             title: "Align",
28366             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28367             width : 80  
28368         }
28369     ],
28370     'A' : [
28371         {
28372             name : 'name',
28373             title: "Name",
28374             width: 50
28375         },
28376         {
28377             name : 'target',
28378             title: "Target",
28379             width: 120
28380         },
28381         {
28382             name : 'href',
28383             title: "Href",
28384             width: 220
28385         } // border?
28386         
28387     ],
28388     
28389     'INPUT' : [
28390         {
28391             name : 'name',
28392             title: "name",
28393             width: 120
28394         },
28395         {
28396             name : 'value',
28397             title: "Value",
28398             width: 120
28399         },
28400         {
28401             name : 'width',
28402             title: "Width",
28403             width: 40
28404         }
28405     ],
28406     'LABEL' : [
28407          {
28408             name : 'for',
28409             title: "For",
28410             width: 120
28411         }
28412     ],
28413     'TEXTAREA' : [
28414         {
28415             name : 'name',
28416             title: "name",
28417             width: 120
28418         },
28419         {
28420             name : 'rows',
28421             title: "Rows",
28422             width: 20
28423         },
28424         {
28425             name : 'cols',
28426             title: "Cols",
28427             width: 20
28428         }
28429     ],
28430     'SELECT' : [
28431         {
28432             name : 'name',
28433             title: "name",
28434             width: 120
28435         },
28436         {
28437             name : 'selectoptions',
28438             title: "Options",
28439             width: 200
28440         }
28441     ],
28442     
28443     // should we really allow this??
28444     // should this just be 
28445     'BODY' : [
28446         
28447         {
28448             name : 'title',
28449             title: "Title",
28450             width: 200,
28451             disabled : true
28452         }
28453     ],
28454  
28455     '*' : [
28456         // empty.
28457     ]
28458
28459 };
28460
28461 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28462 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28463
28464 Roo.form.HtmlEditor.ToolbarContext.options = {
28465         'font-family'  : [ 
28466                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28467                 [ 'Courier New', 'Courier New'],
28468                 [ 'Tahoma', 'Tahoma'],
28469                 [ 'Times New Roman,serif', 'Times'],
28470                 [ 'Verdana','Verdana' ]
28471         ]
28472 };
28473
28474 // fixme - these need to be configurable..
28475  
28476
28477 //Roo.form.HtmlEditor.ToolbarContext.types
28478
28479
28480 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28481     
28482     tb: false,
28483     
28484     rendered: false,
28485     
28486     editor : false,
28487     editorcore : false,
28488     /**
28489      * @cfg {Object} disable  List of toolbar elements to disable
28490          
28491      */
28492     disable : false,
28493     /**
28494      * @cfg {Object} styles List of styles 
28495      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28496      *
28497      * These must be defined in the page, so they get rendered correctly..
28498      * .headline { }
28499      * TD.underline { }
28500      * 
28501      */
28502     styles : false,
28503     
28504     options: false,
28505     
28506     toolbars : false,
28507     
28508     init : function(editor)
28509     {
28510         this.editor = editor;
28511         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28512         var editorcore = this.editorcore;
28513         
28514         var fid = editorcore.frameId;
28515         var etb = this;
28516         function btn(id, toggle, handler){
28517             var xid = fid + '-'+ id ;
28518             return {
28519                 id : xid,
28520                 cmd : id,
28521                 cls : 'x-btn-icon x-edit-'+id,
28522                 enableToggle:toggle !== false,
28523                 scope: editorcore, // was editor...
28524                 handler:handler||editorcore.relayBtnCmd,
28525                 clickEvent:'mousedown',
28526                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28527                 tabIndex:-1
28528             };
28529         }
28530         // create a new element.
28531         var wdiv = editor.wrap.createChild({
28532                 tag: 'div'
28533             }, editor.wrap.dom.firstChild.nextSibling, true);
28534         
28535         // can we do this more than once??
28536         
28537          // stop form submits
28538       
28539  
28540         // disable everything...
28541         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28542         this.toolbars = {};
28543         // block toolbars are built in updateToolbar when needed.
28544         for (var i in  ty) {
28545             
28546             this.toolbars[i] = this.buildToolbar(ty[i],i);
28547         }
28548         this.tb = this.toolbars.BODY;
28549         this.tb.el.show();
28550         this.buildFooter();
28551         this.footer.show();
28552         editor.on('hide', function( ) { this.footer.hide() }, this);
28553         editor.on('show', function( ) { this.footer.show() }, this);
28554         
28555          
28556         this.rendered = true;
28557         
28558         // the all the btns;
28559         editor.on('editorevent', this.updateToolbar, this);
28560         // other toolbars need to implement this..
28561         //editor.on('editmodechange', this.updateToolbar, this);
28562     },
28563     
28564     
28565     
28566     /**
28567      * Protected method that will not generally be called directly. It triggers
28568      * a toolbar update by reading the markup state of the current selection in the editor.
28569      *
28570      * Note you can force an update by calling on('editorevent', scope, false)
28571      */
28572     updateToolbar: function(editor ,ev, sel)
28573     {
28574         
28575         if (ev) {
28576             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28577         }
28578         
28579         //Roo.log(ev);
28580         // capture mouse up - this is handy for selecting images..
28581         // perhaps should go somewhere else...
28582         if(!this.editorcore.activated){
28583              this.editor.onFirstFocus();
28584             return;
28585         }
28586         //Roo.log(ev ? ev.target : 'NOTARGET');
28587         
28588         
28589         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28590         // selectNode - might want to handle IE?
28591         
28592         
28593         
28594         if (ev &&
28595             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28596             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28597             // they have click on an image...
28598             // let's see if we can change the selection...
28599             sel = ev.target;
28600             
28601             // this triggers looping?
28602             //this.editorcore.selectNode(sel);
28603              
28604         }
28605         
28606         // this forces an id..
28607         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28608              e.classList.remove('roo-ed-selection');
28609         });
28610         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28611         //Roo.get(node).addClass('roo-ed-selection');
28612       
28613         //var updateFooter = sel ? false : true; 
28614         
28615         
28616         var ans = this.editorcore.getAllAncestors();
28617         
28618         // pick
28619         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28620         
28621         if (!sel) { 
28622             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28623             sel = sel ? sel : this.editorcore.doc.body;
28624             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28625             
28626         }
28627         
28628         var tn = sel.tagName.toUpperCase();
28629         var lastSel = this.tb.selectedNode;
28630         this.tb.selectedNode = sel;
28631         var left_label = tn;
28632         
28633         // ok see if we are editing a block?
28634         
28635         var db = false;
28636         // you are not actually selecting the block.
28637         if (sel && sel.hasAttribute('data-block')) {
28638             db = sel;
28639         } else if (sel && sel.closest('[data-block]')) {
28640             
28641             db = sel.closest('[data-block]');
28642             //var cepar = sel.closest('[contenteditable=true]');
28643             //if (db && cepar && cepar.tagName != 'BODY') {
28644             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28645             //}   
28646         }
28647         
28648         
28649         var block = false;
28650         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28651         if (db && this.editorcore.enableBlocks) {
28652             block = Roo.htmleditor.Block.factory(db);
28653             
28654             
28655             if (block) {
28656                  db.className = (
28657                         db.classList.length > 0  ? db.className + ' ' : ''
28658                     )  + 'roo-ed-selection';
28659                  
28660                  // since we removed it earlier... its not there..
28661                 tn = 'BLOCK.' + db.getAttribute('data-block');
28662                 
28663                 //this.editorcore.selectNode(db);
28664                 if (typeof(this.toolbars[tn]) == 'undefined') {
28665                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28666                 }
28667                 this.toolbars[tn].selectedNode = db;
28668                 left_label = block.friendly_name;
28669                 ans = this.editorcore.getAllAncestors();
28670             }
28671             
28672                 
28673             
28674         }
28675         
28676         
28677         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28678             return; // no change?
28679         }
28680         
28681         
28682           
28683         this.tb.el.hide();
28684         ///console.log("show: " + tn);
28685         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28686         
28687         this.tb.el.show();
28688         // update name
28689         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28690         
28691         
28692         // update attributes
28693         if (block && this.tb.fields) {
28694              
28695             this.tb.fields.each(function(e) {
28696                 e.setValue(block[e.name]);
28697             });
28698             
28699             
28700         } else  if (this.tb.fields && this.tb.selectedNode) {
28701             this.tb.fields.each( function(e) {
28702                 if (e.stylename) {
28703                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28704                     return;
28705                 } 
28706                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28707             }, this);
28708             this.updateToolbarStyles(this.tb.selectedNode);  
28709         }
28710         
28711         
28712        
28713         Roo.menu.MenuMgr.hideAll();
28714
28715         
28716         
28717     
28718         // update the footer
28719         //
28720         this.updateFooter(ans);
28721              
28722     },
28723     
28724     updateToolbarStyles : function(sel)
28725     {
28726         var hasStyles = false;
28727         for(var i in this.styles) {
28728             hasStyles = true;
28729             break;
28730         }
28731         
28732         // update styles
28733         if (hasStyles && this.tb.hasStyles) { 
28734             var st = this.tb.fields.item(0);
28735             
28736             st.store.removeAll();
28737             var cn = sel.className.split(/\s+/);
28738             
28739             var avs = [];
28740             if (this.styles['*']) {
28741                 
28742                 Roo.each(this.styles['*'], function(v) {
28743                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28744                 });
28745             }
28746             if (this.styles[tn]) { 
28747                 Roo.each(this.styles[tn], function(v) {
28748                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28749                 });
28750             }
28751             
28752             st.store.loadData(avs);
28753             st.collapse();
28754             st.setValue(cn);
28755         }
28756     },
28757     
28758      
28759     updateFooter : function(ans)
28760     {
28761         var html = '';
28762         if (ans === false) {
28763             this.footDisp.dom.innerHTML = '';
28764             return;
28765         }
28766         
28767         this.footerEls = ans.reverse();
28768         Roo.each(this.footerEls, function(a,i) {
28769             if (!a) { return; }
28770             html += html.length ? ' &gt; '  :  '';
28771             
28772             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28773             
28774         });
28775        
28776         // 
28777         var sz = this.footDisp.up('td').getSize();
28778         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28779         this.footDisp.dom.style.marginLeft = '5px';
28780         
28781         this.footDisp.dom.style.overflow = 'hidden';
28782         
28783         this.footDisp.dom.innerHTML = html;
28784             
28785         
28786     },
28787    
28788        
28789     // private
28790     onDestroy : function(){
28791         if(this.rendered){
28792             
28793             this.tb.items.each(function(item){
28794                 if(item.menu){
28795                     item.menu.removeAll();
28796                     if(item.menu.el){
28797                         item.menu.el.destroy();
28798                     }
28799                 }
28800                 item.destroy();
28801             });
28802              
28803         }
28804     },
28805     onFirstFocus: function() {
28806         // need to do this for all the toolbars..
28807         this.tb.items.each(function(item){
28808            item.enable();
28809         });
28810     },
28811     buildToolbar: function(tlist, nm, friendly_name, block)
28812     {
28813         var editor = this.editor;
28814         var editorcore = this.editorcore;
28815          // create a new element.
28816         var wdiv = editor.wrap.createChild({
28817                 tag: 'div'
28818             }, editor.wrap.dom.firstChild.nextSibling, true);
28819         
28820        
28821         var tb = new Roo.Toolbar(wdiv);
28822         ///this.tb = tb; // << this sets the active toolbar..
28823         if (tlist === false && block) {
28824             tlist = block.contextMenu(this);
28825         }
28826         
28827         tb.hasStyles = false;
28828         tb.name = nm;
28829         
28830         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28831         
28832         var styles = Array.from(this.styles);
28833         
28834         
28835         // styles...
28836         if (styles && styles.length) {
28837             tb.hasStyles = true;
28838             // this needs a multi-select checkbox...
28839             tb.addField( new Roo.form.ComboBox({
28840                 store: new Roo.data.SimpleStore({
28841                     id : 'val',
28842                     fields: ['val', 'selected'],
28843                     data : [] 
28844                 }),
28845                 name : '-roo-edit-className',
28846                 attrname : 'className',
28847                 displayField: 'val',
28848                 typeAhead: false,
28849                 mode: 'local',
28850                 editable : false,
28851                 triggerAction: 'all',
28852                 emptyText:'Select Style',
28853                 selectOnFocus:true,
28854                 width: 130,
28855                 listeners : {
28856                     'select': function(c, r, i) {
28857                         // initial support only for on class per el..
28858                         tb.selectedNode.className =  r ? r.get('val') : '';
28859                         editorcore.syncValue();
28860                     }
28861                 }
28862     
28863             }));
28864         }
28865         
28866         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28867         
28868         
28869         for (var i = 0; i < tlist.length; i++) {
28870             
28871             // newer versions will use xtype cfg to create menus.
28872             if (typeof(tlist[i].xtype) != 'undefined') {
28873                 
28874                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28875                 
28876                 
28877                 continue;
28878             }
28879             
28880             var item = tlist[i];
28881             tb.add(item.title + ":&nbsp;");
28882             
28883             
28884             //optname == used so you can configure the options available..
28885             var opts = item.opts ? item.opts : false;
28886             if (item.optname) { // use the b
28887                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28888            
28889             }
28890             
28891             if (opts) {
28892                 // opts == pulldown..
28893                 tb.addField( new Roo.form.ComboBox({
28894                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28895                         id : 'val',
28896                         fields: ['val', 'display'],
28897                         data : opts  
28898                     }),
28899                     name : '-roo-edit-' + tlist[i].name,
28900                     
28901                     attrname : tlist[i].name,
28902                     stylename : item.style ? item.style : false,
28903                     
28904                     displayField: item.displayField ? item.displayField : 'val',
28905                     valueField :  'val',
28906                     typeAhead: false,
28907                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28908                     editable : false,
28909                     triggerAction: 'all',
28910                     emptyText:'Select',
28911                     selectOnFocus:true,
28912                     width: item.width ? item.width  : 130,
28913                     listeners : {
28914                         'select': function(c, r, i) {
28915                              
28916                             
28917                             if (c.stylename) {
28918                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28919                                 editorcore.syncValue();
28920                                 return;
28921                             }
28922                             if (r === false) {
28923                                 tb.selectedNode.removeAttribute(c.attrname);
28924                                 editorcore.syncValue();
28925                                 return;
28926                             }
28927                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28928                             editorcore.syncValue();
28929                         }
28930                     }
28931
28932                 }));
28933                 continue;
28934                     
28935                  
28936                 /*
28937                 tb.addField( new Roo.form.TextField({
28938                     name: i,
28939                     width: 100,
28940                     //allowBlank:false,
28941                     value: ''
28942                 }));
28943                 continue;
28944                 */
28945             }
28946             tb.addField( new Roo.form.TextField({
28947                 name: '-roo-edit-' + tlist[i].name,
28948                 attrname : tlist[i].name,
28949                 
28950                 width: item.width,
28951                 //allowBlank:true,
28952                 value: '',
28953                 listeners: {
28954                     'change' : function(f, nv, ov) {
28955                         
28956                          
28957                         tb.selectedNode.setAttribute(f.attrname, nv);
28958                         editorcore.syncValue();
28959                     }
28960                 }
28961             }));
28962              
28963         }
28964         
28965         var _this = this;
28966         var show_delete = !block || block.deleteTitle !== false;
28967         if(nm == 'BODY'){
28968             show_delete = false;
28969             tb.addSeparator();
28970         
28971             tb.addButton( {
28972                 text: 'Stylesheets',
28973
28974                 listeners : {
28975                     click : function ()
28976                     {
28977                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28978                     }
28979                 }
28980             });
28981         }
28982         
28983         tb.addFill();
28984         if (show_delete) {
28985             tb.addButton({
28986                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28987         
28988                 listeners : {
28989                     click : function ()
28990                     {
28991                         var sn = tb.selectedNode;
28992                         if (block) {
28993                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28994                             
28995                         }
28996                         if (!sn) {
28997                             return;
28998                         }
28999                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
29000                         if (sn.hasAttribute('data-block')) {
29001                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
29002                             sn.parentNode.removeChild(sn);
29003                             
29004                         } else if (sn && sn.tagName != 'BODY') {
29005                             // remove and keep parents.
29006                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
29007                             a.replaceTag(sn);
29008                         }
29009                         
29010                         
29011                         var range = editorcore.createRange();
29012             
29013                         range.setStart(stn,0);
29014                         range.setEnd(stn,0); 
29015                         var selection = editorcore.getSelection();
29016                         selection.removeAllRanges();
29017                         selection.addRange(range);
29018                         
29019                         
29020                         //_this.updateToolbar(null, null, pn);
29021                         _this.updateToolbar(null, null, null);
29022                         _this.updateFooter(false);
29023                         
29024                     }
29025                 }
29026                 
29027                         
29028                     
29029                 
29030             });
29031         }    
29032         
29033         tb.el.on('click', function(e){
29034             e.preventDefault(); // what does this do?
29035         });
29036         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
29037         tb.el.hide();
29038         
29039         // dont need to disable them... as they will get hidden
29040         return tb;
29041          
29042         
29043     },
29044     buildFooter : function()
29045     {
29046         
29047         var fel = this.editor.wrap.createChild();
29048         this.footer = new Roo.Toolbar(fel);
29049         // toolbar has scrolly on left / right?
29050         var footDisp= new Roo.Toolbar.Fill();
29051         var _t = this;
29052         this.footer.add(
29053             {
29054                 text : '&lt;',
29055                 xtype: 'Button',
29056                 handler : function() {
29057                     _t.footDisp.scrollTo('left',0,true)
29058                 }
29059             }
29060         );
29061         this.footer.add( footDisp );
29062         this.footer.add( 
29063             {
29064                 text : '&gt;',
29065                 xtype: 'Button',
29066                 handler : function() {
29067                     // no animation..
29068                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
29069                 }
29070             }
29071         );
29072         var fel = Roo.get(footDisp.el);
29073         fel.addClass('x-editor-context');
29074         this.footDispWrap = fel; 
29075         this.footDispWrap.overflow  = 'hidden';
29076         
29077         this.footDisp = fel.createChild();
29078         this.footDispWrap.on('click', this.onContextClick, this)
29079         
29080         
29081     },
29082     // when the footer contect changes
29083     onContextClick : function (ev,dom)
29084     {
29085         ev.preventDefault();
29086         var  cn = dom.className;
29087         //Roo.log(cn);
29088         if (!cn.match(/x-ed-loc-/)) {
29089             return;
29090         }
29091         var n = cn.split('-').pop();
29092         var ans = this.footerEls;
29093         var sel = ans[n];
29094         
29095         this.editorcore.selectNode(sel);
29096         
29097         
29098         this.updateToolbar(null, null, sel);
29099         
29100         
29101     }
29102     
29103     
29104     
29105     
29106     
29107 });
29108
29109
29110
29111
29112
29113 /*
29114  * Based on:
29115  * Ext JS Library 1.1.1
29116  * Copyright(c) 2006-2007, Ext JS, LLC.
29117  *
29118  * Originally Released Under LGPL - original licence link has changed is not relivant.
29119  *
29120  * Fork - LGPL
29121  * <script type="text/javascript">
29122  */
29123  
29124 /**
29125  * @class Roo.form.BasicForm
29126  * @extends Roo.util.Observable
29127  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
29128  * @constructor
29129  * @param {String/HTMLElement/Roo.Element} el The form element or its id
29130  * @param {Object} config Configuration options
29131  */
29132 Roo.form.BasicForm = function(el, config){
29133     this.allItems = [];
29134     this.childForms = [];
29135     Roo.apply(this, config);
29136     /*
29137      * The Roo.form.Field items in this form.
29138      * @type MixedCollection
29139      */
29140      
29141      
29142     this.items = new Roo.util.MixedCollection(false, function(o){
29143         return o.id || (o.id = Roo.id());
29144     });
29145     this.addEvents({
29146         /**
29147          * @event beforeaction
29148          * Fires before any action is performed. Return false to cancel the action.
29149          * @param {Form} this
29150          * @param {Action} action The action to be performed
29151          */
29152         beforeaction: true,
29153         /**
29154          * @event actionfailed
29155          * Fires when an action fails.
29156          * @param {Form} this
29157          * @param {Action} action The action that failed
29158          */
29159         actionfailed : true,
29160         /**
29161          * @event actioncomplete
29162          * Fires when an action is completed.
29163          * @param {Form} this
29164          * @param {Action} action The action that completed
29165          */
29166         actioncomplete : true
29167     });
29168     if(el){
29169         this.initEl(el);
29170     }
29171     Roo.form.BasicForm.superclass.constructor.call(this);
29172     
29173     Roo.form.BasicForm.popover.apply();
29174 };
29175
29176 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
29177     /**
29178      * @cfg {String} method
29179      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
29180      */
29181     /**
29182      * @cfg {DataReader} reader
29183      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
29184      * This is optional as there is built-in support for processing JSON.
29185      */
29186     /**
29187      * @cfg {DataReader} errorReader
29188      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
29189      * This is completely optional as there is built-in support for processing JSON.
29190      */
29191     /**
29192      * @cfg {String} url
29193      * The URL to use for form actions if one isn't supplied in the action options.
29194      */
29195     /**
29196      * @cfg {Boolean} fileUpload
29197      * Set to true if this form is a file upload.
29198      */
29199      
29200     /**
29201      * @cfg {Object} baseParams
29202      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29203      */
29204      /**
29205      
29206     /**
29207      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29208      */
29209     timeout: 30,
29210
29211     // private
29212     activeAction : null,
29213
29214     /**
29215      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29216      * or setValues() data instead of when the form was first created.
29217      */
29218     trackResetOnLoad : false,
29219     
29220     
29221     /**
29222      * childForms - used for multi-tab forms
29223      * @type {Array}
29224      */
29225     childForms : false,
29226     
29227     /**
29228      * allItems - full list of fields.
29229      * @type {Array}
29230      */
29231     allItems : false,
29232     
29233     /**
29234      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29235      * element by passing it or its id or mask the form itself by passing in true.
29236      * @type Mixed
29237      */
29238     waitMsgTarget : false,
29239     
29240     /**
29241      * @type Boolean
29242      */
29243     disableMask : false,
29244     
29245     /**
29246      * @cfg {Boolean} errorMask (true|false) default false
29247      */
29248     errorMask : false,
29249     
29250     /**
29251      * @cfg {Number} maskOffset Default 100
29252      */
29253     maskOffset : 100,
29254
29255     // private
29256     initEl : function(el){
29257         this.el = Roo.get(el);
29258         this.id = this.el.id || Roo.id();
29259         this.el.on('submit', this.onSubmit, this);
29260         this.el.addClass('x-form');
29261     },
29262
29263     // private
29264     onSubmit : function(e){
29265         e.stopEvent();
29266     },
29267
29268     /**
29269      * Returns true if client-side validation on the form is successful.
29270      * @return Boolean
29271      */
29272     isValid : function(){
29273         var valid = true;
29274         var target = false;
29275         this.items.each(function(f){
29276             if(f.validate()){
29277                 return;
29278             }
29279             
29280             valid = false;
29281                 
29282             if(!target && f.el.isVisible(true)){
29283                 target = f;
29284             }
29285         });
29286         
29287         if(this.errorMask && !valid){
29288             Roo.form.BasicForm.popover.mask(this, target);
29289         }
29290         
29291         return valid;
29292     },
29293     /**
29294      * Returns array of invalid form fields.
29295      * @return Array
29296      */
29297     
29298     invalidFields : function()
29299     {
29300         var ret = [];
29301         this.items.each(function(f){
29302             if(f.validate()){
29303                 return;
29304             }
29305             ret.push(f);
29306             
29307         });
29308         
29309         return ret;
29310     },
29311     
29312     
29313     /**
29314      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29315      * @return Boolean
29316      */
29317     isDirty : function(){
29318         var dirty = false;
29319         this.items.each(function(f){
29320            if(f.isDirty()){
29321                dirty = true;
29322                return false;
29323            }
29324         });
29325         return dirty;
29326     },
29327     
29328     /**
29329      * Returns true if any fields in this form have changed since their original load. (New version)
29330      * @return Boolean
29331      */
29332     
29333     hasChanged : function()
29334     {
29335         var dirty = false;
29336         this.items.each(function(f){
29337            if(f.hasChanged()){
29338                dirty = true;
29339                return false;
29340            }
29341         });
29342         return dirty;
29343         
29344     },
29345     /**
29346      * Resets all hasChanged to 'false' -
29347      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29348      * So hasChanged storage is only to be used for this purpose
29349      * @return Boolean
29350      */
29351     resetHasChanged : function()
29352     {
29353         this.items.each(function(f){
29354            f.resetHasChanged();
29355         });
29356         
29357     },
29358     
29359     
29360     /**
29361      * Performs a predefined action (submit or load) or custom actions you define on this form.
29362      * @param {String} actionName The name of the action type
29363      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29364      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29365      * accept other config options):
29366      * <pre>
29367 Property          Type             Description
29368 ----------------  ---------------  ----------------------------------------------------------------------------------
29369 url               String           The url for the action (defaults to the form's url)
29370 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29371 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29372 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29373                                    validate the form on the client (defaults to false)
29374      * </pre>
29375      * @return {BasicForm} this
29376      */
29377     doAction : function(action, options){
29378         if(typeof action == 'string'){
29379             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29380         }
29381         if(this.fireEvent('beforeaction', this, action) !== false){
29382             this.beforeAction(action);
29383             action.run.defer(100, action);
29384         }
29385         return this;
29386     },
29387
29388     /**
29389      * Shortcut to do a submit action.
29390      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29391      * @return {BasicForm} this
29392      */
29393     submit : function(options){
29394         this.doAction('submit', options);
29395         return this;
29396     },
29397
29398     /**
29399      * Shortcut to do a load action.
29400      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29401      * @return {BasicForm} this
29402      */
29403     load : function(options){
29404         this.doAction('load', options);
29405         return this;
29406     },
29407
29408     /**
29409      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29410      * @param {Record} record The record to edit
29411      * @return {BasicForm} this
29412      */
29413     updateRecord : function(record){
29414         record.beginEdit();
29415         var fs = record.fields;
29416         fs.each(function(f){
29417             var field = this.findField(f.name);
29418             if(field){
29419                 record.set(f.name, field.getValue());
29420             }
29421         }, this);
29422         record.endEdit();
29423         return this;
29424     },
29425
29426     /**
29427      * Loads an Roo.data.Record into this form.
29428      * @param {Record} record The record to load
29429      * @return {BasicForm} this
29430      */
29431     loadRecord : function(record){
29432         this.setValues(record.data);
29433         return this;
29434     },
29435
29436     // private
29437     beforeAction : function(action){
29438         var o = action.options;
29439         
29440         if(!this.disableMask) {
29441             if(this.waitMsgTarget === true){
29442                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29443             }else if(this.waitMsgTarget){
29444                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29445                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29446             }else {
29447                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29448             }
29449         }
29450         
29451          
29452     },
29453
29454     // private
29455     afterAction : function(action, success){
29456         this.activeAction = null;
29457         var o = action.options;
29458         
29459         if(!this.disableMask) {
29460             if(this.waitMsgTarget === true){
29461                 this.el.unmask();
29462             }else if(this.waitMsgTarget){
29463                 this.waitMsgTarget.unmask();
29464             }else{
29465                 Roo.MessageBox.updateProgress(1);
29466                 Roo.MessageBox.hide();
29467             }
29468         }
29469         
29470         if(success){
29471             if(o.reset){
29472                 this.reset();
29473             }
29474             Roo.callback(o.success, o.scope, [this, action]);
29475             this.fireEvent('actioncomplete', this, action);
29476             
29477         }else{
29478             
29479             // failure condition..
29480             // we have a scenario where updates need confirming.
29481             // eg. if a locking scenario exists..
29482             // we look for { errors : { needs_confirm : true }} in the response.
29483             if (
29484                 (typeof(action.result) != 'undefined')  &&
29485                 (typeof(action.result.errors) != 'undefined')  &&
29486                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29487            ){
29488                 var _t = this;
29489                 Roo.MessageBox.confirm(
29490                     "Change requires confirmation",
29491                     action.result.errorMsg,
29492                     function(r) {
29493                         if (r != 'yes') {
29494                             return;
29495                         }
29496                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29497                     }
29498                     
29499                 );
29500                 
29501                 
29502                 
29503                 return;
29504             }
29505             
29506             Roo.callback(o.failure, o.scope, [this, action]);
29507             // show an error message if no failed handler is set..
29508             if (!this.hasListener('actionfailed')) {
29509                 Roo.MessageBox.alert("Error",
29510                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29511                         action.result.errorMsg :
29512                         "Saving Failed, please check your entries or try again"
29513                 );
29514             }
29515             
29516             this.fireEvent('actionfailed', this, action);
29517         }
29518         
29519     },
29520
29521     /**
29522      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29523      * @param {String} id The value to search for
29524      * @return Field
29525      */
29526     findField : function(id){
29527         var field = this.items.get(id);
29528         if(!field){
29529             this.items.each(function(f){
29530                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29531                     field = f;
29532                     return false;
29533                 }
29534             });
29535         }
29536         return field || null;
29537     },
29538
29539     /**
29540      * Add a secondary form to this one, 
29541      * Used to provide tabbed forms. One form is primary, with hidden values 
29542      * which mirror the elements from the other forms.
29543      * 
29544      * @param {Roo.form.Form} form to add.
29545      * 
29546      */
29547     addForm : function(form)
29548     {
29549        
29550         if (this.childForms.indexOf(form) > -1) {
29551             // already added..
29552             return;
29553         }
29554         this.childForms.push(form);
29555         var n = '';
29556         Roo.each(form.allItems, function (fe) {
29557             
29558             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29559             if (this.findField(n)) { // already added..
29560                 return;
29561             }
29562             var add = new Roo.form.Hidden({
29563                 name : n
29564             });
29565             add.render(this.el);
29566             
29567             this.add( add );
29568         }, this);
29569         
29570     },
29571     /**
29572      * Mark fields in this form invalid in bulk.
29573      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29574      * @return {BasicForm} this
29575      */
29576     markInvalid : function(errors){
29577         if(errors instanceof Array){
29578             for(var i = 0, len = errors.length; i < len; i++){
29579                 var fieldError = errors[i];
29580                 var f = this.findField(fieldError.id);
29581                 if(f){
29582                     f.markInvalid(fieldError.msg);
29583                 }
29584             }
29585         }else{
29586             var field, id;
29587             for(id in errors){
29588                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29589                     field.markInvalid(errors[id]);
29590                 }
29591             }
29592         }
29593         Roo.each(this.childForms || [], function (f) {
29594             f.markInvalid(errors);
29595         });
29596         
29597         return this;
29598     },
29599
29600     /**
29601      * Set values for fields in this form in bulk.
29602      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29603      * @return {BasicForm} this
29604      */
29605     setValues : function(values){
29606         if(values instanceof Array){ // array of objects
29607             for(var i = 0, len = values.length; i < len; i++){
29608                 var v = values[i];
29609                 var f = this.findField(v.id);
29610                 if(f){
29611                     f.setValue(v.value);
29612                     if(this.trackResetOnLoad){
29613                         f.originalValue = f.getValue();
29614                     }
29615                 }
29616             }
29617         }else{ // object hash
29618             var field, id;
29619             for(id in values){
29620                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29621                     
29622                     if (field.setFromData && 
29623                         field.valueField && 
29624                         field.displayField &&
29625                         // combos' with local stores can 
29626                         // be queried via setValue()
29627                         // to set their value..
29628                         (field.store && !field.store.isLocal)
29629                         ) {
29630                         // it's a combo
29631                         var sd = { };
29632                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29633                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29634                         field.setFromData(sd);
29635                         
29636                     } else {
29637                         field.setValue(values[id]);
29638                     }
29639                     
29640                     
29641                     if(this.trackResetOnLoad){
29642                         field.originalValue = field.getValue();
29643                     }
29644                 }
29645             }
29646         }
29647         this.resetHasChanged();
29648         
29649         
29650         Roo.each(this.childForms || [], function (f) {
29651             f.setValues(values);
29652             f.resetHasChanged();
29653         });
29654                 
29655         return this;
29656     },
29657  
29658     /**
29659      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29660      * they are returned as an array.
29661      * @param {Boolean} asString
29662      * @return {Object}
29663      */
29664     getValues : function(asString)
29665     {
29666         if (this.childForms) {
29667             // copy values from the child forms
29668             Roo.each(this.childForms, function (f) {
29669                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29670             }, this);
29671         }
29672         
29673         // use formdata
29674         if (typeof(FormData) != 'undefined' && asString !== true) {
29675             // this relies on a 'recent' version of chrome apparently...
29676             try {
29677                 var fd = (new FormData(this.el.dom)).entries();
29678                 var ret = {};
29679                 var ent = fd.next();
29680                 while (!ent.done) {
29681                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29682                     ent = fd.next();
29683                 };
29684                 return ret;
29685             } catch(e) {
29686                 
29687             }
29688             
29689         }
29690         
29691         
29692         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29693         if(asString === true){
29694             return fs;
29695         }
29696         return Roo.urlDecode(fs);
29697     },
29698     
29699     /**
29700      * Returns the fields in this form as an object with key/value pairs. 
29701      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29702      * Normally this will not return readOnly data 
29703      * @param {Boolean} with_readonly return readonly field data.
29704      * @return {Object}
29705      */
29706     getFieldValues : function(with_readonly)
29707     {
29708         if (this.childForms) {
29709             // copy values from the child forms
29710             // should this call getFieldValues - probably not as we do not currently copy
29711             // hidden fields when we generate..
29712             Roo.each(this.childForms, function (f) {
29713                 this.setValues(f.getFieldValues());
29714             }, this);
29715         }
29716         
29717         var ret = {};
29718         this.items.each(function(f){
29719             
29720             if (f.readOnly && with_readonly !== true) {
29721                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29722                         // if a subform contains a copy of them.
29723                         // if you have subforms with the same editable data, you will need to copy the data back
29724                         // and forth.
29725             }
29726             
29727             if (!f.getName()) {
29728                 return;
29729             }
29730             var v = f.getValue();
29731             if (f.inputType =='radio') {
29732                 if (typeof(ret[f.getName()]) == 'undefined') {
29733                     ret[f.getName()] = ''; // empty..
29734                 }
29735                 
29736                 if (!f.el.dom.checked) {
29737                     return;
29738                     
29739                 }
29740                 v = f.el.dom.value;
29741                 
29742             }
29743             
29744             // not sure if this supported any more..
29745             if ((typeof(v) == 'object') && f.getRawValue) {
29746                 v = f.getRawValue() ; // dates..
29747             }
29748             // combo boxes where name != hiddenName...
29749             if (f.name != f.getName()) {
29750                 ret[f.name] = f.getRawValue();
29751             }
29752             ret[f.getName()] = v;
29753         });
29754         
29755         return ret;
29756     },
29757
29758     /**
29759      * Clears all invalid messages in this form.
29760      * @return {BasicForm} this
29761      */
29762     clearInvalid : function(){
29763         this.items.each(function(f){
29764            f.clearInvalid();
29765         });
29766         
29767         Roo.each(this.childForms || [], function (f) {
29768             f.clearInvalid();
29769         });
29770         
29771         
29772         return this;
29773     },
29774
29775     /**
29776      * Resets this form.
29777      * @return {BasicForm} this
29778      */
29779     reset : function(){
29780         this.items.each(function(f){
29781             f.reset();
29782         });
29783         
29784         Roo.each(this.childForms || [], function (f) {
29785             f.reset();
29786         });
29787         this.resetHasChanged();
29788         
29789         return this;
29790     },
29791
29792     /**
29793      * Add Roo.form components to this form.
29794      * @param {Field} field1
29795      * @param {Field} field2 (optional)
29796      * @param {Field} etc (optional)
29797      * @return {BasicForm} this
29798      */
29799     add : function(){
29800         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29801         return this;
29802     },
29803
29804
29805     /**
29806      * Removes a field from the items collection (does NOT remove its markup).
29807      * @param {Field} field
29808      * @return {BasicForm} this
29809      */
29810     remove : function(field){
29811         this.items.remove(field);
29812         return this;
29813     },
29814
29815     /**
29816      * Looks at the fields in this form, checks them for an id attribute,
29817      * and calls applyTo on the existing dom element with that id.
29818      * @return {BasicForm} this
29819      */
29820     render : function(){
29821         this.items.each(function(f){
29822             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29823                 f.applyTo(f.id);
29824             }
29825         });
29826         return this;
29827     },
29828
29829     /**
29830      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29831      * @param {Object} values
29832      * @return {BasicForm} this
29833      */
29834     applyToFields : function(o){
29835         this.items.each(function(f){
29836            Roo.apply(f, o);
29837         });
29838         return this;
29839     },
29840
29841     /**
29842      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29843      * @param {Object} values
29844      * @return {BasicForm} this
29845      */
29846     applyIfToFields : function(o){
29847         this.items.each(function(f){
29848            Roo.applyIf(f, o);
29849         });
29850         return this;
29851     }
29852 });
29853
29854 // back compat
29855 Roo.BasicForm = Roo.form.BasicForm;
29856
29857 Roo.apply(Roo.form.BasicForm, {
29858     
29859     popover : {
29860         
29861         padding : 5,
29862         
29863         isApplied : false,
29864         
29865         isMasked : false,
29866         
29867         form : false,
29868         
29869         target : false,
29870         
29871         intervalID : false,
29872         
29873         maskEl : false,
29874         
29875         apply : function()
29876         {
29877             if(this.isApplied){
29878                 return;
29879             }
29880             
29881             this.maskEl = {
29882                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29883                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29884                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29885                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29886             };
29887             
29888             this.maskEl.top.enableDisplayMode("block");
29889             this.maskEl.left.enableDisplayMode("block");
29890             this.maskEl.bottom.enableDisplayMode("block");
29891             this.maskEl.right.enableDisplayMode("block");
29892             
29893             Roo.get(document.body).on('click', function(){
29894                 this.unmask();
29895             }, this);
29896             
29897             Roo.get(document.body).on('touchstart', function(){
29898                 this.unmask();
29899             }, this);
29900             
29901             this.isApplied = true
29902         },
29903         
29904         mask : function(form, target)
29905         {
29906             this.form = form;
29907             
29908             this.target = target;
29909             
29910             if(!this.form.errorMask || !target.el){
29911                 return;
29912             }
29913             
29914             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29915             
29916             var ot = this.target.el.calcOffsetsTo(scrollable);
29917             
29918             var scrollTo = ot[1] - this.form.maskOffset;
29919             
29920             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29921             
29922             scrollable.scrollTo('top', scrollTo);
29923             
29924             var el = this.target.wrap || this.target.el;
29925             
29926             var box = el.getBox();
29927             
29928             this.maskEl.top.setStyle('position', 'absolute');
29929             this.maskEl.top.setStyle('z-index', 10000);
29930             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29931             this.maskEl.top.setLeft(0);
29932             this.maskEl.top.setTop(0);
29933             this.maskEl.top.show();
29934             
29935             this.maskEl.left.setStyle('position', 'absolute');
29936             this.maskEl.left.setStyle('z-index', 10000);
29937             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29938             this.maskEl.left.setLeft(0);
29939             this.maskEl.left.setTop(box.y - this.padding);
29940             this.maskEl.left.show();
29941
29942             this.maskEl.bottom.setStyle('position', 'absolute');
29943             this.maskEl.bottom.setStyle('z-index', 10000);
29944             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29945             this.maskEl.bottom.setLeft(0);
29946             this.maskEl.bottom.setTop(box.bottom + this.padding);
29947             this.maskEl.bottom.show();
29948
29949             this.maskEl.right.setStyle('position', 'absolute');
29950             this.maskEl.right.setStyle('z-index', 10000);
29951             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29952             this.maskEl.right.setLeft(box.right + this.padding);
29953             this.maskEl.right.setTop(box.y - this.padding);
29954             this.maskEl.right.show();
29955
29956             this.intervalID = window.setInterval(function() {
29957                 Roo.form.BasicForm.popover.unmask();
29958             }, 10000);
29959
29960             window.onwheel = function(){ return false;};
29961             
29962             (function(){ this.isMasked = true; }).defer(500, this);
29963             
29964         },
29965         
29966         unmask : function()
29967         {
29968             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29969                 return;
29970             }
29971             
29972             this.maskEl.top.setStyle('position', 'absolute');
29973             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29974             this.maskEl.top.hide();
29975
29976             this.maskEl.left.setStyle('position', 'absolute');
29977             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29978             this.maskEl.left.hide();
29979
29980             this.maskEl.bottom.setStyle('position', 'absolute');
29981             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29982             this.maskEl.bottom.hide();
29983
29984             this.maskEl.right.setStyle('position', 'absolute');
29985             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29986             this.maskEl.right.hide();
29987             
29988             window.onwheel = function(){ return true;};
29989             
29990             if(this.intervalID){
29991                 window.clearInterval(this.intervalID);
29992                 this.intervalID = false;
29993             }
29994             
29995             this.isMasked = false;
29996             
29997         }
29998         
29999     }
30000     
30001 });/*
30002  * Based on:
30003  * Ext JS Library 1.1.1
30004  * Copyright(c) 2006-2007, Ext JS, LLC.
30005  *
30006  * Originally Released Under LGPL - original licence link has changed is not relivant.
30007  *
30008  * Fork - LGPL
30009  * <script type="text/javascript">
30010  */
30011
30012 /**
30013  * @class Roo.form.Form
30014  * @extends Roo.form.BasicForm
30015  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30016  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
30017  * @constructor
30018  * @param {Object} config Configuration options
30019  */
30020 Roo.form.Form = function(config){
30021     var xitems =  [];
30022     if (config.items) {
30023         xitems = config.items;
30024         delete config.items;
30025     }
30026    
30027     
30028     Roo.form.Form.superclass.constructor.call(this, null, config);
30029     this.url = this.url || this.action;
30030     if(!this.root){
30031         this.root = new Roo.form.Layout(Roo.applyIf({
30032             id: Roo.id()
30033         }, config));
30034     }
30035     this.active = this.root;
30036     /**
30037      * Array of all the buttons that have been added to this form via {@link addButton}
30038      * @type Array
30039      */
30040     this.buttons = [];
30041     this.allItems = [];
30042     this.addEvents({
30043         /**
30044          * @event clientvalidation
30045          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30046          * @param {Form} this
30047          * @param {Boolean} valid true if the form has passed client-side validation
30048          */
30049         clientvalidation: true,
30050         /**
30051          * @event rendered
30052          * Fires when the form is rendered
30053          * @param {Roo.form.Form} form
30054          */
30055         rendered : true
30056     });
30057     
30058     if (this.progressUrl) {
30059             // push a hidden field onto the list of fields..
30060             this.addxtype( {
30061                     xns: Roo.form, 
30062                     xtype : 'Hidden', 
30063                     name : 'UPLOAD_IDENTIFIER' 
30064             });
30065         }
30066         
30067     
30068     Roo.each(xitems, this.addxtype, this);
30069     
30070 };
30071
30072 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
30073      /**
30074      * @cfg {Roo.Button} buttons[] buttons at bottom of form
30075      */
30076     
30077     /**
30078      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
30079      */
30080     /**
30081      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
30082      */
30083     /**
30084      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
30085      */
30086     buttonAlign:'center',
30087
30088     /**
30089      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
30090      */
30091     minButtonWidth:75,
30092
30093     /**
30094      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
30095      * This property cascades to child containers if not set.
30096      */
30097     labelAlign:'left',
30098
30099     /**
30100      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
30101      * fires a looping event with that state. This is required to bind buttons to the valid
30102      * state using the config value formBind:true on the button.
30103      */
30104     monitorValid : false,
30105
30106     /**
30107      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
30108      */
30109     monitorPoll : 200,
30110     
30111     /**
30112      * @cfg {String} progressUrl - Url to return progress data 
30113      */
30114     
30115     progressUrl : false,
30116     /**
30117      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
30118      * sending a formdata with extra parameters - eg uploaded elements.
30119      */
30120     
30121     formData : false,
30122     
30123     /**
30124      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
30125      * fields are added and the column is closed. If no fields are passed the column remains open
30126      * until end() is called.
30127      * @param {Object} config The config to pass to the column
30128      * @param {Field} field1 (optional)
30129      * @param {Field} field2 (optional)
30130      * @param {Field} etc (optional)
30131      * @return Column The column container object
30132      */
30133     column : function(c){
30134         var col = new Roo.form.Column(c);
30135         this.start(col);
30136         if(arguments.length > 1){ // duplicate code required because of Opera
30137             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30138             this.end();
30139         }
30140         return col;
30141     },
30142
30143     /**
30144      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
30145      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
30146      * until end() is called.
30147      * @param {Object} config The config to pass to the fieldset
30148      * @param {Field} field1 (optional)
30149      * @param {Field} field2 (optional)
30150      * @param {Field} etc (optional)
30151      * @return FieldSet The fieldset container object
30152      */
30153     fieldset : function(c){
30154         var fs = new Roo.form.FieldSet(c);
30155         this.start(fs);
30156         if(arguments.length > 1){ // duplicate code required because of Opera
30157             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30158             this.end();
30159         }
30160         return fs;
30161     },
30162
30163     /**
30164      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
30165      * fields are added and the container is closed. If no fields are passed the container remains open
30166      * until end() is called.
30167      * @param {Object} config The config to pass to the Layout
30168      * @param {Field} field1 (optional)
30169      * @param {Field} field2 (optional)
30170      * @param {Field} etc (optional)
30171      * @return Layout The container object
30172      */
30173     container : function(c){
30174         var l = new Roo.form.Layout(c);
30175         this.start(l);
30176         if(arguments.length > 1){ // duplicate code required because of Opera
30177             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30178             this.end();
30179         }
30180         return l;
30181     },
30182
30183     /**
30184      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
30185      * @param {Object} container A Roo.form.Layout or subclass of Layout
30186      * @return {Form} this
30187      */
30188     start : function(c){
30189         // cascade label info
30190         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
30191         this.active.stack.push(c);
30192         c.ownerCt = this.active;
30193         this.active = c;
30194         return this;
30195     },
30196
30197     /**
30198      * Closes the current open container
30199      * @return {Form} this
30200      */
30201     end : function(){
30202         if(this.active == this.root){
30203             return this;
30204         }
30205         this.active = this.active.ownerCt;
30206         return this;
30207     },
30208
30209     /**
30210      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
30211      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30212      * as the label of the field.
30213      * @param {Field} field1
30214      * @param {Field} field2 (optional)
30215      * @param {Field} etc. (optional)
30216      * @return {Form} this
30217      */
30218     add : function(){
30219         this.active.stack.push.apply(this.active.stack, arguments);
30220         this.allItems.push.apply(this.allItems,arguments);
30221         var r = [];
30222         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30223             if(a[i].isFormField){
30224                 r.push(a[i]);
30225             }
30226         }
30227         if(r.length > 0){
30228             Roo.form.Form.superclass.add.apply(this, r);
30229         }
30230         return this;
30231     },
30232     
30233
30234     
30235     
30236     
30237      /**
30238      * Find any element that has been added to a form, using it's ID or name
30239      * This can include framesets, columns etc. along with regular fields..
30240      * @param {String} id - id or name to find.
30241      
30242      * @return {Element} e - or false if nothing found.
30243      */
30244     findbyId : function(id)
30245     {
30246         var ret = false;
30247         if (!id) {
30248             return ret;
30249         }
30250         Roo.each(this.allItems, function(f){
30251             if (f.id == id || f.name == id ){
30252                 ret = f;
30253                 return false;
30254             }
30255         });
30256         return ret;
30257     },
30258
30259     
30260     
30261     /**
30262      * Render this form into the passed container. This should only be called once!
30263      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30264      * @return {Form} this
30265      */
30266     render : function(ct)
30267     {
30268         
30269         
30270         
30271         ct = Roo.get(ct);
30272         var o = this.autoCreate || {
30273             tag: 'form',
30274             method : this.method || 'POST',
30275             id : this.id || Roo.id()
30276         };
30277         this.initEl(ct.createChild(o));
30278
30279         this.root.render(this.el);
30280         
30281        
30282              
30283         this.items.each(function(f){
30284             f.render('x-form-el-'+f.id);
30285         });
30286
30287         if(this.buttons.length > 0){
30288             // tables are required to maintain order and for correct IE layout
30289             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30290                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30291                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30292             }}, null, true);
30293             var tr = tb.getElementsByTagName('tr')[0];
30294             for(var i = 0, len = this.buttons.length; i < len; i++) {
30295                 var b = this.buttons[i];
30296                 var td = document.createElement('td');
30297                 td.className = 'x-form-btn-td';
30298                 b.render(tr.appendChild(td));
30299             }
30300         }
30301         if(this.monitorValid){ // initialize after render
30302             this.startMonitoring();
30303         }
30304         this.fireEvent('rendered', this);
30305         return this;
30306     },
30307
30308     /**
30309      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30310      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30311      * object or a valid Roo.DomHelper element config
30312      * @param {Function} handler The function called when the button is clicked
30313      * @param {Object} scope (optional) The scope of the handler function
30314      * @return {Roo.Button}
30315      */
30316     addButton : function(config, handler, scope){
30317         var bc = {
30318             handler: handler,
30319             scope: scope,
30320             minWidth: this.minButtonWidth,
30321             hideParent:true
30322         };
30323         if(typeof config == "string"){
30324             bc.text = config;
30325         }else{
30326             Roo.apply(bc, config);
30327         }
30328         var btn = new Roo.Button(null, bc);
30329         this.buttons.push(btn);
30330         return btn;
30331     },
30332
30333      /**
30334      * Adds a series of form elements (using the xtype property as the factory method.
30335      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30336      * @param {Object} config 
30337      */
30338     
30339     addxtype : function()
30340     {
30341         var ar = Array.prototype.slice.call(arguments, 0);
30342         var ret = false;
30343         for(var i = 0; i < ar.length; i++) {
30344             if (!ar[i]) {
30345                 continue; // skip -- if this happends something invalid got sent, we 
30346                 // should ignore it, as basically that interface element will not show up
30347                 // and that should be pretty obvious!!
30348             }
30349             
30350             if (Roo.form[ar[i].xtype]) {
30351                 ar[i].form = this;
30352                 var fe = Roo.factory(ar[i], Roo.form);
30353                 if (!ret) {
30354                     ret = fe;
30355                 }
30356                 fe.form = this;
30357                 if (fe.store) {
30358                     fe.store.form = this;
30359                 }
30360                 if (fe.isLayout) {  
30361                          
30362                     this.start(fe);
30363                     this.allItems.push(fe);
30364                     if (fe.items && fe.addxtype) {
30365                         fe.addxtype.apply(fe, fe.items);
30366                         delete fe.items;
30367                     }
30368                      this.end();
30369                     continue;
30370                 }
30371                 
30372                 
30373                  
30374                 this.add(fe);
30375               //  console.log('adding ' + ar[i].xtype);
30376             }
30377             if (ar[i].xtype == 'Button') {  
30378                 //console.log('adding button');
30379                 //console.log(ar[i]);
30380                 this.addButton(ar[i]);
30381                 this.allItems.push(fe);
30382                 continue;
30383             }
30384             
30385             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30386                 alert('end is not supported on xtype any more, use items');
30387             //    this.end();
30388             //    //console.log('adding end');
30389             }
30390             
30391         }
30392         return ret;
30393     },
30394     
30395     /**
30396      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30397      * option "monitorValid"
30398      */
30399     startMonitoring : function(){
30400         if(!this.bound){
30401             this.bound = true;
30402             Roo.TaskMgr.start({
30403                 run : this.bindHandler,
30404                 interval : this.monitorPoll || 200,
30405                 scope: this
30406             });
30407         }
30408     },
30409
30410     /**
30411      * Stops monitoring of the valid state of this form
30412      */
30413     stopMonitoring : function(){
30414         this.bound = false;
30415     },
30416
30417     // private
30418     bindHandler : function(){
30419         if(!this.bound){
30420             return false; // stops binding
30421         }
30422         var valid = true;
30423         this.items.each(function(f){
30424             if(!f.isValid(true)){
30425                 valid = false;
30426                 return false;
30427             }
30428         });
30429         for(var i = 0, len = this.buttons.length; i < len; i++){
30430             var btn = this.buttons[i];
30431             if(btn.formBind === true && btn.disabled === valid){
30432                 btn.setDisabled(!valid);
30433             }
30434         }
30435         this.fireEvent('clientvalidation', this, valid);
30436     }
30437     
30438     
30439     
30440     
30441     
30442     
30443     
30444     
30445 });
30446
30447
30448 // back compat
30449 Roo.Form = Roo.form.Form;
30450 /*
30451  * Based on:
30452  * Ext JS Library 1.1.1
30453  * Copyright(c) 2006-2007, Ext JS, LLC.
30454  *
30455  * Originally Released Under LGPL - original licence link has changed is not relivant.
30456  *
30457  * Fork - LGPL
30458  * <script type="text/javascript">
30459  */
30460
30461 // as we use this in bootstrap.
30462 Roo.namespace('Roo.form');
30463  /**
30464  * @class Roo.form.Action
30465  * Internal Class used to handle form actions
30466  * @constructor
30467  * @param {Roo.form.BasicForm} el The form element or its id
30468  * @param {Object} config Configuration options
30469  */
30470
30471  
30472  
30473 // define the action interface
30474 Roo.form.Action = function(form, options){
30475     this.form = form;
30476     this.options = options || {};
30477 };
30478 /**
30479  * Client Validation Failed
30480  * @const 
30481  */
30482 Roo.form.Action.CLIENT_INVALID = 'client';
30483 /**
30484  * Server Validation Failed
30485  * @const 
30486  */
30487 Roo.form.Action.SERVER_INVALID = 'server';
30488  /**
30489  * Connect to Server Failed
30490  * @const 
30491  */
30492 Roo.form.Action.CONNECT_FAILURE = 'connect';
30493 /**
30494  * Reading Data from Server Failed
30495  * @const 
30496  */
30497 Roo.form.Action.LOAD_FAILURE = 'load';
30498
30499 Roo.form.Action.prototype = {
30500     type : 'default',
30501     failureType : undefined,
30502     response : undefined,
30503     result : undefined,
30504
30505     // interface method
30506     run : function(options){
30507
30508     },
30509
30510     // interface method
30511     success : function(response){
30512
30513     },
30514
30515     // interface method
30516     handleResponse : function(response){
30517
30518     },
30519
30520     // default connection failure
30521     failure : function(response){
30522         
30523         this.response = response;
30524         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30525         this.form.afterAction(this, false);
30526     },
30527
30528     processResponse : function(response){
30529         this.response = response;
30530         if(!response.responseText){
30531             return true;
30532         }
30533         this.result = this.handleResponse(response);
30534         return this.result;
30535     },
30536
30537     // utility functions used internally
30538     getUrl : function(appendParams){
30539         var url = this.options.url || this.form.url || this.form.el.dom.action;
30540         if(appendParams){
30541             var p = this.getParams();
30542             if(p){
30543                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30544             }
30545         }
30546         return url;
30547     },
30548
30549     getMethod : function(){
30550         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30551     },
30552
30553     getParams : function(){
30554         var bp = this.form.baseParams;
30555         var p = this.options.params;
30556         if(p){
30557             if(typeof p == "object"){
30558                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30559             }else if(typeof p == 'string' && bp){
30560                 p += '&' + Roo.urlEncode(bp);
30561             }
30562         }else if(bp){
30563             p = Roo.urlEncode(bp);
30564         }
30565         return p;
30566     },
30567
30568     createCallback : function(){
30569         return {
30570             success: this.success,
30571             failure: this.failure,
30572             scope: this,
30573             timeout: (this.form.timeout*1000),
30574             upload: this.form.fileUpload ? this.success : undefined
30575         };
30576     }
30577 };
30578
30579 Roo.form.Action.Submit = function(form, options){
30580     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30581 };
30582
30583 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30584     type : 'submit',
30585
30586     haveProgress : false,
30587     uploadComplete : false,
30588     
30589     // uploadProgress indicator.
30590     uploadProgress : function()
30591     {
30592         if (!this.form.progressUrl) {
30593             return;
30594         }
30595         
30596         if (!this.haveProgress) {
30597             Roo.MessageBox.progress("Uploading", "Uploading");
30598         }
30599         if (this.uploadComplete) {
30600            Roo.MessageBox.hide();
30601            return;
30602         }
30603         
30604         this.haveProgress = true;
30605    
30606         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30607         
30608         var c = new Roo.data.Connection();
30609         c.request({
30610             url : this.form.progressUrl,
30611             params: {
30612                 id : uid
30613             },
30614             method: 'GET',
30615             success : function(req){
30616                //console.log(data);
30617                 var rdata = false;
30618                 var edata;
30619                 try  {
30620                    rdata = Roo.decode(req.responseText)
30621                 } catch (e) {
30622                     Roo.log("Invalid data from server..");
30623                     Roo.log(edata);
30624                     return;
30625                 }
30626                 if (!rdata || !rdata.success) {
30627                     Roo.log(rdata);
30628                     Roo.MessageBox.alert(Roo.encode(rdata));
30629                     return;
30630                 }
30631                 var data = rdata.data;
30632                 
30633                 if (this.uploadComplete) {
30634                    Roo.MessageBox.hide();
30635                    return;
30636                 }
30637                    
30638                 if (data){
30639                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30640                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30641                     );
30642                 }
30643                 this.uploadProgress.defer(2000,this);
30644             },
30645        
30646             failure: function(data) {
30647                 Roo.log('progress url failed ');
30648                 Roo.log(data);
30649             },
30650             scope : this
30651         });
30652            
30653     },
30654     
30655     
30656     run : function()
30657     {
30658         // run get Values on the form, so it syncs any secondary forms.
30659         this.form.getValues();
30660         
30661         var o = this.options;
30662         var method = this.getMethod();
30663         var isPost = method == 'POST';
30664         if(o.clientValidation === false || this.form.isValid()){
30665             
30666             if (this.form.progressUrl) {
30667                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30668                     (new Date() * 1) + '' + Math.random());
30669                     
30670             } 
30671             
30672             
30673             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30674                 form:this.form.el.dom,
30675                 url:this.getUrl(!isPost),
30676                 method: method,
30677                 params:isPost ? this.getParams() : null,
30678                 isUpload: this.form.fileUpload,
30679                 formData : this.form.formData
30680             }));
30681             
30682             this.uploadProgress();
30683
30684         }else if (o.clientValidation !== false){ // client validation failed
30685             this.failureType = Roo.form.Action.CLIENT_INVALID;
30686             this.form.afterAction(this, false);
30687         }
30688     },
30689
30690     success : function(response)
30691     {
30692         this.uploadComplete= true;
30693         if (this.haveProgress) {
30694             Roo.MessageBox.hide();
30695         }
30696         
30697         
30698         var result = this.processResponse(response);
30699         if(result === true || result.success){
30700             this.form.afterAction(this, true);
30701             return;
30702         }
30703         if(result.errors){
30704             this.form.markInvalid(result.errors);
30705             this.failureType = Roo.form.Action.SERVER_INVALID;
30706         }
30707         this.form.afterAction(this, false);
30708     },
30709     failure : function(response)
30710     {
30711         this.uploadComplete= true;
30712         if (this.haveProgress) {
30713             Roo.MessageBox.hide();
30714         }
30715         
30716         this.response = response;
30717         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30718         this.form.afterAction(this, false);
30719     },
30720     
30721     handleResponse : function(response){
30722         if(this.form.errorReader){
30723             var rs = this.form.errorReader.read(response);
30724             var errors = [];
30725             if(rs.records){
30726                 for(var i = 0, len = rs.records.length; i < len; i++) {
30727                     var r = rs.records[i];
30728                     errors[i] = r.data;
30729                 }
30730             }
30731             if(errors.length < 1){
30732                 errors = null;
30733             }
30734             return {
30735                 success : rs.success,
30736                 errors : errors
30737             };
30738         }
30739         var ret = false;
30740         try {
30741             ret = Roo.decode(response.responseText);
30742         } catch (e) {
30743             ret = {
30744                 success: false,
30745                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30746                 errors : []
30747             };
30748         }
30749         return ret;
30750         
30751     }
30752 });
30753
30754
30755 Roo.form.Action.Load = function(form, options){
30756     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30757     this.reader = this.form.reader;
30758 };
30759
30760 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30761     type : 'load',
30762
30763     run : function(){
30764         
30765         Roo.Ajax.request(Roo.apply(
30766                 this.createCallback(), {
30767                     method:this.getMethod(),
30768                     url:this.getUrl(false),
30769                     params:this.getParams()
30770         }));
30771     },
30772
30773     success : function(response){
30774         
30775         var result = this.processResponse(response);
30776         if(result === true || !result.success || !result.data){
30777             this.failureType = Roo.form.Action.LOAD_FAILURE;
30778             this.form.afterAction(this, false);
30779             return;
30780         }
30781         this.form.clearInvalid();
30782         this.form.setValues(result.data);
30783         this.form.afterAction(this, true);
30784     },
30785
30786     handleResponse : function(response){
30787         if(this.form.reader){
30788             var rs = this.form.reader.read(response);
30789             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30790             return {
30791                 success : rs.success,
30792                 data : data
30793             };
30794         }
30795         return Roo.decode(response.responseText);
30796     }
30797 });
30798
30799 Roo.form.Action.ACTION_TYPES = {
30800     'load' : Roo.form.Action.Load,
30801     'submit' : Roo.form.Action.Submit
30802 };/*
30803  * Based on:
30804  * Ext JS Library 1.1.1
30805  * Copyright(c) 2006-2007, Ext JS, LLC.
30806  *
30807  * Originally Released Under LGPL - original licence link has changed is not relivant.
30808  *
30809  * Fork - LGPL
30810  * <script type="text/javascript">
30811  */
30812  
30813 /**
30814  * @class Roo.form.Layout
30815  * @extends Roo.Component
30816  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30817  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30818  * @constructor
30819  * @param {Object} config Configuration options
30820  */
30821 Roo.form.Layout = function(config){
30822     var xitems = [];
30823     if (config.items) {
30824         xitems = config.items;
30825         delete config.items;
30826     }
30827     Roo.form.Layout.superclass.constructor.call(this, config);
30828     this.stack = [];
30829     Roo.each(xitems, this.addxtype, this);
30830      
30831 };
30832
30833 Roo.extend(Roo.form.Layout, Roo.Component, {
30834     /**
30835      * @cfg {String/Object} autoCreate
30836      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30837      */
30838     /**
30839      * @cfg {String/Object/Function} style
30840      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30841      * a function which returns such a specification.
30842      */
30843     /**
30844      * @cfg {String} labelAlign (left|top|right)
30845      * Valid values are "left," "top" and "right" (defaults to "left")
30846      */
30847     /**
30848      * @cfg {Number} labelWidth
30849      * Fixed width in pixels of all field labels (defaults to undefined)
30850      */
30851     /**
30852      * @cfg {Boolean} clear
30853      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30854      */
30855     clear : true,
30856     /**
30857      * @cfg {String} labelSeparator
30858      * The separator to use after field labels (defaults to ':')
30859      */
30860     labelSeparator : ':',
30861     /**
30862      * @cfg {Boolean} hideLabels
30863      * True to suppress the display of field labels in this layout (defaults to false)
30864      */
30865     hideLabels : false,
30866
30867     // private
30868     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30869     
30870     isLayout : true,
30871     
30872     // private
30873     onRender : function(ct, position){
30874         if(this.el){ // from markup
30875             this.el = Roo.get(this.el);
30876         }else {  // generate
30877             var cfg = this.getAutoCreate();
30878             this.el = ct.createChild(cfg, position);
30879         }
30880         if(this.style){
30881             this.el.applyStyles(this.style);
30882         }
30883         if(this.labelAlign){
30884             this.el.addClass('x-form-label-'+this.labelAlign);
30885         }
30886         if(this.hideLabels){
30887             this.labelStyle = "display:none";
30888             this.elementStyle = "padding-left:0;";
30889         }else{
30890             if(typeof this.labelWidth == 'number'){
30891                 this.labelStyle = "width:"+this.labelWidth+"px;";
30892                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30893             }
30894             if(this.labelAlign == 'top'){
30895                 this.labelStyle = "width:auto;";
30896                 this.elementStyle = "padding-left:0;";
30897             }
30898         }
30899         var stack = this.stack;
30900         var slen = stack.length;
30901         if(slen > 0){
30902             if(!this.fieldTpl){
30903                 var t = new Roo.Template(
30904                     '<div class="x-form-item {5}">',
30905                         '<label for="{0}" style="{2}">{1}{4}</label>',
30906                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30907                         '</div>',
30908                     '</div><div class="x-form-clear-left"></div>'
30909                 );
30910                 t.disableFormats = true;
30911                 t.compile();
30912                 Roo.form.Layout.prototype.fieldTpl = t;
30913             }
30914             for(var i = 0; i < slen; i++) {
30915                 if(stack[i].isFormField){
30916                     this.renderField(stack[i]);
30917                 }else{
30918                     this.renderComponent(stack[i]);
30919                 }
30920             }
30921         }
30922         if(this.clear){
30923             this.el.createChild({cls:'x-form-clear'});
30924         }
30925     },
30926
30927     // private
30928     renderField : function(f){
30929         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30930                f.id, //0
30931                f.fieldLabel, //1
30932                f.labelStyle||this.labelStyle||'', //2
30933                this.elementStyle||'', //3
30934                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30935                f.itemCls||this.itemCls||''  //5
30936        ], true).getPrevSibling());
30937     },
30938
30939     // private
30940     renderComponent : function(c){
30941         c.render(c.isLayout ? this.el : this.el.createChild());    
30942     },
30943     /**
30944      * Adds a object form elements (using the xtype property as the factory method.)
30945      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30946      * @param {Object} config 
30947      */
30948     addxtype : function(o)
30949     {
30950         // create the lement.
30951         o.form = this.form;
30952         var fe = Roo.factory(o, Roo.form);
30953         this.form.allItems.push(fe);
30954         this.stack.push(fe);
30955         
30956         if (fe.isFormField) {
30957             this.form.items.add(fe);
30958         }
30959          
30960         return fe;
30961     }
30962 });
30963
30964
30965 /**
30966  * @class Roo.form.Column
30967  * @extends Roo.form.Layout
30968  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30969  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30970  * @constructor
30971  * @param {Object} config Configuration options
30972  */
30973 Roo.form.Column = function(config){
30974     Roo.form.Column.superclass.constructor.call(this, config);
30975 };
30976
30977 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30978     /**
30979      * @cfg {Number/String} width
30980      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30981      */
30982     /**
30983      * @cfg {String/Object} autoCreate
30984      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30985      */
30986
30987     // private
30988     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30989
30990     // private
30991     onRender : function(ct, position){
30992         Roo.form.Column.superclass.onRender.call(this, ct, position);
30993         if(this.width){
30994             this.el.setWidth(this.width);
30995         }
30996     }
30997 });
30998
30999 /**
31000  * @class Roo.form.Row
31001  * @extends Roo.form.Layout
31002  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
31003  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
31004  * @constructor
31005  * @param {Object} config Configuration options
31006  */
31007
31008  
31009 Roo.form.Row = function(config){
31010     Roo.form.Row.superclass.constructor.call(this, config);
31011 };
31012  
31013 Roo.extend(Roo.form.Row, Roo.form.Layout, {
31014       /**
31015      * @cfg {Number/String} width
31016      * The fixed width of the column in pixels or CSS value (defaults to "auto")
31017      */
31018     /**
31019      * @cfg {Number/String} height
31020      * The fixed height of the column in pixels or CSS value (defaults to "auto")
31021      */
31022     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
31023     
31024     padWidth : 20,
31025     // private
31026     onRender : function(ct, position){
31027         //console.log('row render');
31028         if(!this.rowTpl){
31029             var t = new Roo.Template(
31030                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
31031                     '<label for="{0}" style="{2}">{1}{4}</label>',
31032                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
31033                     '</div>',
31034                 '</div>'
31035             );
31036             t.disableFormats = true;
31037             t.compile();
31038             Roo.form.Layout.prototype.rowTpl = t;
31039         }
31040         this.fieldTpl = this.rowTpl;
31041         
31042         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
31043         var labelWidth = 100;
31044         
31045         if ((this.labelAlign != 'top')) {
31046             if (typeof this.labelWidth == 'number') {
31047                 labelWidth = this.labelWidth
31048             }
31049             this.padWidth =  20 + labelWidth;
31050             
31051         }
31052         
31053         Roo.form.Column.superclass.onRender.call(this, ct, position);
31054         if(this.width){
31055             this.el.setWidth(this.width);
31056         }
31057         if(this.height){
31058             this.el.setHeight(this.height);
31059         }
31060     },
31061     
31062     // private
31063     renderField : function(f){
31064         f.fieldEl = this.fieldTpl.append(this.el, [
31065                f.id, f.fieldLabel,
31066                f.labelStyle||this.labelStyle||'',
31067                this.elementStyle||'',
31068                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
31069                f.itemCls||this.itemCls||'',
31070                f.width ? f.width + this.padWidth : 160 + this.padWidth
31071        ],true);
31072     }
31073 });
31074  
31075
31076 /**
31077  * @class Roo.form.FieldSet
31078  * @extends Roo.form.Layout
31079  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31080  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
31081  * @constructor
31082  * @param {Object} config Configuration options
31083  */
31084 Roo.form.FieldSet = function(config){
31085     Roo.form.FieldSet.superclass.constructor.call(this, config);
31086 };
31087
31088 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
31089     /**
31090      * @cfg {String} legend
31091      * The text to display as the legend for the FieldSet (defaults to '')
31092      */
31093     /**
31094      * @cfg {String/Object} autoCreate
31095      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
31096      */
31097
31098     // private
31099     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
31100
31101     // private
31102     onRender : function(ct, position){
31103         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
31104         if(this.legend){
31105             this.setLegend(this.legend);
31106         }
31107     },
31108
31109     // private
31110     setLegend : function(text){
31111         if(this.rendered){
31112             this.el.child('legend').update(text);
31113         }
31114     }
31115 });/*
31116  * Based on:
31117  * Ext JS Library 1.1.1
31118  * Copyright(c) 2006-2007, Ext JS, LLC.
31119  *
31120  * Originally Released Under LGPL - original licence link has changed is not relivant.
31121  *
31122  * Fork - LGPL
31123  * <script type="text/javascript">
31124  */
31125 /**
31126  * @class Roo.form.VTypes
31127  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
31128  * @static
31129  */
31130 Roo.form.VTypes = function(){
31131     // closure these in so they are only created once.
31132     var alpha = /^[a-zA-Z_]+$/;
31133     var alphanum = /^[a-zA-Z0-9_]+$/;
31134     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
31135     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31136
31137     // All these messages and functions are configurable
31138     return {
31139         /**
31140          * The function used to validate email addresses
31141          * @param {String} value The email address
31142          */
31143         'email' : function(v){
31144             return email.test(v);
31145         },
31146         /**
31147          * The error text to display when the email validation function returns false
31148          * @type String
31149          */
31150         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
31151         /**
31152          * The keystroke filter mask to be applied on email input
31153          * @type RegExp
31154          */
31155         'emailMask' : /[a-z0-9_\.\-@]/i,
31156
31157         /**
31158          * The function used to validate URLs
31159          * @param {String} value The URL
31160          */
31161         'url' : function(v){
31162             return url.test(v);
31163         },
31164         /**
31165          * The error text to display when the url validation function returns false
31166          * @type String
31167          */
31168         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
31169         
31170         /**
31171          * The function used to validate alpha values
31172          * @param {String} value The value
31173          */
31174         'alpha' : function(v){
31175             return alpha.test(v);
31176         },
31177         /**
31178          * The error text to display when the alpha validation function returns false
31179          * @type String
31180          */
31181         'alphaText' : 'This field should only contain letters and _',
31182         /**
31183          * The keystroke filter mask to be applied on alpha input
31184          * @type RegExp
31185          */
31186         'alphaMask' : /[a-z_]/i,
31187
31188         /**
31189          * The function used to validate alphanumeric values
31190          * @param {String} value The value
31191          */
31192         'alphanum' : function(v){
31193             return alphanum.test(v);
31194         },
31195         /**
31196          * The error text to display when the alphanumeric validation function returns false
31197          * @type String
31198          */
31199         'alphanumText' : 'This field should only contain letters, numbers and _',
31200         /**
31201          * The keystroke filter mask to be applied on alphanumeric input
31202          * @type RegExp
31203          */
31204         'alphanumMask' : /[a-z0-9_]/i
31205     };
31206 }();//<script type="text/javascript">
31207
31208 /**
31209  * @class Roo.form.FCKeditor
31210  * @extends Roo.form.TextArea
31211  * Wrapper around the FCKEditor http://www.fckeditor.net
31212  * @constructor
31213  * Creates a new FCKeditor
31214  * @param {Object} config Configuration options
31215  */
31216 Roo.form.FCKeditor = function(config){
31217     Roo.form.FCKeditor.superclass.constructor.call(this, config);
31218     this.addEvents({
31219          /**
31220          * @event editorinit
31221          * Fired when the editor is initialized - you can add extra handlers here..
31222          * @param {FCKeditor} this
31223          * @param {Object} the FCK object.
31224          */
31225         editorinit : true
31226     });
31227     
31228     
31229 };
31230 Roo.form.FCKeditor.editors = { };
31231 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31232 {
31233     //defaultAutoCreate : {
31234     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
31235     //},
31236     // private
31237     /**
31238      * @cfg {Object} fck options - see fck manual for details.
31239      */
31240     fckconfig : false,
31241     
31242     /**
31243      * @cfg {Object} fck toolbar set (Basic or Default)
31244      */
31245     toolbarSet : 'Basic',
31246     /**
31247      * @cfg {Object} fck BasePath
31248      */ 
31249     basePath : '/fckeditor/',
31250     
31251     
31252     frame : false,
31253     
31254     value : '',
31255     
31256    
31257     onRender : function(ct, position)
31258     {
31259         if(!this.el){
31260             this.defaultAutoCreate = {
31261                 tag: "textarea",
31262                 style:"width:300px;height:60px;",
31263                 autocomplete: "new-password"
31264             };
31265         }
31266         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31267         /*
31268         if(this.grow){
31269             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31270             if(this.preventScrollbars){
31271                 this.el.setStyle("overflow", "hidden");
31272             }
31273             this.el.setHeight(this.growMin);
31274         }
31275         */
31276         //console.log('onrender' + this.getId() );
31277         Roo.form.FCKeditor.editors[this.getId()] = this;
31278          
31279
31280         this.replaceTextarea() ;
31281         
31282     },
31283     
31284     getEditor : function() {
31285         return this.fckEditor;
31286     },
31287     /**
31288      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31289      * @param {Mixed} value The value to set
31290      */
31291     
31292     
31293     setValue : function(value)
31294     {
31295         //console.log('setValue: ' + value);
31296         
31297         if(typeof(value) == 'undefined') { // not sure why this is happending...
31298             return;
31299         }
31300         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31301         
31302         //if(!this.el || !this.getEditor()) {
31303         //    this.value = value;
31304             //this.setValue.defer(100,this,[value]);    
31305         //    return;
31306         //} 
31307         
31308         if(!this.getEditor()) {
31309             return;
31310         }
31311         
31312         this.getEditor().SetData(value);
31313         
31314         //
31315
31316     },
31317
31318     /**
31319      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31320      * @return {Mixed} value The field value
31321      */
31322     getValue : function()
31323     {
31324         
31325         if (this.frame && this.frame.dom.style.display == 'none') {
31326             return Roo.form.FCKeditor.superclass.getValue.call(this);
31327         }
31328         
31329         if(!this.el || !this.getEditor()) {
31330            
31331            // this.getValue.defer(100,this); 
31332             return this.value;
31333         }
31334        
31335         
31336         var value=this.getEditor().GetData();
31337         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31338         return Roo.form.FCKeditor.superclass.getValue.call(this);
31339         
31340
31341     },
31342
31343     /**
31344      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31345      * @return {Mixed} value The field value
31346      */
31347     getRawValue : function()
31348     {
31349         if (this.frame && this.frame.dom.style.display == 'none') {
31350             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31351         }
31352         
31353         if(!this.el || !this.getEditor()) {
31354             //this.getRawValue.defer(100,this); 
31355             return this.value;
31356             return;
31357         }
31358         
31359         
31360         
31361         var value=this.getEditor().GetData();
31362         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31363         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31364          
31365     },
31366     
31367     setSize : function(w,h) {
31368         
31369         
31370         
31371         //if (this.frame && this.frame.dom.style.display == 'none') {
31372         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31373         //    return;
31374         //}
31375         //if(!this.el || !this.getEditor()) {
31376         //    this.setSize.defer(100,this, [w,h]); 
31377         //    return;
31378         //}
31379         
31380         
31381         
31382         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31383         
31384         this.frame.dom.setAttribute('width', w);
31385         this.frame.dom.setAttribute('height', h);
31386         this.frame.setSize(w,h);
31387         
31388     },
31389     
31390     toggleSourceEdit : function(value) {
31391         
31392       
31393          
31394         this.el.dom.style.display = value ? '' : 'none';
31395         this.frame.dom.style.display = value ?  'none' : '';
31396         
31397     },
31398     
31399     
31400     focus: function(tag)
31401     {
31402         if (this.frame.dom.style.display == 'none') {
31403             return Roo.form.FCKeditor.superclass.focus.call(this);
31404         }
31405         if(!this.el || !this.getEditor()) {
31406             this.focus.defer(100,this, [tag]); 
31407             return;
31408         }
31409         
31410         
31411         
31412         
31413         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31414         this.getEditor().Focus();
31415         if (tgs.length) {
31416             if (!this.getEditor().Selection.GetSelection()) {
31417                 this.focus.defer(100,this, [tag]); 
31418                 return;
31419             }
31420             
31421             
31422             var r = this.getEditor().EditorDocument.createRange();
31423             r.setStart(tgs[0],0);
31424             r.setEnd(tgs[0],0);
31425             this.getEditor().Selection.GetSelection().removeAllRanges();
31426             this.getEditor().Selection.GetSelection().addRange(r);
31427             this.getEditor().Focus();
31428         }
31429         
31430     },
31431     
31432     
31433     
31434     replaceTextarea : function()
31435     {
31436         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31437             return ;
31438         }
31439         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31440         //{
31441             // We must check the elements firstly using the Id and then the name.
31442         var oTextarea = document.getElementById( this.getId() );
31443         
31444         var colElementsByName = document.getElementsByName( this.getId() ) ;
31445          
31446         oTextarea.style.display = 'none' ;
31447
31448         if ( oTextarea.tabIndex ) {            
31449             this.TabIndex = oTextarea.tabIndex ;
31450         }
31451         
31452         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31453         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31454         this.frame = Roo.get(this.getId() + '___Frame')
31455     },
31456     
31457     _getConfigHtml : function()
31458     {
31459         var sConfig = '' ;
31460
31461         for ( var o in this.fckconfig ) {
31462             sConfig += sConfig.length > 0  ? '&amp;' : '';
31463             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31464         }
31465
31466         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31467     },
31468     
31469     
31470     _getIFrameHtml : function()
31471     {
31472         var sFile = 'fckeditor.html' ;
31473         /* no idea what this is about..
31474         try
31475         {
31476             if ( (/fcksource=true/i).test( window.top.location.search ) )
31477                 sFile = 'fckeditor.original.html' ;
31478         }
31479         catch (e) { 
31480         */
31481
31482         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31483         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31484         
31485         
31486         var html = '<iframe id="' + this.getId() +
31487             '___Frame" src="' + sLink +
31488             '" width="' + this.width +
31489             '" height="' + this.height + '"' +
31490             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31491             ' frameborder="0" scrolling="no"></iframe>' ;
31492
31493         return html ;
31494     },
31495     
31496     _insertHtmlBefore : function( html, element )
31497     {
31498         if ( element.insertAdjacentHTML )       {
31499             // IE
31500             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31501         } else { // Gecko
31502             var oRange = document.createRange() ;
31503             oRange.setStartBefore( element ) ;
31504             var oFragment = oRange.createContextualFragment( html );
31505             element.parentNode.insertBefore( oFragment, element ) ;
31506         }
31507     }
31508     
31509     
31510   
31511     
31512     
31513     
31514     
31515
31516 });
31517
31518 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31519
31520 function FCKeditor_OnComplete(editorInstance){
31521     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31522     f.fckEditor = editorInstance;
31523     //console.log("loaded");
31524     f.fireEvent('editorinit', f, editorInstance);
31525
31526   
31527
31528  
31529
31530
31531
31532
31533
31534
31535
31536
31537
31538
31539
31540
31541
31542
31543
31544 //<script type="text/javascript">
31545 /**
31546  * @class Roo.form.GridField
31547  * @extends Roo.form.Field
31548  * Embed a grid (or editable grid into a form)
31549  * STATUS ALPHA
31550  * 
31551  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31552  * it needs 
31553  * xgrid.store = Roo.data.Store
31554  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31555  * xgrid.store.reader = Roo.data.JsonReader 
31556  * 
31557  * 
31558  * @constructor
31559  * Creates a new GridField
31560  * @param {Object} config Configuration options
31561  */
31562 Roo.form.GridField = function(config){
31563     Roo.form.GridField.superclass.constructor.call(this, config);
31564      
31565 };
31566
31567 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31568     /**
31569      * @cfg {Number} width  - used to restrict width of grid..
31570      */
31571     width : 100,
31572     /**
31573      * @cfg {Number} height - used to restrict height of grid..
31574      */
31575     height : 50,
31576      /**
31577      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31578          * 
31579          *}
31580      */
31581     xgrid : false, 
31582     /**
31583      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31584      * {tag: "input", type: "checkbox", autocomplete: "off"})
31585      */
31586    // defaultAutoCreate : { tag: 'div' },
31587     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31588     /**
31589      * @cfg {String} addTitle Text to include for adding a title.
31590      */
31591     addTitle : false,
31592     //
31593     onResize : function(){
31594         Roo.form.Field.superclass.onResize.apply(this, arguments);
31595     },
31596
31597     initEvents : function(){
31598         // Roo.form.Checkbox.superclass.initEvents.call(this);
31599         // has no events...
31600        
31601     },
31602
31603
31604     getResizeEl : function(){
31605         return this.wrap;
31606     },
31607
31608     getPositionEl : function(){
31609         return this.wrap;
31610     },
31611
31612     // private
31613     onRender : function(ct, position){
31614         
31615         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31616         var style = this.style;
31617         delete this.style;
31618         
31619         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31620         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31621         this.viewEl = this.wrap.createChild({ tag: 'div' });
31622         if (style) {
31623             this.viewEl.applyStyles(style);
31624         }
31625         if (this.width) {
31626             this.viewEl.setWidth(this.width);
31627         }
31628         if (this.height) {
31629             this.viewEl.setHeight(this.height);
31630         }
31631         //if(this.inputValue !== undefined){
31632         //this.setValue(this.value);
31633         
31634         
31635         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31636         
31637         
31638         this.grid.render();
31639         this.grid.getDataSource().on('remove', this.refreshValue, this);
31640         this.grid.getDataSource().on('update', this.refreshValue, this);
31641         this.grid.on('afteredit', this.refreshValue, this);
31642  
31643     },
31644      
31645     
31646     /**
31647      * Sets the value of the item. 
31648      * @param {String} either an object  or a string..
31649      */
31650     setValue : function(v){
31651         //this.value = v;
31652         v = v || []; // empty set..
31653         // this does not seem smart - it really only affects memoryproxy grids..
31654         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31655             var ds = this.grid.getDataSource();
31656             // assumes a json reader..
31657             var data = {}
31658             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31659             ds.loadData( data);
31660         }
31661         // clear selection so it does not get stale.
31662         if (this.grid.sm) { 
31663             this.grid.sm.clearSelections();
31664         }
31665         
31666         Roo.form.GridField.superclass.setValue.call(this, v);
31667         this.refreshValue();
31668         // should load data in the grid really....
31669     },
31670     
31671     // private
31672     refreshValue: function() {
31673          var val = [];
31674         this.grid.getDataSource().each(function(r) {
31675             val.push(r.data);
31676         });
31677         this.el.dom.value = Roo.encode(val);
31678     }
31679     
31680      
31681     
31682     
31683 });/*
31684  * Based on:
31685  * Ext JS Library 1.1.1
31686  * Copyright(c) 2006-2007, Ext JS, LLC.
31687  *
31688  * Originally Released Under LGPL - original licence link has changed is not relivant.
31689  *
31690  * Fork - LGPL
31691  * <script type="text/javascript">
31692  */
31693 /**
31694  * @class Roo.form.DisplayField
31695  * @extends Roo.form.Field
31696  * A generic Field to display non-editable data.
31697  * @cfg {Boolean} closable (true|false) default false
31698  * @constructor
31699  * Creates a new Display Field item.
31700  * @param {Object} config Configuration options
31701  */
31702 Roo.form.DisplayField = function(config){
31703     Roo.form.DisplayField.superclass.constructor.call(this, config);
31704     
31705     this.addEvents({
31706         /**
31707          * @event close
31708          * Fires after the click the close btn
31709              * @param {Roo.form.DisplayField} this
31710              */
31711         close : true
31712     });
31713 };
31714
31715 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31716     inputType:      'hidden',
31717     allowBlank:     true,
31718     readOnly:         true,
31719     
31720  
31721     /**
31722      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31723      */
31724     focusClass : undefined,
31725     /**
31726      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31727      */
31728     fieldClass: 'x-form-field',
31729     
31730      /**
31731      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31732      */
31733     valueRenderer: undefined,
31734     
31735     width: 100,
31736     /**
31737      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31738      * {tag: "input", type: "checkbox", autocomplete: "off"})
31739      */
31740      
31741  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31742  
31743     closable : false,
31744     
31745     onResize : function(){
31746         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31747         
31748     },
31749
31750     initEvents : function(){
31751         // Roo.form.Checkbox.superclass.initEvents.call(this);
31752         // has no events...
31753         
31754         if(this.closable){
31755             this.closeEl.on('click', this.onClose, this);
31756         }
31757        
31758     },
31759
31760
31761     getResizeEl : function(){
31762         return this.wrap;
31763     },
31764
31765     getPositionEl : function(){
31766         return this.wrap;
31767     },
31768
31769     // private
31770     onRender : function(ct, position){
31771         
31772         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31773         //if(this.inputValue !== undefined){
31774         this.wrap = this.el.wrap();
31775         
31776         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31777         
31778         if(this.closable){
31779             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31780         }
31781         
31782         if (this.bodyStyle) {
31783             this.viewEl.applyStyles(this.bodyStyle);
31784         }
31785         //this.viewEl.setStyle('padding', '2px');
31786         
31787         this.setValue(this.value);
31788         
31789     },
31790 /*
31791     // private
31792     initValue : Roo.emptyFn,
31793
31794   */
31795
31796         // private
31797     onClick : function(){
31798         
31799     },
31800
31801     /**
31802      * Sets the checked state of the checkbox.
31803      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31804      */
31805     setValue : function(v){
31806         this.value = v;
31807         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31808         // this might be called before we have a dom element..
31809         if (!this.viewEl) {
31810             return;
31811         }
31812         this.viewEl.dom.innerHTML = html;
31813         Roo.form.DisplayField.superclass.setValue.call(this, v);
31814
31815     },
31816     
31817     onClose : function(e)
31818     {
31819         e.preventDefault();
31820         
31821         this.fireEvent('close', this);
31822     }
31823 });/*
31824  * 
31825  * Licence- LGPL
31826  * 
31827  */
31828
31829 /**
31830  * @class Roo.form.DayPicker
31831  * @extends Roo.form.Field
31832  * A Day picker show [M] [T] [W] ....
31833  * @constructor
31834  * Creates a new Day Picker
31835  * @param {Object} config Configuration options
31836  */
31837 Roo.form.DayPicker= function(config){
31838     Roo.form.DayPicker.superclass.constructor.call(this, config);
31839      
31840 };
31841
31842 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31843     /**
31844      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31845      */
31846     focusClass : undefined,
31847     /**
31848      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31849      */
31850     fieldClass: "x-form-field",
31851    
31852     /**
31853      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31854      * {tag: "input", type: "checkbox", autocomplete: "off"})
31855      */
31856     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31857     
31858    
31859     actionMode : 'viewEl', 
31860     //
31861     // private
31862  
31863     inputType : 'hidden',
31864     
31865      
31866     inputElement: false, // real input element?
31867     basedOn: false, // ????
31868     
31869     isFormField: true, // not sure where this is needed!!!!
31870
31871     onResize : function(){
31872         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31873         if(!this.boxLabel){
31874             this.el.alignTo(this.wrap, 'c-c');
31875         }
31876     },
31877
31878     initEvents : function(){
31879         Roo.form.Checkbox.superclass.initEvents.call(this);
31880         this.el.on("click", this.onClick,  this);
31881         this.el.on("change", this.onClick,  this);
31882     },
31883
31884
31885     getResizeEl : function(){
31886         return this.wrap;
31887     },
31888
31889     getPositionEl : function(){
31890         return this.wrap;
31891     },
31892
31893     
31894     // private
31895     onRender : function(ct, position){
31896         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31897        
31898         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31899         
31900         var r1 = '<table><tr>';
31901         var r2 = '<tr class="x-form-daypick-icons">';
31902         for (var i=0; i < 7; i++) {
31903             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31904             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31905         }
31906         
31907         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31908         viewEl.select('img').on('click', this.onClick, this);
31909         this.viewEl = viewEl;   
31910         
31911         
31912         // this will not work on Chrome!!!
31913         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31914         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31915         
31916         
31917           
31918
31919     },
31920
31921     // private
31922     initValue : Roo.emptyFn,
31923
31924     /**
31925      * Returns the checked state of the checkbox.
31926      * @return {Boolean} True if checked, else false
31927      */
31928     getValue : function(){
31929         return this.el.dom.value;
31930         
31931     },
31932
31933         // private
31934     onClick : function(e){ 
31935         //this.setChecked(!this.checked);
31936         Roo.get(e.target).toggleClass('x-menu-item-checked');
31937         this.refreshValue();
31938         //if(this.el.dom.checked != this.checked){
31939         //    this.setValue(this.el.dom.checked);
31940        // }
31941     },
31942     
31943     // private
31944     refreshValue : function()
31945     {
31946         var val = '';
31947         this.viewEl.select('img',true).each(function(e,i,n)  {
31948             val += e.is(".x-menu-item-checked") ? String(n) : '';
31949         });
31950         this.setValue(val, true);
31951     },
31952
31953     /**
31954      * Sets the checked state of the checkbox.
31955      * On is always based on a string comparison between inputValue and the param.
31956      * @param {Boolean/String} value - the value to set 
31957      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31958      */
31959     setValue : function(v,suppressEvent){
31960         if (!this.el.dom) {
31961             return;
31962         }
31963         var old = this.el.dom.value ;
31964         this.el.dom.value = v;
31965         if (suppressEvent) {
31966             return ;
31967         }
31968          
31969         // update display..
31970         this.viewEl.select('img',true).each(function(e,i,n)  {
31971             
31972             var on = e.is(".x-menu-item-checked");
31973             var newv = v.indexOf(String(n)) > -1;
31974             if (on != newv) {
31975                 e.toggleClass('x-menu-item-checked');
31976             }
31977             
31978         });
31979         
31980         
31981         this.fireEvent('change', this, v, old);
31982         
31983         
31984     },
31985    
31986     // handle setting of hidden value by some other method!!?!?
31987     setFromHidden: function()
31988     {
31989         if(!this.el){
31990             return;
31991         }
31992         //console.log("SET FROM HIDDEN");
31993         //alert('setFrom hidden');
31994         this.setValue(this.el.dom.value);
31995     },
31996     
31997     onDestroy : function()
31998     {
31999         if(this.viewEl){
32000             Roo.get(this.viewEl).remove();
32001         }
32002          
32003         Roo.form.DayPicker.superclass.onDestroy.call(this);
32004     }
32005
32006 });/*
32007  * RooJS Library 1.1.1
32008  * Copyright(c) 2008-2011  Alan Knowles
32009  *
32010  * License - LGPL
32011  */
32012  
32013
32014 /**
32015  * @class Roo.form.ComboCheck
32016  * @extends Roo.form.ComboBox
32017  * A combobox for multiple select items.
32018  *
32019  * FIXME - could do with a reset button..
32020  * 
32021  * @constructor
32022  * Create a new ComboCheck
32023  * @param {Object} config Configuration options
32024  */
32025 Roo.form.ComboCheck = function(config){
32026     Roo.form.ComboCheck.superclass.constructor.call(this, config);
32027     // should verify some data...
32028     // like
32029     // hiddenName = required..
32030     // displayField = required
32031     // valudField == required
32032     var req= [ 'hiddenName', 'displayField', 'valueField' ];
32033     var _t = this;
32034     Roo.each(req, function(e) {
32035         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
32036             throw "Roo.form.ComboCheck : missing value for: " + e;
32037         }
32038     });
32039     
32040     
32041 };
32042
32043 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
32044      
32045      
32046     editable : false,
32047      
32048     selectedClass: 'x-menu-item-checked', 
32049     
32050     // private
32051     onRender : function(ct, position){
32052         var _t = this;
32053         
32054         
32055         
32056         if(!this.tpl){
32057             var cls = 'x-combo-list';
32058
32059             
32060             this.tpl =  new Roo.Template({
32061                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
32062                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
32063                    '<span>{' + this.displayField + '}</span>' +
32064                     '</div>' 
32065                 
32066             });
32067         }
32068  
32069         
32070         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
32071         this.view.singleSelect = false;
32072         this.view.multiSelect = true;
32073         this.view.toggleSelect = true;
32074         this.pageTb.add(new Roo.Toolbar.Fill(), {
32075             
32076             text: 'Done',
32077             handler: function()
32078             {
32079                 _t.collapse();
32080             }
32081         });
32082     },
32083     
32084     onViewOver : function(e, t){
32085         // do nothing...
32086         return;
32087         
32088     },
32089     
32090     onViewClick : function(doFocus,index){
32091         return;
32092         
32093     },
32094     select: function () {
32095         //Roo.log("SELECT CALLED");
32096     },
32097      
32098     selectByValue : function(xv, scrollIntoView){
32099         var ar = this.getValueArray();
32100         var sels = [];
32101         
32102         Roo.each(ar, function(v) {
32103             if(v === undefined || v === null){
32104                 return;
32105             }
32106             var r = this.findRecord(this.valueField, v);
32107             if(r){
32108                 sels.push(this.store.indexOf(r))
32109                 
32110             }
32111         },this);
32112         this.view.select(sels);
32113         return false;
32114     },
32115     
32116     
32117     
32118     onSelect : function(record, index){
32119        // Roo.log("onselect Called");
32120        // this is only called by the clear button now..
32121         this.view.clearSelections();
32122         this.setValue('[]');
32123         if (this.value != this.valueBefore) {
32124             this.fireEvent('change', this, this.value, this.valueBefore);
32125             this.valueBefore = this.value;
32126         }
32127     },
32128     getValueArray : function()
32129     {
32130         var ar = [] ;
32131         
32132         try {
32133             //Roo.log(this.value);
32134             if (typeof(this.value) == 'undefined') {
32135                 return [];
32136             }
32137             var ar = Roo.decode(this.value);
32138             return  ar instanceof Array ? ar : []; //?? valid?
32139             
32140         } catch(e) {
32141             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
32142             return [];
32143         }
32144          
32145     },
32146     expand : function ()
32147     {
32148         
32149         Roo.form.ComboCheck.superclass.expand.call(this);
32150         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
32151         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
32152         
32153
32154     },
32155     
32156     collapse : function(){
32157         Roo.form.ComboCheck.superclass.collapse.call(this);
32158         var sl = this.view.getSelectedIndexes();
32159         var st = this.store;
32160         var nv = [];
32161         var tv = [];
32162         var r;
32163         Roo.each(sl, function(i) {
32164             r = st.getAt(i);
32165             nv.push(r.get(this.valueField));
32166         },this);
32167         this.setValue(Roo.encode(nv));
32168         if (this.value != this.valueBefore) {
32169
32170             this.fireEvent('change', this, this.value, this.valueBefore);
32171             this.valueBefore = this.value;
32172         }
32173         
32174     },
32175     
32176     setValue : function(v){
32177         // Roo.log(v);
32178         this.value = v;
32179         
32180         var vals = this.getValueArray();
32181         var tv = [];
32182         Roo.each(vals, function(k) {
32183             var r = this.findRecord(this.valueField, k);
32184             if(r){
32185                 tv.push(r.data[this.displayField]);
32186             }else if(this.valueNotFoundText !== undefined){
32187                 tv.push( this.valueNotFoundText );
32188             }
32189         },this);
32190        // Roo.log(tv);
32191         
32192         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
32193         this.hiddenField.value = v;
32194         this.value = v;
32195     }
32196     
32197 });/*
32198  * Based on:
32199  * Ext JS Library 1.1.1
32200  * Copyright(c) 2006-2007, Ext JS, LLC.
32201  *
32202  * Originally Released Under LGPL - original licence link has changed is not relivant.
32203  *
32204  * Fork - LGPL
32205  * <script type="text/javascript">
32206  */
32207  
32208 /**
32209  * @class Roo.form.Signature
32210  * @extends Roo.form.Field
32211  * Signature field.  
32212  * @constructor
32213  * 
32214  * @param {Object} config Configuration options
32215  */
32216
32217 Roo.form.Signature = function(config){
32218     Roo.form.Signature.superclass.constructor.call(this, config);
32219     
32220     this.addEvents({// not in used??
32221          /**
32222          * @event confirm
32223          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32224              * @param {Roo.form.Signature} combo This combo box
32225              */
32226         'confirm' : true,
32227         /**
32228          * @event reset
32229          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32230              * @param {Roo.form.ComboBox} combo This combo box
32231              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32232              */
32233         'reset' : true
32234     });
32235 };
32236
32237 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
32238     /**
32239      * @cfg {Object} labels Label to use when rendering a form.
32240      * defaults to 
32241      * labels : { 
32242      *      clear : "Clear",
32243      *      confirm : "Confirm"
32244      *  }
32245      */
32246     labels : { 
32247         clear : "Clear",
32248         confirm : "Confirm"
32249     },
32250     /**
32251      * @cfg {Number} width The signature panel width (defaults to 300)
32252      */
32253     width: 300,
32254     /**
32255      * @cfg {Number} height The signature panel height (defaults to 100)
32256      */
32257     height : 100,
32258     /**
32259      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32260      */
32261     allowBlank : false,
32262     
32263     //private
32264     // {Object} signPanel The signature SVG panel element (defaults to {})
32265     signPanel : {},
32266     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32267     isMouseDown : false,
32268     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32269     isConfirmed : false,
32270     // {String} signatureTmp SVG mapping string (defaults to empty string)
32271     signatureTmp : '',
32272     
32273     
32274     defaultAutoCreate : { // modified by initCompnoent..
32275         tag: "input",
32276         type:"hidden"
32277     },
32278
32279     // private
32280     onRender : function(ct, position){
32281         
32282         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32283         
32284         this.wrap = this.el.wrap({
32285             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32286         });
32287         
32288         this.createToolbar(this);
32289         this.signPanel = this.wrap.createChild({
32290                 tag: 'div',
32291                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32292             }, this.el
32293         );
32294             
32295         this.svgID = Roo.id();
32296         this.svgEl = this.signPanel.createChild({
32297               xmlns : 'http://www.w3.org/2000/svg',
32298               tag : 'svg',
32299               id : this.svgID + "-svg",
32300               width: this.width,
32301               height: this.height,
32302               viewBox: '0 0 '+this.width+' '+this.height,
32303               cn : [
32304                 {
32305                     tag: "rect",
32306                     id: this.svgID + "-svg-r",
32307                     width: this.width,
32308                     height: this.height,
32309                     fill: "#ffa"
32310                 },
32311                 {
32312                     tag: "line",
32313                     id: this.svgID + "-svg-l",
32314                     x1: "0", // start
32315                     y1: (this.height*0.8), // start set the line in 80% of height
32316                     x2: this.width, // end
32317                     y2: (this.height*0.8), // end set the line in 80% of height
32318                     'stroke': "#666",
32319                     'stroke-width': "1",
32320                     'stroke-dasharray': "3",
32321                     'shape-rendering': "crispEdges",
32322                     'pointer-events': "none"
32323                 },
32324                 {
32325                     tag: "path",
32326                     id: this.svgID + "-svg-p",
32327                     'stroke': "navy",
32328                     'stroke-width': "3",
32329                     'fill': "none",
32330                     'pointer-events': 'none'
32331                 }
32332               ]
32333         });
32334         this.createSVG();
32335         this.svgBox = this.svgEl.dom.getScreenCTM();
32336     },
32337     createSVG : function(){ 
32338         var svg = this.signPanel;
32339         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32340         var t = this;
32341
32342         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32343         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32344         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32345         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32346         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32347         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32348         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32349         
32350     },
32351     isTouchEvent : function(e){
32352         return e.type.match(/^touch/);
32353     },
32354     getCoords : function (e) {
32355         var pt    = this.svgEl.dom.createSVGPoint();
32356         pt.x = e.clientX; 
32357         pt.y = e.clientY;
32358         if (this.isTouchEvent(e)) {
32359             pt.x =  e.targetTouches[0].clientX;
32360             pt.y = e.targetTouches[0].clientY;
32361         }
32362         var a = this.svgEl.dom.getScreenCTM();
32363         var b = a.inverse();
32364         var mx = pt.matrixTransform(b);
32365         return mx.x + ',' + mx.y;
32366     },
32367     //mouse event headler 
32368     down : function (e) {
32369         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32370         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32371         
32372         this.isMouseDown = true;
32373         
32374         e.preventDefault();
32375     },
32376     move : function (e) {
32377         if (this.isMouseDown) {
32378             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32379             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32380         }
32381         
32382         e.preventDefault();
32383     },
32384     up : function (e) {
32385         this.isMouseDown = false;
32386         var sp = this.signatureTmp.split(' ');
32387         
32388         if(sp.length > 1){
32389             if(!sp[sp.length-2].match(/^L/)){
32390                 sp.pop();
32391                 sp.pop();
32392                 sp.push("");
32393                 this.signatureTmp = sp.join(" ");
32394             }
32395         }
32396         if(this.getValue() != this.signatureTmp){
32397             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32398             this.isConfirmed = false;
32399         }
32400         e.preventDefault();
32401     },
32402     
32403     /**
32404      * Protected method that will not generally be called directly. It
32405      * is called when the editor creates its toolbar. Override this method if you need to
32406      * add custom toolbar buttons.
32407      * @param {HtmlEditor} editor
32408      */
32409     createToolbar : function(editor){
32410          function btn(id, toggle, handler){
32411             var xid = fid + '-'+ id ;
32412             return {
32413                 id : xid,
32414                 cmd : id,
32415                 cls : 'x-btn-icon x-edit-'+id,
32416                 enableToggle:toggle !== false,
32417                 scope: editor, // was editor...
32418                 handler:handler||editor.relayBtnCmd,
32419                 clickEvent:'mousedown',
32420                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32421                 tabIndex:-1
32422             };
32423         }
32424         
32425         
32426         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32427         this.tb = tb;
32428         this.tb.add(
32429            {
32430                 cls : ' x-signature-btn x-signature-'+id,
32431                 scope: editor, // was editor...
32432                 handler: this.reset,
32433                 clickEvent:'mousedown',
32434                 text: this.labels.clear
32435             },
32436             {
32437                  xtype : 'Fill',
32438                  xns: Roo.Toolbar
32439             }, 
32440             {
32441                 cls : '  x-signature-btn x-signature-'+id,
32442                 scope: editor, // was editor...
32443                 handler: this.confirmHandler,
32444                 clickEvent:'mousedown',
32445                 text: this.labels.confirm
32446             }
32447         );
32448     
32449     },
32450     //public
32451     /**
32452      * when user is clicked confirm then show this image.....
32453      * 
32454      * @return {String} Image Data URI
32455      */
32456     getImageDataURI : function(){
32457         var svg = this.svgEl.dom.parentNode.innerHTML;
32458         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32459         return src; 
32460     },
32461     /**
32462      * 
32463      * @return {Boolean} this.isConfirmed
32464      */
32465     getConfirmed : function(){
32466         return this.isConfirmed;
32467     },
32468     /**
32469      * 
32470      * @return {Number} this.width
32471      */
32472     getWidth : function(){
32473         return this.width;
32474     },
32475     /**
32476      * 
32477      * @return {Number} this.height
32478      */
32479     getHeight : function(){
32480         return this.height;
32481     },
32482     // private
32483     getSignature : function(){
32484         return this.signatureTmp;
32485     },
32486     // private
32487     reset : function(){
32488         this.signatureTmp = '';
32489         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32490         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32491         this.isConfirmed = false;
32492         Roo.form.Signature.superclass.reset.call(this);
32493     },
32494     setSignature : function(s){
32495         this.signatureTmp = s;
32496         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32497         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32498         this.setValue(s);
32499         this.isConfirmed = false;
32500         Roo.form.Signature.superclass.reset.call(this);
32501     }, 
32502     test : function(){
32503 //        Roo.log(this.signPanel.dom.contentWindow.up())
32504     },
32505     //private
32506     setConfirmed : function(){
32507         
32508         
32509         
32510 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32511     },
32512     // private
32513     confirmHandler : function(){
32514         if(!this.getSignature()){
32515             return;
32516         }
32517         
32518         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32519         this.setValue(this.getSignature());
32520         this.isConfirmed = true;
32521         
32522         this.fireEvent('confirm', this);
32523     },
32524     // private
32525     // Subclasses should provide the validation implementation by overriding this
32526     validateValue : function(value){
32527         if(this.allowBlank){
32528             return true;
32529         }
32530         
32531         if(this.isConfirmed){
32532             return true;
32533         }
32534         return false;
32535     }
32536 });/*
32537  * Based on:
32538  * Ext JS Library 1.1.1
32539  * Copyright(c) 2006-2007, Ext JS, LLC.
32540  *
32541  * Originally Released Under LGPL - original licence link has changed is not relivant.
32542  *
32543  * Fork - LGPL
32544  * <script type="text/javascript">
32545  */
32546  
32547
32548 /**
32549  * @class Roo.form.ComboBox
32550  * @extends Roo.form.TriggerField
32551  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32552  * @constructor
32553  * Create a new ComboBox.
32554  * @param {Object} config Configuration options
32555  */
32556 Roo.form.Select = function(config){
32557     Roo.form.Select.superclass.constructor.call(this, config);
32558      
32559 };
32560
32561 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32562     /**
32563      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32564      */
32565     /**
32566      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32567      * rendering into an Roo.Editor, defaults to false)
32568      */
32569     /**
32570      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32571      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32572      */
32573     /**
32574      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32575      */
32576     /**
32577      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32578      * the dropdown list (defaults to undefined, with no header element)
32579      */
32580
32581      /**
32582      * @cfg {String/Roo.Template} tpl The template to use to render the output
32583      */
32584      
32585     // private
32586     defaultAutoCreate : {tag: "select"  },
32587     /**
32588      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32589      */
32590     listWidth: undefined,
32591     /**
32592      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32593      * mode = 'remote' or 'text' if mode = 'local')
32594      */
32595     displayField: undefined,
32596     /**
32597      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32598      * mode = 'remote' or 'value' if mode = 'local'). 
32599      * Note: use of a valueField requires the user make a selection
32600      * in order for a value to be mapped.
32601      */
32602     valueField: undefined,
32603     
32604     
32605     /**
32606      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32607      * field's data value (defaults to the underlying DOM element's name)
32608      */
32609     hiddenName: undefined,
32610     /**
32611      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32612      */
32613     listClass: '',
32614     /**
32615      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32616      */
32617     selectedClass: 'x-combo-selected',
32618     /**
32619      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32620      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32621      * which displays a downward arrow icon).
32622      */
32623     triggerClass : 'x-form-arrow-trigger',
32624     /**
32625      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32626      */
32627     shadow:'sides',
32628     /**
32629      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32630      * anchor positions (defaults to 'tl-bl')
32631      */
32632     listAlign: 'tl-bl?',
32633     /**
32634      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32635      */
32636     maxHeight: 300,
32637     /**
32638      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32639      * query specified by the allQuery config option (defaults to 'query')
32640      */
32641     triggerAction: 'query',
32642     /**
32643      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32644      * (defaults to 4, does not apply if editable = false)
32645      */
32646     minChars : 4,
32647     /**
32648      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32649      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32650      */
32651     typeAhead: false,
32652     /**
32653      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32654      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32655      */
32656     queryDelay: 500,
32657     /**
32658      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32659      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32660      */
32661     pageSize: 0,
32662     /**
32663      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32664      * when editable = true (defaults to false)
32665      */
32666     selectOnFocus:false,
32667     /**
32668      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32669      */
32670     queryParam: 'query',
32671     /**
32672      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32673      * when mode = 'remote' (defaults to 'Loading...')
32674      */
32675     loadingText: 'Loading...',
32676     /**
32677      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32678      */
32679     resizable: false,
32680     /**
32681      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32682      */
32683     handleHeight : 8,
32684     /**
32685      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32686      * traditional select (defaults to true)
32687      */
32688     editable: true,
32689     /**
32690      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32691      */
32692     allQuery: '',
32693     /**
32694      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32695      */
32696     mode: 'remote',
32697     /**
32698      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32699      * listWidth has a higher value)
32700      */
32701     minListWidth : 70,
32702     /**
32703      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32704      * allow the user to set arbitrary text into the field (defaults to false)
32705      */
32706     forceSelection:false,
32707     /**
32708      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32709      * if typeAhead = true (defaults to 250)
32710      */
32711     typeAheadDelay : 250,
32712     /**
32713      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32714      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32715      */
32716     valueNotFoundText : undefined,
32717     
32718     /**
32719      * @cfg {String} defaultValue The value displayed after loading the store.
32720      */
32721     defaultValue: '',
32722     
32723     /**
32724      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32725      */
32726     blockFocus : false,
32727     
32728     /**
32729      * @cfg {Boolean} disableClear Disable showing of clear button.
32730      */
32731     disableClear : false,
32732     /**
32733      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32734      */
32735     alwaysQuery : false,
32736     
32737     //private
32738     addicon : false,
32739     editicon: false,
32740     
32741     // element that contains real text value.. (when hidden is used..)
32742      
32743     // private
32744     onRender : function(ct, position){
32745         Roo.form.Field.prototype.onRender.call(this, ct, position);
32746         
32747         if(this.store){
32748             this.store.on('beforeload', this.onBeforeLoad, this);
32749             this.store.on('load', this.onLoad, this);
32750             this.store.on('loadexception', this.onLoadException, this);
32751             this.store.load({});
32752         }
32753         
32754         
32755         
32756     },
32757
32758     // private
32759     initEvents : function(){
32760         //Roo.form.ComboBox.superclass.initEvents.call(this);
32761  
32762     },
32763
32764     onDestroy : function(){
32765        
32766         if(this.store){
32767             this.store.un('beforeload', this.onBeforeLoad, this);
32768             this.store.un('load', this.onLoad, this);
32769             this.store.un('loadexception', this.onLoadException, this);
32770         }
32771         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32772     },
32773
32774     // private
32775     fireKey : function(e){
32776         if(e.isNavKeyPress() && !this.list.isVisible()){
32777             this.fireEvent("specialkey", this, e);
32778         }
32779     },
32780
32781     // private
32782     onResize: function(w, h){
32783         
32784         return; 
32785     
32786         
32787     },
32788
32789     /**
32790      * Allow or prevent the user from directly editing the field text.  If false is passed,
32791      * the user will only be able to select from the items defined in the dropdown list.  This method
32792      * is the runtime equivalent of setting the 'editable' config option at config time.
32793      * @param {Boolean} value True to allow the user to directly edit the field text
32794      */
32795     setEditable : function(value){
32796          
32797     },
32798
32799     // private
32800     onBeforeLoad : function(){
32801         
32802         Roo.log("Select before load");
32803         return;
32804     
32805         this.innerList.update(this.loadingText ?
32806                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32807         //this.restrictHeight();
32808         this.selectedIndex = -1;
32809     },
32810
32811     // private
32812     onLoad : function(){
32813
32814     
32815         var dom = this.el.dom;
32816         dom.innerHTML = '';
32817          var od = dom.ownerDocument;
32818          
32819         if (this.emptyText) {
32820             var op = od.createElement('option');
32821             op.setAttribute('value', '');
32822             op.innerHTML = String.format('{0}', this.emptyText);
32823             dom.appendChild(op);
32824         }
32825         if(this.store.getCount() > 0){
32826            
32827             var vf = this.valueField;
32828             var df = this.displayField;
32829             this.store.data.each(function(r) {
32830                 // which colmsn to use... testing - cdoe / title..
32831                 var op = od.createElement('option');
32832                 op.setAttribute('value', r.data[vf]);
32833                 op.innerHTML = String.format('{0}', r.data[df]);
32834                 dom.appendChild(op);
32835             });
32836             if (typeof(this.defaultValue != 'undefined')) {
32837                 this.setValue(this.defaultValue);
32838             }
32839             
32840              
32841         }else{
32842             //this.onEmptyResults();
32843         }
32844         //this.el.focus();
32845     },
32846     // private
32847     onLoadException : function()
32848     {
32849         dom.innerHTML = '';
32850             
32851         Roo.log("Select on load exception");
32852         return;
32853     
32854         this.collapse();
32855         Roo.log(this.store.reader.jsonData);
32856         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32857             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32858         }
32859         
32860         
32861     },
32862     // private
32863     onTypeAhead : function(){
32864          
32865     },
32866
32867     // private
32868     onSelect : function(record, index){
32869         Roo.log('on select?');
32870         return;
32871         if(this.fireEvent('beforeselect', this, record, index) !== false){
32872             this.setFromData(index > -1 ? record.data : false);
32873             this.collapse();
32874             this.fireEvent('select', this, record, index);
32875         }
32876     },
32877
32878     /**
32879      * Returns the currently selected field value or empty string if no value is set.
32880      * @return {String} value The selected value
32881      */
32882     getValue : function(){
32883         var dom = this.el.dom;
32884         this.value = dom.options[dom.selectedIndex].value;
32885         return this.value;
32886         
32887     },
32888
32889     /**
32890      * Clears any text/value currently set in the field
32891      */
32892     clearValue : function(){
32893         this.value = '';
32894         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32895         
32896     },
32897
32898     /**
32899      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32900      * will be displayed in the field.  If the value does not match the data value of an existing item,
32901      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32902      * Otherwise the field will be blank (although the value will still be set).
32903      * @param {String} value The value to match
32904      */
32905     setValue : function(v){
32906         var d = this.el.dom;
32907         for (var i =0; i < d.options.length;i++) {
32908             if (v == d.options[i].value) {
32909                 d.selectedIndex = i;
32910                 this.value = v;
32911                 return;
32912             }
32913         }
32914         this.clearValue();
32915     },
32916     /**
32917      * @property {Object} the last set data for the element
32918      */
32919     
32920     lastData : false,
32921     /**
32922      * Sets the value of the field based on a object which is related to the record format for the store.
32923      * @param {Object} value the value to set as. or false on reset?
32924      */
32925     setFromData : function(o){
32926         Roo.log('setfrom data?');
32927          
32928         
32929         
32930     },
32931     // private
32932     reset : function(){
32933         this.clearValue();
32934     },
32935     // private
32936     findRecord : function(prop, value){
32937         
32938         return false;
32939     
32940         var record;
32941         if(this.store.getCount() > 0){
32942             this.store.each(function(r){
32943                 if(r.data[prop] == value){
32944                     record = r;
32945                     return false;
32946                 }
32947                 return true;
32948             });
32949         }
32950         return record;
32951     },
32952     
32953     getName: function()
32954     {
32955         // returns hidden if it's set..
32956         if (!this.rendered) {return ''};
32957         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32958         
32959     },
32960      
32961
32962     
32963
32964     // private
32965     onEmptyResults : function(){
32966         Roo.log('empty results');
32967         //this.collapse();
32968     },
32969
32970     /**
32971      * Returns true if the dropdown list is expanded, else false.
32972      */
32973     isExpanded : function(){
32974         return false;
32975     },
32976
32977     /**
32978      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32979      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32980      * @param {String} value The data value of the item to select
32981      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32982      * selected item if it is not currently in view (defaults to true)
32983      * @return {Boolean} True if the value matched an item in the list, else false
32984      */
32985     selectByValue : function(v, scrollIntoView){
32986         Roo.log('select By Value');
32987         return false;
32988     
32989         if(v !== undefined && v !== null){
32990             var r = this.findRecord(this.valueField || this.displayField, v);
32991             if(r){
32992                 this.select(this.store.indexOf(r), scrollIntoView);
32993                 return true;
32994             }
32995         }
32996         return false;
32997     },
32998
32999     /**
33000      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
33001      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
33002      * @param {Number} index The zero-based index of the list item to select
33003      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
33004      * selected item if it is not currently in view (defaults to true)
33005      */
33006     select : function(index, scrollIntoView){
33007         Roo.log('select ');
33008         return  ;
33009         
33010         this.selectedIndex = index;
33011         this.view.select(index);
33012         if(scrollIntoView !== false){
33013             var el = this.view.getNode(index);
33014             if(el){
33015                 this.innerList.scrollChildIntoView(el, false);
33016             }
33017         }
33018     },
33019
33020       
33021
33022     // private
33023     validateBlur : function(){
33024         
33025         return;
33026         
33027     },
33028
33029     // private
33030     initQuery : function(){
33031         this.doQuery(this.getRawValue());
33032     },
33033
33034     // private
33035     doForce : function(){
33036         if(this.el.dom.value.length > 0){
33037             this.el.dom.value =
33038                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
33039              
33040         }
33041     },
33042
33043     /**
33044      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
33045      * query allowing the query action to be canceled if needed.
33046      * @param {String} query The SQL query to execute
33047      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
33048      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
33049      * saved in the current store (defaults to false)
33050      */
33051     doQuery : function(q, forceAll){
33052         
33053         Roo.log('doQuery?');
33054         if(q === undefined || q === null){
33055             q = '';
33056         }
33057         var qe = {
33058             query: q,
33059             forceAll: forceAll,
33060             combo: this,
33061             cancel:false
33062         };
33063         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
33064             return false;
33065         }
33066         q = qe.query;
33067         forceAll = qe.forceAll;
33068         if(forceAll === true || (q.length >= this.minChars)){
33069             if(this.lastQuery != q || this.alwaysQuery){
33070                 this.lastQuery = q;
33071                 if(this.mode == 'local'){
33072                     this.selectedIndex = -1;
33073                     if(forceAll){
33074                         this.store.clearFilter();
33075                     }else{
33076                         this.store.filter(this.displayField, q);
33077                     }
33078                     this.onLoad();
33079                 }else{
33080                     this.store.baseParams[this.queryParam] = q;
33081                     this.store.load({
33082                         params: this.getParams(q)
33083                     });
33084                     this.expand();
33085                 }
33086             }else{
33087                 this.selectedIndex = -1;
33088                 this.onLoad();   
33089             }
33090         }
33091     },
33092
33093     // private
33094     getParams : function(q){
33095         var p = {};
33096         //p[this.queryParam] = q;
33097         if(this.pageSize){
33098             p.start = 0;
33099             p.limit = this.pageSize;
33100         }
33101         return p;
33102     },
33103
33104     /**
33105      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
33106      */
33107     collapse : function(){
33108         
33109     },
33110
33111     // private
33112     collapseIf : function(e){
33113         
33114     },
33115
33116     /**
33117      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
33118      */
33119     expand : function(){
33120         
33121     } ,
33122
33123     // private
33124      
33125
33126     /** 
33127     * @cfg {Boolean} grow 
33128     * @hide 
33129     */
33130     /** 
33131     * @cfg {Number} growMin 
33132     * @hide 
33133     */
33134     /** 
33135     * @cfg {Number} growMax 
33136     * @hide 
33137     */
33138     /**
33139      * @hide
33140      * @method autoSize
33141      */
33142     
33143     setWidth : function()
33144     {
33145         
33146     },
33147     getResizeEl : function(){
33148         return this.el;
33149     }
33150 });//<script type="text/javasscript">
33151  
33152
33153 /**
33154  * @class Roo.DDView
33155  * A DnD enabled version of Roo.View.
33156  * @param {Element/String} container The Element in which to create the View.
33157  * @param {String} tpl The template string used to create the markup for each element of the View
33158  * @param {Object} config The configuration properties. These include all the config options of
33159  * {@link Roo.View} plus some specific to this class.<br>
33160  * <p>
33161  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
33162  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
33163  * <p>
33164  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
33165 .x-view-drag-insert-above {
33166         border-top:1px dotted #3366cc;
33167 }
33168 .x-view-drag-insert-below {
33169         border-bottom:1px dotted #3366cc;
33170 }
33171 </code></pre>
33172  * 
33173  */
33174  
33175 Roo.DDView = function(container, tpl, config) {
33176     Roo.DDView.superclass.constructor.apply(this, arguments);
33177     this.getEl().setStyle("outline", "0px none");
33178     this.getEl().unselectable();
33179     if (this.dragGroup) {
33180         this.setDraggable(this.dragGroup.split(","));
33181     }
33182     if (this.dropGroup) {
33183         this.setDroppable(this.dropGroup.split(","));
33184     }
33185     if (this.deletable) {
33186         this.setDeletable();
33187     }
33188     this.isDirtyFlag = false;
33189         this.addEvents({
33190                 "drop" : true
33191         });
33192 };
33193
33194 Roo.extend(Roo.DDView, Roo.View, {
33195 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
33196 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
33197 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
33198 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
33199
33200         isFormField: true,
33201
33202         reset: Roo.emptyFn,
33203         
33204         clearInvalid: Roo.form.Field.prototype.clearInvalid,
33205
33206         validate: function() {
33207                 return true;
33208         },
33209         
33210         destroy: function() {
33211                 this.purgeListeners();
33212                 this.getEl.removeAllListeners();
33213                 this.getEl().remove();
33214                 if (this.dragZone) {
33215                         if (this.dragZone.destroy) {
33216                                 this.dragZone.destroy();
33217                         }
33218                 }
33219                 if (this.dropZone) {
33220                         if (this.dropZone.destroy) {
33221                                 this.dropZone.destroy();
33222                         }
33223                 }
33224         },
33225
33226 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33227         getName: function() {
33228                 return this.name;
33229         },
33230
33231 /**     Loads the View from a JSON string representing the Records to put into the Store. */
33232         setValue: function(v) {
33233                 if (!this.store) {
33234                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
33235                 }
33236                 var data = {};
33237                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33238                 this.store.proxy = new Roo.data.MemoryProxy(data);
33239                 this.store.load();
33240         },
33241
33242 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33243         getValue: function() {
33244                 var result = '(';
33245                 this.store.each(function(rec) {
33246                         result += rec.id + ',';
33247                 });
33248                 return result.substr(0, result.length - 1) + ')';
33249         },
33250         
33251         getIds: function() {
33252                 var i = 0, result = new Array(this.store.getCount());
33253                 this.store.each(function(rec) {
33254                         result[i++] = rec.id;
33255                 });
33256                 return result;
33257         },
33258         
33259         isDirty: function() {
33260                 return this.isDirtyFlag;
33261         },
33262
33263 /**
33264  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33265  *      whole Element becomes the target, and this causes the drop gesture to append.
33266  */
33267     getTargetFromEvent : function(e) {
33268                 var target = e.getTarget();
33269                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33270                 target = target.parentNode;
33271                 }
33272                 if (!target) {
33273                         target = this.el.dom.lastChild || this.el.dom;
33274                 }
33275                 return target;
33276     },
33277
33278 /**
33279  *      Create the drag data which consists of an object which has the property "ddel" as
33280  *      the drag proxy element. 
33281  */
33282     getDragData : function(e) {
33283         var target = this.findItemFromChild(e.getTarget());
33284                 if(target) {
33285                         this.handleSelection(e);
33286                         var selNodes = this.getSelectedNodes();
33287             var dragData = {
33288                 source: this,
33289                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33290                 nodes: selNodes,
33291                 records: []
33292                         };
33293                         var selectedIndices = this.getSelectedIndexes();
33294                         for (var i = 0; i < selectedIndices.length; i++) {
33295                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33296                         }
33297                         if (selNodes.length == 1) {
33298                                 dragData.ddel = target.cloneNode(true); // the div element
33299                         } else {
33300                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33301                                 div.className = 'multi-proxy';
33302                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33303                                         div.appendChild(selNodes[i].cloneNode(true));
33304                                 }
33305                                 dragData.ddel = div;
33306                         }
33307             //console.log(dragData)
33308             //console.log(dragData.ddel.innerHTML)
33309                         return dragData;
33310                 }
33311         //console.log('nodragData')
33312                 return false;
33313     },
33314     
33315 /**     Specify to which ddGroup items in this DDView may be dragged. */
33316     setDraggable: function(ddGroup) {
33317         if (ddGroup instanceof Array) {
33318                 Roo.each(ddGroup, this.setDraggable, this);
33319                 return;
33320         }
33321         if (this.dragZone) {
33322                 this.dragZone.addToGroup(ddGroup);
33323         } else {
33324                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33325                                 containerScroll: true,
33326                                 ddGroup: ddGroup 
33327
33328                         });
33329 //                      Draggability implies selection. DragZone's mousedown selects the element.
33330                         if (!this.multiSelect) { this.singleSelect = true; }
33331
33332 //                      Wire the DragZone's handlers up to methods in *this*
33333                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33334                 }
33335     },
33336
33337 /**     Specify from which ddGroup this DDView accepts drops. */
33338     setDroppable: function(ddGroup) {
33339         if (ddGroup instanceof Array) {
33340                 Roo.each(ddGroup, this.setDroppable, this);
33341                 return;
33342         }
33343         if (this.dropZone) {
33344                 this.dropZone.addToGroup(ddGroup);
33345         } else {
33346                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33347                                 containerScroll: true,
33348                                 ddGroup: ddGroup
33349                         });
33350
33351 //                      Wire the DropZone's handlers up to methods in *this*
33352                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33353                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33354                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33355                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33356                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33357                 }
33358     },
33359
33360 /**     Decide whether to drop above or below a View node. */
33361     getDropPoint : function(e, n, dd){
33362         if (n == this.el.dom) { return "above"; }
33363                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33364                 var c = t + (b - t) / 2;
33365                 var y = Roo.lib.Event.getPageY(e);
33366                 if(y <= c) {
33367                         return "above";
33368                 }else{
33369                         return "below";
33370                 }
33371     },
33372
33373     onNodeEnter : function(n, dd, e, data){
33374                 return false;
33375     },
33376     
33377     onNodeOver : function(n, dd, e, data){
33378                 var pt = this.getDropPoint(e, n, dd);
33379                 // set the insert point style on the target node
33380                 var dragElClass = this.dropNotAllowed;
33381                 if (pt) {
33382                         var targetElClass;
33383                         if (pt == "above"){
33384                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33385                                 targetElClass = "x-view-drag-insert-above";
33386                         } else {
33387                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33388                                 targetElClass = "x-view-drag-insert-below";
33389                         }
33390                         if (this.lastInsertClass != targetElClass){
33391                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33392                                 this.lastInsertClass = targetElClass;
33393                         }
33394                 }
33395                 return dragElClass;
33396         },
33397
33398     onNodeOut : function(n, dd, e, data){
33399                 this.removeDropIndicators(n);
33400     },
33401
33402     onNodeDrop : function(n, dd, e, data){
33403         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33404                 return false;
33405         }
33406         var pt = this.getDropPoint(e, n, dd);
33407                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33408                 if (pt == "below") { insertAt++; }
33409                 for (var i = 0; i < data.records.length; i++) {
33410                         var r = data.records[i];
33411                         var dup = this.store.getById(r.id);
33412                         if (dup && (dd != this.dragZone)) {
33413                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33414                         } else {
33415                                 if (data.copy) {
33416                                         this.store.insert(insertAt++, r.copy());
33417                                 } else {
33418                                         data.source.isDirtyFlag = true;
33419                                         r.store.remove(r);
33420                                         this.store.insert(insertAt++, r);
33421                                 }
33422                                 this.isDirtyFlag = true;
33423                         }
33424                 }
33425                 this.dragZone.cachedTarget = null;
33426                 return true;
33427     },
33428
33429     removeDropIndicators : function(n){
33430                 if(n){
33431                         Roo.fly(n).removeClass([
33432                                 "x-view-drag-insert-above",
33433                                 "x-view-drag-insert-below"]);
33434                         this.lastInsertClass = "_noclass";
33435                 }
33436     },
33437
33438 /**
33439  *      Utility method. Add a delete option to the DDView's context menu.
33440  *      @param {String} imageUrl The URL of the "delete" icon image.
33441  */
33442         setDeletable: function(imageUrl) {
33443                 if (!this.singleSelect && !this.multiSelect) {
33444                         this.singleSelect = true;
33445                 }
33446                 var c = this.getContextMenu();
33447                 this.contextMenu.on("itemclick", function(item) {
33448                         switch (item.id) {
33449                                 case "delete":
33450                                         this.remove(this.getSelectedIndexes());
33451                                         break;
33452                         }
33453                 }, this);
33454                 this.contextMenu.add({
33455                         icon: imageUrl,
33456                         id: "delete",
33457                         text: 'Delete'
33458                 });
33459         },
33460         
33461 /**     Return the context menu for this DDView. */
33462         getContextMenu: function() {
33463                 if (!this.contextMenu) {
33464 //                      Create the View's context menu
33465                         this.contextMenu = new Roo.menu.Menu({
33466                                 id: this.id + "-contextmenu"
33467                         });
33468                         this.el.on("contextmenu", this.showContextMenu, this);
33469                 }
33470                 return this.contextMenu;
33471         },
33472         
33473         disableContextMenu: function() {
33474                 if (this.contextMenu) {
33475                         this.el.un("contextmenu", this.showContextMenu, this);
33476                 }
33477         },
33478
33479         showContextMenu: function(e, item) {
33480         item = this.findItemFromChild(e.getTarget());
33481                 if (item) {
33482                         e.stopEvent();
33483                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33484                         this.contextMenu.showAt(e.getXY());
33485             }
33486     },
33487
33488 /**
33489  *      Remove {@link Roo.data.Record}s at the specified indices.
33490  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33491  */
33492     remove: function(selectedIndices) {
33493                 selectedIndices = [].concat(selectedIndices);
33494                 for (var i = 0; i < selectedIndices.length; i++) {
33495                         var rec = this.store.getAt(selectedIndices[i]);
33496                         this.store.remove(rec);
33497                 }
33498     },
33499
33500 /**
33501  *      Double click fires the event, but also, if this is draggable, and there is only one other
33502  *      related DropZone, it transfers the selected node.
33503  */
33504     onDblClick : function(e){
33505         var item = this.findItemFromChild(e.getTarget());
33506         if(item){
33507             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33508                 return false;
33509             }
33510             if (this.dragGroup) {
33511                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33512                     while (targets.indexOf(this.dropZone) > -1) {
33513                             targets.remove(this.dropZone);
33514                                 }
33515                     if (targets.length == 1) {
33516                                         this.dragZone.cachedTarget = null;
33517                         var el = Roo.get(targets[0].getEl());
33518                         var box = el.getBox(true);
33519                         targets[0].onNodeDrop(el.dom, {
33520                                 target: el.dom,
33521                                 xy: [box.x, box.y + box.height - 1]
33522                         }, null, this.getDragData(e));
33523                     }
33524                 }
33525         }
33526     },
33527     
33528     handleSelection: function(e) {
33529                 this.dragZone.cachedTarget = null;
33530         var item = this.findItemFromChild(e.getTarget());
33531         if (!item) {
33532                 this.clearSelections(true);
33533                 return;
33534         }
33535                 if (item && (this.multiSelect || this.singleSelect)){
33536                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33537                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33538                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33539                                 this.unselect(item);
33540                         } else {
33541                                 this.select(item, this.multiSelect && e.ctrlKey);
33542                                 this.lastSelection = item;
33543                         }
33544                 }
33545     },
33546
33547     onItemClick : function(item, index, e){
33548                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33549                         return false;
33550                 }
33551                 return true;
33552     },
33553
33554     unselect : function(nodeInfo, suppressEvent){
33555                 var node = this.getNode(nodeInfo);
33556                 if(node && this.isSelected(node)){
33557                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33558                                 Roo.fly(node).removeClass(this.selectedClass);
33559                                 this.selections.remove(node);
33560                                 if(!suppressEvent){
33561                                         this.fireEvent("selectionchange", this, this.selections);
33562                                 }
33563                         }
33564                 }
33565     }
33566 });
33567 /*
33568  * Based on:
33569  * Ext JS Library 1.1.1
33570  * Copyright(c) 2006-2007, Ext JS, LLC.
33571  *
33572  * Originally Released Under LGPL - original licence link has changed is not relivant.
33573  *
33574  * Fork - LGPL
33575  * <script type="text/javascript">
33576  */
33577  
33578 /**
33579  * @class Roo.LayoutManager
33580  * @extends Roo.util.Observable
33581  * Base class for layout managers.
33582  */
33583 Roo.LayoutManager = function(container, config){
33584     Roo.LayoutManager.superclass.constructor.call(this);
33585     this.el = Roo.get(container);
33586     // ie scrollbar fix
33587     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33588         document.body.scroll = "no";
33589     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33590         this.el.position('relative');
33591     }
33592     this.id = this.el.id;
33593     this.el.addClass("x-layout-container");
33594     /** false to disable window resize monitoring @type Boolean */
33595     this.monitorWindowResize = true;
33596     this.regions = {};
33597     this.addEvents({
33598         /**
33599          * @event layout
33600          * Fires when a layout is performed. 
33601          * @param {Roo.LayoutManager} this
33602          */
33603         "layout" : true,
33604         /**
33605          * @event regionresized
33606          * Fires when the user resizes a region. 
33607          * @param {Roo.LayoutRegion} region The resized region
33608          * @param {Number} newSize The new size (width for east/west, height for north/south)
33609          */
33610         "regionresized" : true,
33611         /**
33612          * @event regioncollapsed
33613          * Fires when a region is collapsed. 
33614          * @param {Roo.LayoutRegion} region The collapsed region
33615          */
33616         "regioncollapsed" : true,
33617         /**
33618          * @event regionexpanded
33619          * Fires when a region is expanded.  
33620          * @param {Roo.LayoutRegion} region The expanded region
33621          */
33622         "regionexpanded" : true
33623     });
33624     this.updating = false;
33625     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33626 };
33627
33628 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33629     /**
33630      * Returns true if this layout is currently being updated
33631      * @return {Boolean}
33632      */
33633     isUpdating : function(){
33634         return this.updating; 
33635     },
33636     
33637     /**
33638      * Suspend the LayoutManager from doing auto-layouts while
33639      * making multiple add or remove calls
33640      */
33641     beginUpdate : function(){
33642         this.updating = true;    
33643     },
33644     
33645     /**
33646      * Restore auto-layouts and optionally disable the manager from performing a layout
33647      * @param {Boolean} noLayout true to disable a layout update 
33648      */
33649     endUpdate : function(noLayout){
33650         this.updating = false;
33651         if(!noLayout){
33652             this.layout();
33653         }    
33654     },
33655     
33656     layout: function(){
33657         
33658     },
33659     
33660     onRegionResized : function(region, newSize){
33661         this.fireEvent("regionresized", region, newSize);
33662         this.layout();
33663     },
33664     
33665     onRegionCollapsed : function(region){
33666         this.fireEvent("regioncollapsed", region);
33667     },
33668     
33669     onRegionExpanded : function(region){
33670         this.fireEvent("regionexpanded", region);
33671     },
33672         
33673     /**
33674      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33675      * performs box-model adjustments.
33676      * @return {Object} The size as an object {width: (the width), height: (the height)}
33677      */
33678     getViewSize : function(){
33679         var size;
33680         if(this.el.dom != document.body){
33681             size = this.el.getSize();
33682         }else{
33683             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33684         }
33685         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33686         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33687         return size;
33688     },
33689     
33690     /**
33691      * Returns the Element this layout is bound to.
33692      * @return {Roo.Element}
33693      */
33694     getEl : function(){
33695         return this.el;
33696     },
33697     
33698     /**
33699      * Returns the specified region.
33700      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33701      * @return {Roo.LayoutRegion}
33702      */
33703     getRegion : function(target){
33704         return this.regions[target.toLowerCase()];
33705     },
33706     
33707     onWindowResize : function(){
33708         if(this.monitorWindowResize){
33709             this.layout();
33710         }
33711     }
33712 });/*
33713  * Based on:
33714  * Ext JS Library 1.1.1
33715  * Copyright(c) 2006-2007, Ext JS, LLC.
33716  *
33717  * Originally Released Under LGPL - original licence link has changed is not relivant.
33718  *
33719  * Fork - LGPL
33720  * <script type="text/javascript">
33721  */
33722 /**
33723  * @class Roo.BorderLayout
33724  * @extends Roo.LayoutManager
33725  * @children Roo.ContentPanel
33726  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33727  * please see: <br><br>
33728  * <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>
33729  * <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>
33730  * Example:
33731  <pre><code>
33732  var layout = new Roo.BorderLayout(document.body, {
33733     north: {
33734         initialSize: 25,
33735         titlebar: false
33736     },
33737     west: {
33738         split:true,
33739         initialSize: 200,
33740         minSize: 175,
33741         maxSize: 400,
33742         titlebar: true,
33743         collapsible: true
33744     },
33745     east: {
33746         split:true,
33747         initialSize: 202,
33748         minSize: 175,
33749         maxSize: 400,
33750         titlebar: true,
33751         collapsible: true
33752     },
33753     south: {
33754         split:true,
33755         initialSize: 100,
33756         minSize: 100,
33757         maxSize: 200,
33758         titlebar: true,
33759         collapsible: true
33760     },
33761     center: {
33762         titlebar: true,
33763         autoScroll:true,
33764         resizeTabs: true,
33765         minTabWidth: 50,
33766         preferredTabWidth: 150
33767     }
33768 });
33769
33770 // shorthand
33771 var CP = Roo.ContentPanel;
33772
33773 layout.beginUpdate();
33774 layout.add("north", new CP("north", "North"));
33775 layout.add("south", new CP("south", {title: "South", closable: true}));
33776 layout.add("west", new CP("west", {title: "West"}));
33777 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33778 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33779 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33780 layout.getRegion("center").showPanel("center1");
33781 layout.endUpdate();
33782 </code></pre>
33783
33784 <b>The container the layout is rendered into can be either the body element or any other element.
33785 If it is not the body element, the container needs to either be an absolute positioned element,
33786 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33787 the container size if it is not the body element.</b>
33788
33789 * @constructor
33790 * Create a new BorderLayout
33791 * @param {String/HTMLElement/Element} container The container this layout is bound to
33792 * @param {Object} config Configuration options
33793  */
33794 Roo.BorderLayout = function(container, config){
33795     config = config || {};
33796     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33797     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33798     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33799         var target = this.factory.validRegions[i];
33800         if(config[target]){
33801             this.addRegion(target, config[target]);
33802         }
33803     }
33804 };
33805
33806 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33807         
33808         /**
33809          * @cfg {Roo.LayoutRegion} east
33810          */
33811         /**
33812          * @cfg {Roo.LayoutRegion} west
33813          */
33814         /**
33815          * @cfg {Roo.LayoutRegion} north
33816          */
33817         /**
33818          * @cfg {Roo.LayoutRegion} south
33819          */
33820         /**
33821          * @cfg {Roo.LayoutRegion} center
33822          */
33823     /**
33824      * Creates and adds a new region if it doesn't already exist.
33825      * @param {String} target The target region key (north, south, east, west or center).
33826      * @param {Object} config The regions config object
33827      * @return {BorderLayoutRegion} The new region
33828      */
33829     addRegion : function(target, config){
33830         if(!this.regions[target]){
33831             var r = this.factory.create(target, this, config);
33832             this.bindRegion(target, r);
33833         }
33834         return this.regions[target];
33835     },
33836
33837     // private (kinda)
33838     bindRegion : function(name, r){
33839         this.regions[name] = r;
33840         r.on("visibilitychange", this.layout, this);
33841         r.on("paneladded", this.layout, this);
33842         r.on("panelremoved", this.layout, this);
33843         r.on("invalidated", this.layout, this);
33844         r.on("resized", this.onRegionResized, this);
33845         r.on("collapsed", this.onRegionCollapsed, this);
33846         r.on("expanded", this.onRegionExpanded, this);
33847     },
33848
33849     /**
33850      * Performs a layout update.
33851      */
33852     layout : function(){
33853         if(this.updating) {
33854             return;
33855         }
33856         var size = this.getViewSize();
33857         var w = size.width;
33858         var h = size.height;
33859         var centerW = w;
33860         var centerH = h;
33861         var centerY = 0;
33862         var centerX = 0;
33863         //var x = 0, y = 0;
33864
33865         var rs = this.regions;
33866         var north = rs["north"];
33867         var south = rs["south"]; 
33868         var west = rs["west"];
33869         var east = rs["east"];
33870         var center = rs["center"];
33871         //if(this.hideOnLayout){ // not supported anymore
33872             //c.el.setStyle("display", "none");
33873         //}
33874         if(north && north.isVisible()){
33875             var b = north.getBox();
33876             var m = north.getMargins();
33877             b.width = w - (m.left+m.right);
33878             b.x = m.left;
33879             b.y = m.top;
33880             centerY = b.height + b.y + m.bottom;
33881             centerH -= centerY;
33882             north.updateBox(this.safeBox(b));
33883         }
33884         if(south && south.isVisible()){
33885             var b = south.getBox();
33886             var m = south.getMargins();
33887             b.width = w - (m.left+m.right);
33888             b.x = m.left;
33889             var totalHeight = (b.height + m.top + m.bottom);
33890             b.y = h - totalHeight + m.top;
33891             centerH -= totalHeight;
33892             south.updateBox(this.safeBox(b));
33893         }
33894         if(west && west.isVisible()){
33895             var b = west.getBox();
33896             var m = west.getMargins();
33897             b.height = centerH - (m.top+m.bottom);
33898             b.x = m.left;
33899             b.y = centerY + m.top;
33900             var totalWidth = (b.width + m.left + m.right);
33901             centerX += totalWidth;
33902             centerW -= totalWidth;
33903             west.updateBox(this.safeBox(b));
33904         }
33905         if(east && east.isVisible()){
33906             var b = east.getBox();
33907             var m = east.getMargins();
33908             b.height = centerH - (m.top+m.bottom);
33909             var totalWidth = (b.width + m.left + m.right);
33910             b.x = w - totalWidth + m.left;
33911             b.y = centerY + m.top;
33912             centerW -= totalWidth;
33913             east.updateBox(this.safeBox(b));
33914         }
33915         if(center){
33916             var m = center.getMargins();
33917             var centerBox = {
33918                 x: centerX + m.left,
33919                 y: centerY + m.top,
33920                 width: centerW - (m.left+m.right),
33921                 height: centerH - (m.top+m.bottom)
33922             };
33923             //if(this.hideOnLayout){
33924                 //center.el.setStyle("display", "block");
33925             //}
33926             center.updateBox(this.safeBox(centerBox));
33927         }
33928         this.el.repaint();
33929         this.fireEvent("layout", this);
33930     },
33931
33932     // private
33933     safeBox : function(box){
33934         box.width = Math.max(0, box.width);
33935         box.height = Math.max(0, box.height);
33936         return box;
33937     },
33938
33939     /**
33940      * Adds a ContentPanel (or subclass) to this layout.
33941      * @param {String} target The target region key (north, south, east, west or center).
33942      * @param {Roo.ContentPanel} panel The panel to add
33943      * @return {Roo.ContentPanel} The added panel
33944      */
33945     add : function(target, panel){
33946          
33947         target = target.toLowerCase();
33948         return this.regions[target].add(panel);
33949     },
33950
33951     /**
33952      * Remove a ContentPanel (or subclass) to this layout.
33953      * @param {String} target The target region key (north, south, east, west or center).
33954      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33955      * @return {Roo.ContentPanel} The removed panel
33956      */
33957     remove : function(target, panel){
33958         target = target.toLowerCase();
33959         return this.regions[target].remove(panel);
33960     },
33961
33962     /**
33963      * Searches all regions for a panel with the specified id
33964      * @param {String} panelId
33965      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33966      */
33967     findPanel : function(panelId){
33968         var rs = this.regions;
33969         for(var target in rs){
33970             if(typeof rs[target] != "function"){
33971                 var p = rs[target].getPanel(panelId);
33972                 if(p){
33973                     return p;
33974                 }
33975             }
33976         }
33977         return null;
33978     },
33979
33980     /**
33981      * Searches all regions for a panel with the specified id and activates (shows) it.
33982      * @param {String/ContentPanel} panelId The panels id or the panel itself
33983      * @return {Roo.ContentPanel} The shown panel or null
33984      */
33985     showPanel : function(panelId) {
33986       var rs = this.regions;
33987       for(var target in rs){
33988          var r = rs[target];
33989          if(typeof r != "function"){
33990             if(r.hasPanel(panelId)){
33991                return r.showPanel(panelId);
33992             }
33993          }
33994       }
33995       return null;
33996    },
33997
33998    /**
33999      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34000      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34001      */
34002     restoreState : function(provider){
34003         if(!provider){
34004             provider = Roo.state.Manager;
34005         }
34006         var sm = new Roo.LayoutStateManager();
34007         sm.init(this, provider);
34008     },
34009
34010     /**
34011      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
34012      * object should contain properties for each region to add ContentPanels to, and each property's value should be
34013      * a valid ContentPanel config object.  Example:
34014      * <pre><code>
34015 // Create the main layout
34016 var layout = new Roo.BorderLayout('main-ct', {
34017     west: {
34018         split:true,
34019         minSize: 175,
34020         titlebar: true
34021     },
34022     center: {
34023         title:'Components'
34024     }
34025 }, 'main-ct');
34026
34027 // Create and add multiple ContentPanels at once via configs
34028 layout.batchAdd({
34029    west: {
34030        id: 'source-files',
34031        autoCreate:true,
34032        title:'Ext Source Files',
34033        autoScroll:true,
34034        fitToFrame:true
34035    },
34036    center : {
34037        el: cview,
34038        autoScroll:true,
34039        fitToFrame:true,
34040        toolbar: tb,
34041        resizeEl:'cbody'
34042    }
34043 });
34044 </code></pre>
34045      * @param {Object} regions An object containing ContentPanel configs by region name
34046      */
34047     batchAdd : function(regions){
34048         this.beginUpdate();
34049         for(var rname in regions){
34050             var lr = this.regions[rname];
34051             if(lr){
34052                 this.addTypedPanels(lr, regions[rname]);
34053             }
34054         }
34055         this.endUpdate();
34056     },
34057
34058     // private
34059     addTypedPanels : function(lr, ps){
34060         if(typeof ps == 'string'){
34061             lr.add(new Roo.ContentPanel(ps));
34062         }
34063         else if(ps instanceof Array){
34064             for(var i =0, len = ps.length; i < len; i++){
34065                 this.addTypedPanels(lr, ps[i]);
34066             }
34067         }
34068         else if(!ps.events){ // raw config?
34069             var el = ps.el;
34070             delete ps.el; // prevent conflict
34071             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
34072         }
34073         else {  // panel object assumed!
34074             lr.add(ps);
34075         }
34076     },
34077     /**
34078      * Adds a xtype elements to the layout.
34079      * <pre><code>
34080
34081 layout.addxtype({
34082        xtype : 'ContentPanel',
34083        region: 'west',
34084        items: [ .... ]
34085    }
34086 );
34087
34088 layout.addxtype({
34089         xtype : 'NestedLayoutPanel',
34090         region: 'west',
34091         layout: {
34092            center: { },
34093            west: { }   
34094         },
34095         items : [ ... list of content panels or nested layout panels.. ]
34096    }
34097 );
34098 </code></pre>
34099      * @param {Object} cfg Xtype definition of item to add.
34100      */
34101     addxtype : function(cfg)
34102     {
34103         // basically accepts a pannel...
34104         // can accept a layout region..!?!?
34105         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34106         
34107         if (!cfg.xtype.match(/Panel$/)) {
34108             return false;
34109         }
34110         var ret = false;
34111         
34112         if (typeof(cfg.region) == 'undefined') {
34113             Roo.log("Failed to add Panel, region was not set");
34114             Roo.log(cfg);
34115             return false;
34116         }
34117         var region = cfg.region;
34118         delete cfg.region;
34119         
34120           
34121         var xitems = [];
34122         if (cfg.items) {
34123             xitems = cfg.items;
34124             delete cfg.items;
34125         }
34126         var nb = false;
34127         
34128         switch(cfg.xtype) 
34129         {
34130             case 'ContentPanel':  // ContentPanel (el, cfg)
34131             case 'ScrollPanel':  // ContentPanel (el, cfg)
34132             case 'ViewPanel': 
34133                 if(cfg.autoCreate) {
34134                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34135                 } else {
34136                     var el = this.el.createChild();
34137                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34138                 }
34139                 
34140                 this.add(region, ret);
34141                 break;
34142             
34143             
34144             case 'TreePanel': // our new panel!
34145                 cfg.el = this.el.createChild();
34146                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34147                 this.add(region, ret);
34148                 break;
34149             
34150             case 'NestedLayoutPanel': 
34151                 // create a new Layout (which is  a Border Layout...
34152                 var el = this.el.createChild();
34153                 var clayout = cfg.layout;
34154                 delete cfg.layout;
34155                 clayout.items   = clayout.items  || [];
34156                 // replace this exitems with the clayout ones..
34157                 xitems = clayout.items;
34158                  
34159                 
34160                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34161                     cfg.background = false;
34162                 }
34163                 var layout = new Roo.BorderLayout(el, clayout);
34164                 
34165                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
34166                 //console.log('adding nested layout panel '  + cfg.toSource());
34167                 this.add(region, ret);
34168                 nb = {}; /// find first...
34169                 break;
34170                 
34171             case 'GridPanel': 
34172             
34173                 // needs grid and region
34174                 
34175                 //var el = this.getRegion(region).el.createChild();
34176                 var el = this.el.createChild();
34177                 // create the grid first...
34178                 
34179                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
34180                 delete cfg.grid;
34181                 if (region == 'center' && this.active ) {
34182                     cfg.background = false;
34183                 }
34184                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
34185                 
34186                 this.add(region, ret);
34187                 if (cfg.background) {
34188                     ret.on('activate', function(gp) {
34189                         if (!gp.grid.rendered) {
34190                             gp.grid.render();
34191                         }
34192                     });
34193                 } else {
34194                     grid.render();
34195                 }
34196                 break;
34197            
34198            
34199            
34200                 
34201                 
34202                 
34203             default:
34204                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34205                     
34206                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34207                     this.add(region, ret);
34208                 } else {
34209                 
34210                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34211                     return null;
34212                 }
34213                 
34214              // GridPanel (grid, cfg)
34215             
34216         }
34217         this.beginUpdate();
34218         // add children..
34219         var region = '';
34220         var abn = {};
34221         Roo.each(xitems, function(i)  {
34222             region = nb && i.region ? i.region : false;
34223             
34224             var add = ret.addxtype(i);
34225            
34226             if (region) {
34227                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34228                 if (!i.background) {
34229                     abn[region] = nb[region] ;
34230                 }
34231             }
34232             
34233         });
34234         this.endUpdate();
34235
34236         // make the last non-background panel active..
34237         //if (nb) { Roo.log(abn); }
34238         if (nb) {
34239             
34240             for(var r in abn) {
34241                 region = this.getRegion(r);
34242                 if (region) {
34243                     // tried using nb[r], but it does not work..
34244                      
34245                     region.showPanel(abn[r]);
34246                    
34247                 }
34248             }
34249         }
34250         return ret;
34251         
34252     }
34253 });
34254
34255 /**
34256  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34257  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34258  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34259  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34260  * <pre><code>
34261 // shorthand
34262 var CP = Roo.ContentPanel;
34263
34264 var layout = Roo.BorderLayout.create({
34265     north: {
34266         initialSize: 25,
34267         titlebar: false,
34268         panels: [new CP("north", "North")]
34269     },
34270     west: {
34271         split:true,
34272         initialSize: 200,
34273         minSize: 175,
34274         maxSize: 400,
34275         titlebar: true,
34276         collapsible: true,
34277         panels: [new CP("west", {title: "West"})]
34278     },
34279     east: {
34280         split:true,
34281         initialSize: 202,
34282         minSize: 175,
34283         maxSize: 400,
34284         titlebar: true,
34285         collapsible: true,
34286         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34287     },
34288     south: {
34289         split:true,
34290         initialSize: 100,
34291         minSize: 100,
34292         maxSize: 200,
34293         titlebar: true,
34294         collapsible: true,
34295         panels: [new CP("south", {title: "South", closable: true})]
34296     },
34297     center: {
34298         titlebar: true,
34299         autoScroll:true,
34300         resizeTabs: true,
34301         minTabWidth: 50,
34302         preferredTabWidth: 150,
34303         panels: [
34304             new CP("center1", {title: "Close Me", closable: true}),
34305             new CP("center2", {title: "Center Panel", closable: false})
34306         ]
34307     }
34308 }, document.body);
34309
34310 layout.getRegion("center").showPanel("center1");
34311 </code></pre>
34312  * @param config
34313  * @param targetEl
34314  */
34315 Roo.BorderLayout.create = function(config, targetEl){
34316     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34317     layout.beginUpdate();
34318     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34319     for(var j = 0, jlen = regions.length; j < jlen; j++){
34320         var lr = regions[j];
34321         if(layout.regions[lr] && config[lr].panels){
34322             var r = layout.regions[lr];
34323             var ps = config[lr].panels;
34324             layout.addTypedPanels(r, ps);
34325         }
34326     }
34327     layout.endUpdate();
34328     return layout;
34329 };
34330
34331 // private
34332 Roo.BorderLayout.RegionFactory = {
34333     // private
34334     validRegions : ["north","south","east","west","center"],
34335
34336     // private
34337     create : function(target, mgr, config){
34338         target = target.toLowerCase();
34339         if(config.lightweight || config.basic){
34340             return new Roo.BasicLayoutRegion(mgr, config, target);
34341         }
34342         switch(target){
34343             case "north":
34344                 return new Roo.NorthLayoutRegion(mgr, config);
34345             case "south":
34346                 return new Roo.SouthLayoutRegion(mgr, config);
34347             case "east":
34348                 return new Roo.EastLayoutRegion(mgr, config);
34349             case "west":
34350                 return new Roo.WestLayoutRegion(mgr, config);
34351             case "center":
34352                 return new Roo.CenterLayoutRegion(mgr, config);
34353         }
34354         throw 'Layout region "'+target+'" not supported.';
34355     }
34356 };/*
34357  * Based on:
34358  * Ext JS Library 1.1.1
34359  * Copyright(c) 2006-2007, Ext JS, LLC.
34360  *
34361  * Originally Released Under LGPL - original licence link has changed is not relivant.
34362  *
34363  * Fork - LGPL
34364  * <script type="text/javascript">
34365  */
34366  
34367 /**
34368  * @class Roo.BasicLayoutRegion
34369  * @extends Roo.util.Observable
34370  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34371  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34372  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34373  */
34374 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34375     this.mgr = mgr;
34376     this.position  = pos;
34377     this.events = {
34378         /**
34379          * @scope Roo.BasicLayoutRegion
34380          */
34381         
34382         /**
34383          * @event beforeremove
34384          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34385          * @param {Roo.LayoutRegion} this
34386          * @param {Roo.ContentPanel} panel The panel
34387          * @param {Object} e The cancel event object
34388          */
34389         "beforeremove" : true,
34390         /**
34391          * @event invalidated
34392          * Fires when the layout for this region is changed.
34393          * @param {Roo.LayoutRegion} this
34394          */
34395         "invalidated" : true,
34396         /**
34397          * @event visibilitychange
34398          * Fires when this region is shown or hidden 
34399          * @param {Roo.LayoutRegion} this
34400          * @param {Boolean} visibility true or false
34401          */
34402         "visibilitychange" : true,
34403         /**
34404          * @event paneladded
34405          * Fires when a panel is added. 
34406          * @param {Roo.LayoutRegion} this
34407          * @param {Roo.ContentPanel} panel The panel
34408          */
34409         "paneladded" : true,
34410         /**
34411          * @event panelremoved
34412          * Fires when a panel is removed. 
34413          * @param {Roo.LayoutRegion} this
34414          * @param {Roo.ContentPanel} panel The panel
34415          */
34416         "panelremoved" : true,
34417         /**
34418          * @event beforecollapse
34419          * Fires when this region before collapse.
34420          * @param {Roo.LayoutRegion} this
34421          */
34422         "beforecollapse" : true,
34423         /**
34424          * @event collapsed
34425          * Fires when this region is collapsed.
34426          * @param {Roo.LayoutRegion} this
34427          */
34428         "collapsed" : true,
34429         /**
34430          * @event expanded
34431          * Fires when this region is expanded.
34432          * @param {Roo.LayoutRegion} this
34433          */
34434         "expanded" : true,
34435         /**
34436          * @event slideshow
34437          * Fires when this region is slid into view.
34438          * @param {Roo.LayoutRegion} this
34439          */
34440         "slideshow" : true,
34441         /**
34442          * @event slidehide
34443          * Fires when this region slides out of view. 
34444          * @param {Roo.LayoutRegion} this
34445          */
34446         "slidehide" : true,
34447         /**
34448          * @event panelactivated
34449          * Fires when a panel is activated. 
34450          * @param {Roo.LayoutRegion} this
34451          * @param {Roo.ContentPanel} panel The activated panel
34452          */
34453         "panelactivated" : true,
34454         /**
34455          * @event resized
34456          * Fires when the user resizes this region. 
34457          * @param {Roo.LayoutRegion} this
34458          * @param {Number} newSize The new size (width for east/west, height for north/south)
34459          */
34460         "resized" : true
34461     };
34462     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34463     this.panels = new Roo.util.MixedCollection();
34464     this.panels.getKey = this.getPanelId.createDelegate(this);
34465     this.box = null;
34466     this.activePanel = null;
34467     // ensure listeners are added...
34468     
34469     if (config.listeners || config.events) {
34470         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34471             listeners : config.listeners || {},
34472             events : config.events || {}
34473         });
34474     }
34475     
34476     if(skipConfig !== true){
34477         this.applyConfig(config);
34478     }
34479 };
34480
34481 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34482     getPanelId : function(p){
34483         return p.getId();
34484     },
34485     
34486     applyConfig : function(config){
34487         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34488         this.config = config;
34489         
34490     },
34491     
34492     /**
34493      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34494      * the width, for horizontal (north, south) the height.
34495      * @param {Number} newSize The new width or height
34496      */
34497     resizeTo : function(newSize){
34498         var el = this.el ? this.el :
34499                  (this.activePanel ? this.activePanel.getEl() : null);
34500         if(el){
34501             switch(this.position){
34502                 case "east":
34503                 case "west":
34504                     el.setWidth(newSize);
34505                     this.fireEvent("resized", this, newSize);
34506                 break;
34507                 case "north":
34508                 case "south":
34509                     el.setHeight(newSize);
34510                     this.fireEvent("resized", this, newSize);
34511                 break;                
34512             }
34513         }
34514     },
34515     
34516     getBox : function(){
34517         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34518     },
34519     
34520     getMargins : function(){
34521         return this.margins;
34522     },
34523     
34524     updateBox : function(box){
34525         this.box = box;
34526         var el = this.activePanel.getEl();
34527         el.dom.style.left = box.x + "px";
34528         el.dom.style.top = box.y + "px";
34529         this.activePanel.setSize(box.width, box.height);
34530     },
34531     
34532     /**
34533      * Returns the container element for this region.
34534      * @return {Roo.Element}
34535      */
34536     getEl : function(){
34537         return this.activePanel;
34538     },
34539     
34540     /**
34541      * Returns true if this region is currently visible.
34542      * @return {Boolean}
34543      */
34544     isVisible : function(){
34545         return this.activePanel ? true : false;
34546     },
34547     
34548     setActivePanel : function(panel){
34549         panel = this.getPanel(panel);
34550         if(this.activePanel && this.activePanel != panel){
34551             this.activePanel.setActiveState(false);
34552             this.activePanel.getEl().setLeftTop(-10000,-10000);
34553         }
34554         this.activePanel = panel;
34555         panel.setActiveState(true);
34556         if(this.box){
34557             panel.setSize(this.box.width, this.box.height);
34558         }
34559         this.fireEvent("panelactivated", this, panel);
34560         this.fireEvent("invalidated");
34561     },
34562     
34563     /**
34564      * Show the specified panel.
34565      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34566      * @return {Roo.ContentPanel} The shown panel or null
34567      */
34568     showPanel : function(panel){
34569         if(panel = this.getPanel(panel)){
34570             this.setActivePanel(panel);
34571         }
34572         return panel;
34573     },
34574     
34575     /**
34576      * Get the active panel for this region.
34577      * @return {Roo.ContentPanel} The active panel or null
34578      */
34579     getActivePanel : function(){
34580         return this.activePanel;
34581     },
34582     
34583     /**
34584      * Add the passed ContentPanel(s)
34585      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34586      * @return {Roo.ContentPanel} The panel added (if only one was added)
34587      */
34588     add : function(panel){
34589         if(arguments.length > 1){
34590             for(var i = 0, len = arguments.length; i < len; i++) {
34591                 this.add(arguments[i]);
34592             }
34593             return null;
34594         }
34595         if(this.hasPanel(panel)){
34596             this.showPanel(panel);
34597             return panel;
34598         }
34599         var el = panel.getEl();
34600         if(el.dom.parentNode != this.mgr.el.dom){
34601             this.mgr.el.dom.appendChild(el.dom);
34602         }
34603         if(panel.setRegion){
34604             panel.setRegion(this);
34605         }
34606         this.panels.add(panel);
34607         el.setStyle("position", "absolute");
34608         if(!panel.background){
34609             this.setActivePanel(panel);
34610             if(this.config.initialSize && this.panels.getCount()==1){
34611                 this.resizeTo(this.config.initialSize);
34612             }
34613         }
34614         this.fireEvent("paneladded", this, panel);
34615         return panel;
34616     },
34617     
34618     /**
34619      * Returns true if the panel is in this region.
34620      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34621      * @return {Boolean}
34622      */
34623     hasPanel : function(panel){
34624         if(typeof panel == "object"){ // must be panel obj
34625             panel = panel.getId();
34626         }
34627         return this.getPanel(panel) ? true : false;
34628     },
34629     
34630     /**
34631      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34632      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34633      * @param {Boolean} preservePanel Overrides the config preservePanel option
34634      * @return {Roo.ContentPanel} The panel that was removed
34635      */
34636     remove : function(panel, preservePanel){
34637         panel = this.getPanel(panel);
34638         if(!panel){
34639             return null;
34640         }
34641         var e = {};
34642         this.fireEvent("beforeremove", this, panel, e);
34643         if(e.cancel === true){
34644             return null;
34645         }
34646         var panelId = panel.getId();
34647         this.panels.removeKey(panelId);
34648         return panel;
34649     },
34650     
34651     /**
34652      * Returns the panel specified or null if it's not in this region.
34653      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34654      * @return {Roo.ContentPanel}
34655      */
34656     getPanel : function(id){
34657         if(typeof id == "object"){ // must be panel obj
34658             return id;
34659         }
34660         return this.panels.get(id);
34661     },
34662     
34663     /**
34664      * Returns this regions position (north/south/east/west/center).
34665      * @return {String} 
34666      */
34667     getPosition: function(){
34668         return this.position;    
34669     }
34670 });/*
34671  * Based on:
34672  * Ext JS Library 1.1.1
34673  * Copyright(c) 2006-2007, Ext JS, LLC.
34674  *
34675  * Originally Released Under LGPL - original licence link has changed is not relivant.
34676  *
34677  * Fork - LGPL
34678  * <script type="text/javascript">
34679  */
34680  
34681 /**
34682  * @class Roo.LayoutRegion
34683  * @extends Roo.BasicLayoutRegion
34684  * This class represents a region in a layout manager.
34685  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34686  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34687  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34688  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34689  * @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})
34690  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34691  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34692  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34693  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34694  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34695  * @cfg {String}    title           The title for the region (overrides panel titles)
34696  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34697  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34698  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34699  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34700  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34701  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34702  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34703  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34704  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34705  * @cfg {Boolean}   showPin         True to show a pin button
34706  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34707  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34708  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34709  * @cfg {Number}    width           For East/West panels
34710  * @cfg {Number}    height          For North/South panels
34711  * @cfg {Boolean}   split           To show the splitter
34712  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34713  */
34714 Roo.LayoutRegion = function(mgr, config, pos){
34715     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34716     var dh = Roo.DomHelper;
34717     /** This region's container element 
34718     * @type Roo.Element */
34719     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34720     /** This region's title element 
34721     * @type Roo.Element */
34722
34723     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34724         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34725         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34726     ]}, true);
34727     this.titleEl.enableDisplayMode();
34728     /** This region's title text element 
34729     * @type HTMLElement */
34730     this.titleTextEl = this.titleEl.dom.firstChild;
34731     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34732     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34733     this.closeBtn.enableDisplayMode();
34734     this.closeBtn.on("click", this.closeClicked, this);
34735     this.closeBtn.hide();
34736
34737     this.createBody(config);
34738     this.visible = true;
34739     this.collapsed = false;
34740
34741     if(config.hideWhenEmpty){
34742         this.hide();
34743         this.on("paneladded", this.validateVisibility, this);
34744         this.on("panelremoved", this.validateVisibility, this);
34745     }
34746     this.applyConfig(config);
34747 };
34748
34749 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34750
34751     createBody : function(){
34752         /** This region's body element 
34753         * @type Roo.Element */
34754         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34755     },
34756
34757     applyConfig : function(c){
34758         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34759             var dh = Roo.DomHelper;
34760             if(c.titlebar !== false){
34761                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34762                 this.collapseBtn.on("click", this.collapse, this);
34763                 this.collapseBtn.enableDisplayMode();
34764
34765                 if(c.showPin === true || this.showPin){
34766                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34767                     this.stickBtn.enableDisplayMode();
34768                     this.stickBtn.on("click", this.expand, this);
34769                     this.stickBtn.hide();
34770                 }
34771             }
34772             /** This region's collapsed element
34773             * @type Roo.Element */
34774             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34775                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34776             ]}, true);
34777             if(c.floatable !== false){
34778                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34779                this.collapsedEl.on("click", this.collapseClick, this);
34780             }
34781
34782             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34783                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34784                    id: "message", unselectable: "on", style:{"float":"left"}});
34785                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34786              }
34787             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34788             this.expandBtn.on("click", this.expand, this);
34789         }
34790         if(this.collapseBtn){
34791             this.collapseBtn.setVisible(c.collapsible == true);
34792         }
34793         this.cmargins = c.cmargins || this.cmargins ||
34794                          (this.position == "west" || this.position == "east" ?
34795                              {top: 0, left: 2, right:2, bottom: 0} :
34796                              {top: 2, left: 0, right:0, bottom: 2});
34797         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34798         this.bottomTabs = c.tabPosition != "top";
34799         this.autoScroll = c.autoScroll || false;
34800         if(this.autoScroll){
34801             this.bodyEl.setStyle("overflow", "auto");
34802         }else{
34803             this.bodyEl.setStyle("overflow", "hidden");
34804         }
34805         //if(c.titlebar !== false){
34806             if((!c.titlebar && !c.title) || c.titlebar === false){
34807                 this.titleEl.hide();
34808             }else{
34809                 this.titleEl.show();
34810                 if(c.title){
34811                     this.titleTextEl.innerHTML = c.title;
34812                 }
34813             }
34814         //}
34815         this.duration = c.duration || .30;
34816         this.slideDuration = c.slideDuration || .45;
34817         this.config = c;
34818         if(c.collapsed){
34819             this.collapse(true);
34820         }
34821         if(c.hidden){
34822             this.hide();
34823         }
34824     },
34825     /**
34826      * Returns true if this region is currently visible.
34827      * @return {Boolean}
34828      */
34829     isVisible : function(){
34830         return this.visible;
34831     },
34832
34833     /**
34834      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34835      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34836      */
34837     setCollapsedTitle : function(title){
34838         title = title || "&#160;";
34839         if(this.collapsedTitleTextEl){
34840             this.collapsedTitleTextEl.innerHTML = title;
34841         }
34842     },
34843
34844     getBox : function(){
34845         var b;
34846         if(!this.collapsed){
34847             b = this.el.getBox(false, true);
34848         }else{
34849             b = this.collapsedEl.getBox(false, true);
34850         }
34851         return b;
34852     },
34853
34854     getMargins : function(){
34855         return this.collapsed ? this.cmargins : this.margins;
34856     },
34857
34858     highlight : function(){
34859         this.el.addClass("x-layout-panel-dragover");
34860     },
34861
34862     unhighlight : function(){
34863         this.el.removeClass("x-layout-panel-dragover");
34864     },
34865
34866     updateBox : function(box){
34867         this.box = box;
34868         if(!this.collapsed){
34869             this.el.dom.style.left = box.x + "px";
34870             this.el.dom.style.top = box.y + "px";
34871             this.updateBody(box.width, box.height);
34872         }else{
34873             this.collapsedEl.dom.style.left = box.x + "px";
34874             this.collapsedEl.dom.style.top = box.y + "px";
34875             this.collapsedEl.setSize(box.width, box.height);
34876         }
34877         if(this.tabs){
34878             this.tabs.autoSizeTabs();
34879         }
34880     },
34881
34882     updateBody : function(w, h){
34883         if(w !== null){
34884             this.el.setWidth(w);
34885             w -= this.el.getBorderWidth("rl");
34886             if(this.config.adjustments){
34887                 w += this.config.adjustments[0];
34888             }
34889         }
34890         if(h !== null){
34891             this.el.setHeight(h);
34892             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34893             h -= this.el.getBorderWidth("tb");
34894             if(this.config.adjustments){
34895                 h += this.config.adjustments[1];
34896             }
34897             this.bodyEl.setHeight(h);
34898             if(this.tabs){
34899                 h = this.tabs.syncHeight(h);
34900             }
34901         }
34902         if(this.panelSize){
34903             w = w !== null ? w : this.panelSize.width;
34904             h = h !== null ? h : this.panelSize.height;
34905         }
34906         if(this.activePanel){
34907             var el = this.activePanel.getEl();
34908             w = w !== null ? w : el.getWidth();
34909             h = h !== null ? h : el.getHeight();
34910             this.panelSize = {width: w, height: h};
34911             this.activePanel.setSize(w, h);
34912         }
34913         if(Roo.isIE && this.tabs){
34914             this.tabs.el.repaint();
34915         }
34916     },
34917
34918     /**
34919      * Returns the container element for this region.
34920      * @return {Roo.Element}
34921      */
34922     getEl : function(){
34923         return this.el;
34924     },
34925
34926     /**
34927      * Hides this region.
34928      */
34929     hide : function(){
34930         if(!this.collapsed){
34931             this.el.dom.style.left = "-2000px";
34932             this.el.hide();
34933         }else{
34934             this.collapsedEl.dom.style.left = "-2000px";
34935             this.collapsedEl.hide();
34936         }
34937         this.visible = false;
34938         this.fireEvent("visibilitychange", this, false);
34939     },
34940
34941     /**
34942      * Shows this region if it was previously hidden.
34943      */
34944     show : function(){
34945         if(!this.collapsed){
34946             this.el.show();
34947         }else{
34948             this.collapsedEl.show();
34949         }
34950         this.visible = true;
34951         this.fireEvent("visibilitychange", this, true);
34952     },
34953
34954     closeClicked : function(){
34955         if(this.activePanel){
34956             this.remove(this.activePanel);
34957         }
34958     },
34959
34960     collapseClick : function(e){
34961         if(this.isSlid){
34962            e.stopPropagation();
34963            this.slideIn();
34964         }else{
34965            e.stopPropagation();
34966            this.slideOut();
34967         }
34968     },
34969
34970     /**
34971      * Collapses this region.
34972      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34973      */
34974     collapse : function(skipAnim, skipCheck){
34975         if(this.collapsed) {
34976             return;
34977         }
34978         
34979         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34980             
34981             this.collapsed = true;
34982             if(this.split){
34983                 this.split.el.hide();
34984             }
34985             if(this.config.animate && skipAnim !== true){
34986                 this.fireEvent("invalidated", this);
34987                 this.animateCollapse();
34988             }else{
34989                 this.el.setLocation(-20000,-20000);
34990                 this.el.hide();
34991                 this.collapsedEl.show();
34992                 this.fireEvent("collapsed", this);
34993                 this.fireEvent("invalidated", this);
34994             }
34995         }
34996         
34997     },
34998
34999     animateCollapse : function(){
35000         // overridden
35001     },
35002
35003     /**
35004      * Expands this region if it was previously collapsed.
35005      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35006      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35007      */
35008     expand : function(e, skipAnim){
35009         if(e) {
35010             e.stopPropagation();
35011         }
35012         if(!this.collapsed || this.el.hasActiveFx()) {
35013             return;
35014         }
35015         if(this.isSlid){
35016             this.afterSlideIn();
35017             skipAnim = true;
35018         }
35019         this.collapsed = false;
35020         if(this.config.animate && skipAnim !== true){
35021             this.animateExpand();
35022         }else{
35023             this.el.show();
35024             if(this.split){
35025                 this.split.el.show();
35026             }
35027             this.collapsedEl.setLocation(-2000,-2000);
35028             this.collapsedEl.hide();
35029             this.fireEvent("invalidated", this);
35030             this.fireEvent("expanded", this);
35031         }
35032     },
35033
35034     animateExpand : function(){
35035         // overridden
35036     },
35037
35038     initTabs : function()
35039     {
35040         this.bodyEl.setStyle("overflow", "hidden");
35041         var ts = new Roo.TabPanel(
35042                 this.bodyEl.dom,
35043                 {
35044                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
35045                     disableTooltips: this.config.disableTabTips,
35046                     toolbar : this.config.toolbar
35047                 }
35048         );
35049         if(this.config.hideTabs){
35050             ts.stripWrap.setDisplayed(false);
35051         }
35052         this.tabs = ts;
35053         ts.resizeTabs = this.config.resizeTabs === true;
35054         ts.minTabWidth = this.config.minTabWidth || 40;
35055         ts.maxTabWidth = this.config.maxTabWidth || 250;
35056         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35057         ts.monitorResize = false;
35058         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35059         ts.bodyEl.addClass('x-layout-tabs-body');
35060         this.panels.each(this.initPanelAsTab, this);
35061     },
35062
35063     initPanelAsTab : function(panel){
35064         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
35065                     this.config.closeOnTab && panel.isClosable());
35066         if(panel.tabTip !== undefined){
35067             ti.setTooltip(panel.tabTip);
35068         }
35069         ti.on("activate", function(){
35070               this.setActivePanel(panel);
35071         }, this);
35072         if(this.config.closeOnTab){
35073             ti.on("beforeclose", function(t, e){
35074                 e.cancel = true;
35075                 this.remove(panel);
35076             }, this);
35077         }
35078         return ti;
35079     },
35080
35081     updatePanelTitle : function(panel, title){
35082         if(this.activePanel == panel){
35083             this.updateTitle(title);
35084         }
35085         if(this.tabs){
35086             var ti = this.tabs.getTab(panel.getEl().id);
35087             ti.setText(title);
35088             if(panel.tabTip !== undefined){
35089                 ti.setTooltip(panel.tabTip);
35090             }
35091         }
35092     },
35093
35094     updateTitle : function(title){
35095         if(this.titleTextEl && !this.config.title){
35096             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35097         }
35098     },
35099
35100     setActivePanel : function(panel){
35101         panel = this.getPanel(panel);
35102         if(this.activePanel && this.activePanel != panel){
35103             this.activePanel.setActiveState(false);
35104         }
35105         this.activePanel = panel;
35106         panel.setActiveState(true);
35107         if(this.panelSize){
35108             panel.setSize(this.panelSize.width, this.panelSize.height);
35109         }
35110         if(this.closeBtn){
35111             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35112         }
35113         this.updateTitle(panel.getTitle());
35114         if(this.tabs){
35115             this.fireEvent("invalidated", this);
35116         }
35117         this.fireEvent("panelactivated", this, panel);
35118     },
35119
35120     /**
35121      * Shows the specified panel.
35122      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35123      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35124      */
35125     showPanel : function(panel)
35126     {
35127         panel = this.getPanel(panel);
35128         if(panel){
35129             if(this.tabs){
35130                 var tab = this.tabs.getTab(panel.getEl().id);
35131                 if(tab.isHidden()){
35132                     this.tabs.unhideTab(tab.id);
35133                 }
35134                 tab.activate();
35135             }else{
35136                 this.setActivePanel(panel);
35137             }
35138         }
35139         return panel;
35140     },
35141
35142     /**
35143      * Get the active panel for this region.
35144      * @return {Roo.ContentPanel} The active panel or null
35145      */
35146     getActivePanel : function(){
35147         return this.activePanel;
35148     },
35149
35150     validateVisibility : function(){
35151         if(this.panels.getCount() < 1){
35152             this.updateTitle("&#160;");
35153             this.closeBtn.hide();
35154             this.hide();
35155         }else{
35156             if(!this.isVisible()){
35157                 this.show();
35158             }
35159         }
35160     },
35161
35162     /**
35163      * Adds the passed ContentPanel(s) to this region.
35164      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35165      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35166      */
35167     add : function(panel){
35168         if(arguments.length > 1){
35169             for(var i = 0, len = arguments.length; i < len; i++) {
35170                 this.add(arguments[i]);
35171             }
35172             return null;
35173         }
35174         if(this.hasPanel(panel)){
35175             this.showPanel(panel);
35176             return panel;
35177         }
35178         panel.setRegion(this);
35179         this.panels.add(panel);
35180         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35181             this.bodyEl.dom.appendChild(panel.getEl().dom);
35182             if(panel.background !== true){
35183                 this.setActivePanel(panel);
35184             }
35185             this.fireEvent("paneladded", this, panel);
35186             return panel;
35187         }
35188         if(!this.tabs){
35189             this.initTabs();
35190         }else{
35191             this.initPanelAsTab(panel);
35192         }
35193         if(panel.background !== true){
35194             this.tabs.activate(panel.getEl().id);
35195         }
35196         this.fireEvent("paneladded", this, panel);
35197         return panel;
35198     },
35199
35200     /**
35201      * Hides the tab for the specified panel.
35202      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35203      */
35204     hidePanel : function(panel){
35205         if(this.tabs && (panel = this.getPanel(panel))){
35206             this.tabs.hideTab(panel.getEl().id);
35207         }
35208     },
35209
35210     /**
35211      * Unhides the tab for a previously hidden panel.
35212      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35213      */
35214     unhidePanel : function(panel){
35215         if(this.tabs && (panel = this.getPanel(panel))){
35216             this.tabs.unhideTab(panel.getEl().id);
35217         }
35218     },
35219
35220     clearPanels : function(){
35221         while(this.panels.getCount() > 0){
35222              this.remove(this.panels.first());
35223         }
35224     },
35225
35226     /**
35227      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35228      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35229      * @param {Boolean} preservePanel Overrides the config preservePanel option
35230      * @return {Roo.ContentPanel} The panel that was removed
35231      */
35232     remove : function(panel, preservePanel){
35233         panel = this.getPanel(panel);
35234         if(!panel){
35235             return null;
35236         }
35237         var e = {};
35238         this.fireEvent("beforeremove", this, panel, e);
35239         if(e.cancel === true){
35240             return null;
35241         }
35242         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35243         var panelId = panel.getId();
35244         this.panels.removeKey(panelId);
35245         if(preservePanel){
35246             document.body.appendChild(panel.getEl().dom);
35247         }
35248         if(this.tabs){
35249             this.tabs.removeTab(panel.getEl().id);
35250         }else if (!preservePanel){
35251             this.bodyEl.dom.removeChild(panel.getEl().dom);
35252         }
35253         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35254             var p = this.panels.first();
35255             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35256             tempEl.appendChild(p.getEl().dom);
35257             this.bodyEl.update("");
35258             this.bodyEl.dom.appendChild(p.getEl().dom);
35259             tempEl = null;
35260             this.updateTitle(p.getTitle());
35261             this.tabs = null;
35262             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35263             this.setActivePanel(p);
35264         }
35265         panel.setRegion(null);
35266         if(this.activePanel == panel){
35267             this.activePanel = null;
35268         }
35269         if(this.config.autoDestroy !== false && preservePanel !== true){
35270             try{panel.destroy();}catch(e){}
35271         }
35272         this.fireEvent("panelremoved", this, panel);
35273         return panel;
35274     },
35275
35276     /**
35277      * Returns the TabPanel component used by this region
35278      * @return {Roo.TabPanel}
35279      */
35280     getTabs : function(){
35281         return this.tabs;
35282     },
35283
35284     createTool : function(parentEl, className){
35285         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35286             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35287         btn.addClassOnOver("x-layout-tools-button-over");
35288         return btn;
35289     }
35290 });/*
35291  * Based on:
35292  * Ext JS Library 1.1.1
35293  * Copyright(c) 2006-2007, Ext JS, LLC.
35294  *
35295  * Originally Released Under LGPL - original licence link has changed is not relivant.
35296  *
35297  * Fork - LGPL
35298  * <script type="text/javascript">
35299  */
35300  
35301
35302
35303 /**
35304  * @class Roo.SplitLayoutRegion
35305  * @extends Roo.LayoutRegion
35306  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35307  */
35308 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35309     this.cursor = cursor;
35310     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35311 };
35312
35313 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35314     splitTip : "Drag to resize.",
35315     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35316     useSplitTips : false,
35317
35318     applyConfig : function(config){
35319         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35320         if(config.split){
35321             if(!this.split){
35322                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35323                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35324                 /** The SplitBar for this region 
35325                 * @type Roo.SplitBar */
35326                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35327                 this.split.on("moved", this.onSplitMove, this);
35328                 this.split.useShim = config.useShim === true;
35329                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35330                 if(this.useSplitTips){
35331                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35332                 }
35333                 if(config.collapsible){
35334                     this.split.el.on("dblclick", this.collapse,  this);
35335                 }
35336             }
35337             if(typeof config.minSize != "undefined"){
35338                 this.split.minSize = config.minSize;
35339             }
35340             if(typeof config.maxSize != "undefined"){
35341                 this.split.maxSize = config.maxSize;
35342             }
35343             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35344                 this.hideSplitter();
35345             }
35346         }
35347     },
35348
35349     getHMaxSize : function(){
35350          var cmax = this.config.maxSize || 10000;
35351          var center = this.mgr.getRegion("center");
35352          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35353     },
35354
35355     getVMaxSize : function(){
35356          var cmax = this.config.maxSize || 10000;
35357          var center = this.mgr.getRegion("center");
35358          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35359     },
35360
35361     onSplitMove : function(split, newSize){
35362         this.fireEvent("resized", this, newSize);
35363     },
35364     
35365     /** 
35366      * Returns the {@link Roo.SplitBar} for this region.
35367      * @return {Roo.SplitBar}
35368      */
35369     getSplitBar : function(){
35370         return this.split;
35371     },
35372     
35373     hide : function(){
35374         this.hideSplitter();
35375         Roo.SplitLayoutRegion.superclass.hide.call(this);
35376     },
35377
35378     hideSplitter : function(){
35379         if(this.split){
35380             this.split.el.setLocation(-2000,-2000);
35381             this.split.el.hide();
35382         }
35383     },
35384
35385     show : function(){
35386         if(this.split){
35387             this.split.el.show();
35388         }
35389         Roo.SplitLayoutRegion.superclass.show.call(this);
35390     },
35391     
35392     beforeSlide: function(){
35393         if(Roo.isGecko){// firefox overflow auto bug workaround
35394             this.bodyEl.clip();
35395             if(this.tabs) {
35396                 this.tabs.bodyEl.clip();
35397             }
35398             if(this.activePanel){
35399                 this.activePanel.getEl().clip();
35400                 
35401                 if(this.activePanel.beforeSlide){
35402                     this.activePanel.beforeSlide();
35403                 }
35404             }
35405         }
35406     },
35407     
35408     afterSlide : function(){
35409         if(Roo.isGecko){// firefox overflow auto bug workaround
35410             this.bodyEl.unclip();
35411             if(this.tabs) {
35412                 this.tabs.bodyEl.unclip();
35413             }
35414             if(this.activePanel){
35415                 this.activePanel.getEl().unclip();
35416                 if(this.activePanel.afterSlide){
35417                     this.activePanel.afterSlide();
35418                 }
35419             }
35420         }
35421     },
35422
35423     initAutoHide : function(){
35424         if(this.autoHide !== false){
35425             if(!this.autoHideHd){
35426                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35427                 this.autoHideHd = {
35428                     "mouseout": function(e){
35429                         if(!e.within(this.el, true)){
35430                             st.delay(500);
35431                         }
35432                     },
35433                     "mouseover" : function(e){
35434                         st.cancel();
35435                     },
35436                     scope : this
35437                 };
35438             }
35439             this.el.on(this.autoHideHd);
35440         }
35441     },
35442
35443     clearAutoHide : function(){
35444         if(this.autoHide !== false){
35445             this.el.un("mouseout", this.autoHideHd.mouseout);
35446             this.el.un("mouseover", this.autoHideHd.mouseover);
35447         }
35448     },
35449
35450     clearMonitor : function(){
35451         Roo.get(document).un("click", this.slideInIf, this);
35452     },
35453
35454     // these names are backwards but not changed for compat
35455     slideOut : function(){
35456         if(this.isSlid || this.el.hasActiveFx()){
35457             return;
35458         }
35459         this.isSlid = true;
35460         if(this.collapseBtn){
35461             this.collapseBtn.hide();
35462         }
35463         this.closeBtnState = this.closeBtn.getStyle('display');
35464         this.closeBtn.hide();
35465         if(this.stickBtn){
35466             this.stickBtn.show();
35467         }
35468         this.el.show();
35469         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35470         this.beforeSlide();
35471         this.el.setStyle("z-index", 10001);
35472         this.el.slideIn(this.getSlideAnchor(), {
35473             callback: function(){
35474                 this.afterSlide();
35475                 this.initAutoHide();
35476                 Roo.get(document).on("click", this.slideInIf, this);
35477                 this.fireEvent("slideshow", this);
35478             },
35479             scope: this,
35480             block: true
35481         });
35482     },
35483
35484     afterSlideIn : function(){
35485         this.clearAutoHide();
35486         this.isSlid = false;
35487         this.clearMonitor();
35488         this.el.setStyle("z-index", "");
35489         if(this.collapseBtn){
35490             this.collapseBtn.show();
35491         }
35492         this.closeBtn.setStyle('display', this.closeBtnState);
35493         if(this.stickBtn){
35494             this.stickBtn.hide();
35495         }
35496         this.fireEvent("slidehide", this);
35497     },
35498
35499     slideIn : function(cb){
35500         if(!this.isSlid || this.el.hasActiveFx()){
35501             Roo.callback(cb);
35502             return;
35503         }
35504         this.isSlid = false;
35505         this.beforeSlide();
35506         this.el.slideOut(this.getSlideAnchor(), {
35507             callback: function(){
35508                 this.el.setLeftTop(-10000, -10000);
35509                 this.afterSlide();
35510                 this.afterSlideIn();
35511                 Roo.callback(cb);
35512             },
35513             scope: this,
35514             block: true
35515         });
35516     },
35517     
35518     slideInIf : function(e){
35519         if(!e.within(this.el)){
35520             this.slideIn();
35521         }
35522     },
35523
35524     animateCollapse : function(){
35525         this.beforeSlide();
35526         this.el.setStyle("z-index", 20000);
35527         var anchor = this.getSlideAnchor();
35528         this.el.slideOut(anchor, {
35529             callback : function(){
35530                 this.el.setStyle("z-index", "");
35531                 this.collapsedEl.slideIn(anchor, {duration:.3});
35532                 this.afterSlide();
35533                 this.el.setLocation(-10000,-10000);
35534                 this.el.hide();
35535                 this.fireEvent("collapsed", this);
35536             },
35537             scope: this,
35538             block: true
35539         });
35540     },
35541
35542     animateExpand : function(){
35543         this.beforeSlide();
35544         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35545         this.el.setStyle("z-index", 20000);
35546         this.collapsedEl.hide({
35547             duration:.1
35548         });
35549         this.el.slideIn(this.getSlideAnchor(), {
35550             callback : function(){
35551                 this.el.setStyle("z-index", "");
35552                 this.afterSlide();
35553                 if(this.split){
35554                     this.split.el.show();
35555                 }
35556                 this.fireEvent("invalidated", this);
35557                 this.fireEvent("expanded", this);
35558             },
35559             scope: this,
35560             block: true
35561         });
35562     },
35563
35564     anchors : {
35565         "west" : "left",
35566         "east" : "right",
35567         "north" : "top",
35568         "south" : "bottom"
35569     },
35570
35571     sanchors : {
35572         "west" : "l",
35573         "east" : "r",
35574         "north" : "t",
35575         "south" : "b"
35576     },
35577
35578     canchors : {
35579         "west" : "tl-tr",
35580         "east" : "tr-tl",
35581         "north" : "tl-bl",
35582         "south" : "bl-tl"
35583     },
35584
35585     getAnchor : function(){
35586         return this.anchors[this.position];
35587     },
35588
35589     getCollapseAnchor : function(){
35590         return this.canchors[this.position];
35591     },
35592
35593     getSlideAnchor : function(){
35594         return this.sanchors[this.position];
35595     },
35596
35597     getAlignAdj : function(){
35598         var cm = this.cmargins;
35599         switch(this.position){
35600             case "west":
35601                 return [0, 0];
35602             break;
35603             case "east":
35604                 return [0, 0];
35605             break;
35606             case "north":
35607                 return [0, 0];
35608             break;
35609             case "south":
35610                 return [0, 0];
35611             break;
35612         }
35613     },
35614
35615     getExpandAdj : function(){
35616         var c = this.collapsedEl, cm = this.cmargins;
35617         switch(this.position){
35618             case "west":
35619                 return [-(cm.right+c.getWidth()+cm.left), 0];
35620             break;
35621             case "east":
35622                 return [cm.right+c.getWidth()+cm.left, 0];
35623             break;
35624             case "north":
35625                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35626             break;
35627             case "south":
35628                 return [0, cm.top+cm.bottom+c.getHeight()];
35629             break;
35630         }
35631     }
35632 });/*
35633  * Based on:
35634  * Ext JS Library 1.1.1
35635  * Copyright(c) 2006-2007, Ext JS, LLC.
35636  *
35637  * Originally Released Under LGPL - original licence link has changed is not relivant.
35638  *
35639  * Fork - LGPL
35640  * <script type="text/javascript">
35641  */
35642 /*
35643  * These classes are private internal classes
35644  */
35645 Roo.CenterLayoutRegion = function(mgr, config){
35646     Roo.LayoutRegion.call(this, mgr, config, "center");
35647     this.visible = true;
35648     this.minWidth = config.minWidth || 20;
35649     this.minHeight = config.minHeight || 20;
35650 };
35651
35652 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35653     hide : function(){
35654         // center panel can't be hidden
35655     },
35656     
35657     show : function(){
35658         // center panel can't be hidden
35659     },
35660     
35661     getMinWidth: function(){
35662         return this.minWidth;
35663     },
35664     
35665     getMinHeight: function(){
35666         return this.minHeight;
35667     }
35668 });
35669
35670
35671 Roo.NorthLayoutRegion = function(mgr, config){
35672     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35673     if(this.split){
35674         this.split.placement = Roo.SplitBar.TOP;
35675         this.split.orientation = Roo.SplitBar.VERTICAL;
35676         this.split.el.addClass("x-layout-split-v");
35677     }
35678     var size = config.initialSize || config.height;
35679     if(typeof size != "undefined"){
35680         this.el.setHeight(size);
35681     }
35682 };
35683 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35684     orientation: Roo.SplitBar.VERTICAL,
35685     getBox : function(){
35686         if(this.collapsed){
35687             return this.collapsedEl.getBox();
35688         }
35689         var box = this.el.getBox();
35690         if(this.split){
35691             box.height += this.split.el.getHeight();
35692         }
35693         return box;
35694     },
35695     
35696     updateBox : function(box){
35697         if(this.split && !this.collapsed){
35698             box.height -= this.split.el.getHeight();
35699             this.split.el.setLeft(box.x);
35700             this.split.el.setTop(box.y+box.height);
35701             this.split.el.setWidth(box.width);
35702         }
35703         if(this.collapsed){
35704             this.updateBody(box.width, null);
35705         }
35706         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35707     }
35708 });
35709
35710 Roo.SouthLayoutRegion = function(mgr, config){
35711     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35712     if(this.split){
35713         this.split.placement = Roo.SplitBar.BOTTOM;
35714         this.split.orientation = Roo.SplitBar.VERTICAL;
35715         this.split.el.addClass("x-layout-split-v");
35716     }
35717     var size = config.initialSize || config.height;
35718     if(typeof size != "undefined"){
35719         this.el.setHeight(size);
35720     }
35721 };
35722 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35723     orientation: Roo.SplitBar.VERTICAL,
35724     getBox : function(){
35725         if(this.collapsed){
35726             return this.collapsedEl.getBox();
35727         }
35728         var box = this.el.getBox();
35729         if(this.split){
35730             var sh = this.split.el.getHeight();
35731             box.height += sh;
35732             box.y -= sh;
35733         }
35734         return box;
35735     },
35736     
35737     updateBox : function(box){
35738         if(this.split && !this.collapsed){
35739             var sh = this.split.el.getHeight();
35740             box.height -= sh;
35741             box.y += sh;
35742             this.split.el.setLeft(box.x);
35743             this.split.el.setTop(box.y-sh);
35744             this.split.el.setWidth(box.width);
35745         }
35746         if(this.collapsed){
35747             this.updateBody(box.width, null);
35748         }
35749         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35750     }
35751 });
35752
35753 Roo.EastLayoutRegion = function(mgr, config){
35754     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35755     if(this.split){
35756         this.split.placement = Roo.SplitBar.RIGHT;
35757         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35758         this.split.el.addClass("x-layout-split-h");
35759     }
35760     var size = config.initialSize || config.width;
35761     if(typeof size != "undefined"){
35762         this.el.setWidth(size);
35763     }
35764 };
35765 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35766     orientation: Roo.SplitBar.HORIZONTAL,
35767     getBox : function(){
35768         if(this.collapsed){
35769             return this.collapsedEl.getBox();
35770         }
35771         var box = this.el.getBox();
35772         if(this.split){
35773             var sw = this.split.el.getWidth();
35774             box.width += sw;
35775             box.x -= sw;
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);
35785             this.split.el.setTop(box.y);
35786             this.split.el.setHeight(box.height);
35787             box.x += sw;
35788         }
35789         if(this.collapsed){
35790             this.updateBody(null, box.height);
35791         }
35792         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35793     }
35794 });
35795
35796 Roo.WestLayoutRegion = function(mgr, config){
35797     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35798     if(this.split){
35799         this.split.placement = Roo.SplitBar.LEFT;
35800         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35801         this.split.el.addClass("x-layout-split-h");
35802     }
35803     var size = config.initialSize || config.width;
35804     if(typeof size != "undefined"){
35805         this.el.setWidth(size);
35806     }
35807 };
35808 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35809     orientation: Roo.SplitBar.HORIZONTAL,
35810     getBox : function(){
35811         if(this.collapsed){
35812             return this.collapsedEl.getBox();
35813         }
35814         var box = this.el.getBox();
35815         if(this.split){
35816             box.width += this.split.el.getWidth();
35817         }
35818         return box;
35819     },
35820     
35821     updateBox : function(box){
35822         if(this.split && !this.collapsed){
35823             var sw = this.split.el.getWidth();
35824             box.width -= sw;
35825             this.split.el.setLeft(box.x+box.width);
35826             this.split.el.setTop(box.y);
35827             this.split.el.setHeight(box.height);
35828         }
35829         if(this.collapsed){
35830             this.updateBody(null, box.height);
35831         }
35832         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35833     }
35834 });
35835 /*
35836  * Based on:
35837  * Ext JS Library 1.1.1
35838  * Copyright(c) 2006-2007, Ext JS, LLC.
35839  *
35840  * Originally Released Under LGPL - original licence link has changed is not relivant.
35841  *
35842  * Fork - LGPL
35843  * <script type="text/javascript">
35844  */
35845  
35846  
35847 /*
35848  * Private internal class for reading and applying state
35849  */
35850 Roo.LayoutStateManager = function(layout){
35851      // default empty state
35852      this.state = {
35853         north: {},
35854         south: {},
35855         east: {},
35856         west: {}       
35857     };
35858 };
35859
35860 Roo.LayoutStateManager.prototype = {
35861     init : function(layout, provider){
35862         this.provider = provider;
35863         var state = provider.get(layout.id+"-layout-state");
35864         if(state){
35865             var wasUpdating = layout.isUpdating();
35866             if(!wasUpdating){
35867                 layout.beginUpdate();
35868             }
35869             for(var key in state){
35870                 if(typeof state[key] != "function"){
35871                     var rstate = state[key];
35872                     var r = layout.getRegion(key);
35873                     if(r && rstate){
35874                         if(rstate.size){
35875                             r.resizeTo(rstate.size);
35876                         }
35877                         if(rstate.collapsed == true){
35878                             r.collapse(true);
35879                         }else{
35880                             r.expand(null, true);
35881                         }
35882                     }
35883                 }
35884             }
35885             if(!wasUpdating){
35886                 layout.endUpdate();
35887             }
35888             this.state = state; 
35889         }
35890         this.layout = layout;
35891         layout.on("regionresized", this.onRegionResized, this);
35892         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35893         layout.on("regionexpanded", this.onRegionExpanded, this);
35894     },
35895     
35896     storeState : function(){
35897         this.provider.set(this.layout.id+"-layout-state", this.state);
35898     },
35899     
35900     onRegionResized : function(region, newSize){
35901         this.state[region.getPosition()].size = newSize;
35902         this.storeState();
35903     },
35904     
35905     onRegionCollapsed : function(region){
35906         this.state[region.getPosition()].collapsed = true;
35907         this.storeState();
35908     },
35909     
35910     onRegionExpanded : function(region){
35911         this.state[region.getPosition()].collapsed = false;
35912         this.storeState();
35913     }
35914 };/*
35915  * Based on:
35916  * Ext JS Library 1.1.1
35917  * Copyright(c) 2006-2007, Ext JS, LLC.
35918  *
35919  * Originally Released Under LGPL - original licence link has changed is not relivant.
35920  *
35921  * Fork - LGPL
35922  * <script type="text/javascript">
35923  */
35924 /**
35925  * @class Roo.ContentPanel
35926  * @extends Roo.util.Observable
35927  * @children Roo.form.Form Roo.JsonView Roo.View
35928  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35929  * A basic ContentPanel element.
35930  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35931  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35932  * @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
35933  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35934  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35935  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35936  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35937  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35938  * @cfg {String} title          The title for this panel
35939  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35940  * @cfg {String} url            Calls {@link #setUrl} with this value
35941  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35942  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35943  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35944  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35945  * @cfg {String}    style  Extra style to add to the content panel
35946  * @cfg {Roo.menu.Menu} menu  popup menu
35947
35948  * @constructor
35949  * Create a new ContentPanel.
35950  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35951  * @param {String/Object} config A string to set only the title or a config object
35952  * @param {String} content (optional) Set the HTML content for this panel
35953  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35954  */
35955 Roo.ContentPanel = function(el, config, content){
35956     
35957      
35958     /*
35959     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35960         config = el;
35961         el = Roo.id();
35962     }
35963     if (config && config.parentLayout) { 
35964         el = config.parentLayout.el.createChild(); 
35965     }
35966     */
35967     if(el.autoCreate){ // xtype is available if this is called from factory
35968         config = el;
35969         el = Roo.id();
35970     }
35971     this.el = Roo.get(el);
35972     if(!this.el && config && config.autoCreate){
35973         if(typeof config.autoCreate == "object"){
35974             if(!config.autoCreate.id){
35975                 config.autoCreate.id = config.id||el;
35976             }
35977             this.el = Roo.DomHelper.append(document.body,
35978                         config.autoCreate, true);
35979         }else{
35980             this.el = Roo.DomHelper.append(document.body,
35981                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35982         }
35983     }
35984     
35985     
35986     this.closable = false;
35987     this.loaded = false;
35988     this.active = false;
35989     if(typeof config == "string"){
35990         this.title = config;
35991     }else{
35992         Roo.apply(this, config);
35993     }
35994     
35995     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35996         this.wrapEl = this.el.wrap();
35997         this.toolbar.container = this.el.insertSibling(false, 'before');
35998         this.toolbar = new Roo.Toolbar(this.toolbar);
35999     }
36000     
36001     // xtype created footer. - not sure if will work as we normally have to render first..
36002     if (this.footer && !this.footer.el && this.footer.xtype) {
36003         if (!this.wrapEl) {
36004             this.wrapEl = this.el.wrap();
36005         }
36006     
36007         this.footer.container = this.wrapEl.createChild();
36008          
36009         this.footer = Roo.factory(this.footer, Roo);
36010         
36011     }
36012     
36013     if(this.resizeEl){
36014         this.resizeEl = Roo.get(this.resizeEl, true);
36015     }else{
36016         this.resizeEl = this.el;
36017     }
36018     // handle view.xtype
36019     
36020  
36021     
36022     
36023     this.addEvents({
36024         /**
36025          * @event activate
36026          * Fires when this panel is activated. 
36027          * @param {Roo.ContentPanel} this
36028          */
36029         "activate" : true,
36030         /**
36031          * @event deactivate
36032          * Fires when this panel is activated. 
36033          * @param {Roo.ContentPanel} this
36034          */
36035         "deactivate" : true,
36036
36037         /**
36038          * @event resize
36039          * Fires when this panel is resized if fitToFrame is true.
36040          * @param {Roo.ContentPanel} this
36041          * @param {Number} width The width after any component adjustments
36042          * @param {Number} height The height after any component adjustments
36043          */
36044         "resize" : true,
36045         
36046          /**
36047          * @event render
36048          * Fires when this tab is created
36049          * @param {Roo.ContentPanel} this
36050          */
36051         "render" : true
36052          
36053         
36054     });
36055     
36056
36057     
36058     
36059     if(this.autoScroll){
36060         this.resizeEl.setStyle("overflow", "auto");
36061     } else {
36062         // fix randome scrolling
36063         this.el.on('scroll', function() {
36064             Roo.log('fix random scolling');
36065             this.scrollTo('top',0); 
36066         });
36067     }
36068     content = content || this.content;
36069     if(content){
36070         this.setContent(content);
36071     }
36072     if(config && config.url){
36073         this.setUrl(this.url, this.params, this.loadOnce);
36074     }
36075     
36076     
36077     
36078     Roo.ContentPanel.superclass.constructor.call(this);
36079     
36080     if (this.view && typeof(this.view.xtype) != 'undefined') {
36081         this.view.el = this.el.appendChild(document.createElement("div"));
36082         this.view = Roo.factory(this.view); 
36083         this.view.render  &&  this.view.render(false, '');  
36084     }
36085     
36086     
36087     this.fireEvent('render', this);
36088 };
36089
36090 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
36091     tabTip:'',
36092     setRegion : function(region){
36093         this.region = region;
36094         if(region){
36095            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
36096         }else{
36097            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
36098         } 
36099     },
36100     
36101     /**
36102      * Returns the toolbar for this Panel if one was configured. 
36103      * @return {Roo.Toolbar} 
36104      */
36105     getToolbar : function(){
36106         return this.toolbar;
36107     },
36108     
36109     setActiveState : function(active){
36110         this.active = active;
36111         if(!active){
36112             this.fireEvent("deactivate", this);
36113         }else{
36114             this.fireEvent("activate", this);
36115         }
36116     },
36117     /**
36118      * Updates this panel's element
36119      * @param {String} content The new content
36120      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36121     */
36122     setContent : function(content, loadScripts){
36123         this.el.update(content, loadScripts);
36124     },
36125
36126     ignoreResize : function(w, h){
36127         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36128             return true;
36129         }else{
36130             this.lastSize = {width: w, height: h};
36131             return false;
36132         }
36133     },
36134     /**
36135      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36136      * @return {Roo.UpdateManager} The UpdateManager
36137      */
36138     getUpdateManager : function(){
36139         return this.el.getUpdateManager();
36140     },
36141      /**
36142      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36143      * @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:
36144 <pre><code>
36145 panel.load({
36146     url: "your-url.php",
36147     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36148     callback: yourFunction,
36149     scope: yourObject, //(optional scope)
36150     discardUrl: false,
36151     nocache: false,
36152     text: "Loading...",
36153     timeout: 30,
36154     scripts: false
36155 });
36156 </code></pre>
36157      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36158      * 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.
36159      * @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}
36160      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36161      * @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.
36162      * @return {Roo.ContentPanel} this
36163      */
36164     load : function(){
36165         var um = this.el.getUpdateManager();
36166         um.update.apply(um, arguments);
36167         return this;
36168     },
36169
36170
36171     /**
36172      * 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.
36173      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36174      * @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)
36175      * @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)
36176      * @return {Roo.UpdateManager} The UpdateManager
36177      */
36178     setUrl : function(url, params, loadOnce){
36179         if(this.refreshDelegate){
36180             this.removeListener("activate", this.refreshDelegate);
36181         }
36182         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36183         this.on("activate", this.refreshDelegate);
36184         return this.el.getUpdateManager();
36185     },
36186     
36187     _handleRefresh : function(url, params, loadOnce){
36188         if(!loadOnce || !this.loaded){
36189             var updater = this.el.getUpdateManager();
36190             updater.update(url, params, this._setLoaded.createDelegate(this));
36191         }
36192     },
36193     
36194     _setLoaded : function(){
36195         this.loaded = true;
36196     }, 
36197     
36198     /**
36199      * Returns this panel's id
36200      * @return {String} 
36201      */
36202     getId : function(){
36203         return this.el.id;
36204     },
36205     
36206     /** 
36207      * Returns this panel's element - used by regiosn to add.
36208      * @return {Roo.Element} 
36209      */
36210     getEl : function(){
36211         return this.wrapEl || this.el;
36212     },
36213     
36214     adjustForComponents : function(width, height)
36215     {
36216         //Roo.log('adjustForComponents ');
36217         if(this.resizeEl != this.el){
36218             width -= this.el.getFrameWidth('lr');
36219             height -= this.el.getFrameWidth('tb');
36220         }
36221         if(this.toolbar){
36222             var te = this.toolbar.getEl();
36223             height -= te.getHeight();
36224             te.setWidth(width);
36225         }
36226         if(this.footer){
36227             var te = this.footer.getEl();
36228             //Roo.log("footer:" + te.getHeight());
36229             
36230             height -= te.getHeight();
36231             te.setWidth(width);
36232         }
36233         
36234         
36235         if(this.adjustments){
36236             width += this.adjustments[0];
36237             height += this.adjustments[1];
36238         }
36239         return {"width": width, "height": height};
36240     },
36241     
36242     setSize : function(width, height){
36243         if(this.fitToFrame && !this.ignoreResize(width, height)){
36244             if(this.fitContainer && this.resizeEl != this.el){
36245                 this.el.setSize(width, height);
36246             }
36247             var size = this.adjustForComponents(width, height);
36248             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36249             this.fireEvent('resize', this, size.width, size.height);
36250         }
36251     },
36252     
36253     /**
36254      * Returns this panel's title
36255      * @return {String} 
36256      */
36257     getTitle : function(){
36258         return this.title;
36259     },
36260     
36261     /**
36262      * Set this panel's title
36263      * @param {String} title
36264      */
36265     setTitle : function(title){
36266         this.title = title;
36267         if(this.region){
36268             this.region.updatePanelTitle(this, title);
36269         }
36270     },
36271     
36272     /**
36273      * Returns true is this panel was configured to be closable
36274      * @return {Boolean} 
36275      */
36276     isClosable : function(){
36277         return this.closable;
36278     },
36279     
36280     beforeSlide : function(){
36281         this.el.clip();
36282         this.resizeEl.clip();
36283     },
36284     
36285     afterSlide : function(){
36286         this.el.unclip();
36287         this.resizeEl.unclip();
36288     },
36289     
36290     /**
36291      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36292      *   Will fail silently if the {@link #setUrl} method has not been called.
36293      *   This does not activate the panel, just updates its content.
36294      */
36295     refresh : function(){
36296         if(this.refreshDelegate){
36297            this.loaded = false;
36298            this.refreshDelegate();
36299         }
36300     },
36301     
36302     /**
36303      * Destroys this panel
36304      */
36305     destroy : function(){
36306         this.el.removeAllListeners();
36307         var tempEl = document.createElement("span");
36308         tempEl.appendChild(this.el.dom);
36309         tempEl.innerHTML = "";
36310         this.el.remove();
36311         this.el = null;
36312     },
36313     
36314     /**
36315      * form - if the content panel contains a form - this is a reference to it.
36316      * @type {Roo.form.Form}
36317      */
36318     form : false,
36319     /**
36320      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36321      *    This contains a reference to it.
36322      * @type {Roo.View}
36323      */
36324     view : false,
36325     
36326       /**
36327      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36328      * <pre><code>
36329
36330 layout.addxtype({
36331        xtype : 'Form',
36332        items: [ .... ]
36333    }
36334 );
36335
36336 </code></pre>
36337      * @param {Object} cfg Xtype definition of item to add.
36338      */
36339     
36340     addxtype : function(cfg) {
36341         // add form..
36342         if (cfg.xtype.match(/^Form$/)) {
36343             
36344             var el;
36345             //if (this.footer) {
36346             //    el = this.footer.container.insertSibling(false, 'before');
36347             //} else {
36348                 el = this.el.createChild();
36349             //}
36350
36351             this.form = new  Roo.form.Form(cfg);
36352             
36353             
36354             if ( this.form.allItems.length) {
36355                 this.form.render(el.dom);
36356             }
36357             return this.form;
36358         }
36359         // should only have one of theses..
36360         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36361             // views.. should not be just added - used named prop 'view''
36362             
36363             cfg.el = this.el.appendChild(document.createElement("div"));
36364             // factory?
36365             
36366             var ret = new Roo.factory(cfg);
36367              
36368              ret.render && ret.render(false, ''); // render blank..
36369             this.view = ret;
36370             return ret;
36371         }
36372         return false;
36373     }
36374 });
36375
36376
36377
36378
36379
36380
36381
36382
36383
36384
36385
36386
36387 /**
36388  * @class Roo.GridPanel
36389  * @extends Roo.ContentPanel
36390  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36391  * @constructor
36392  * Create a new GridPanel.
36393  * @cfg {Roo.grid.Grid} grid The grid for this panel
36394  */
36395 Roo.GridPanel = function(grid, config){
36396     
36397     // universal ctor...
36398     if (typeof(grid.grid) != 'undefined') {
36399         config = grid;
36400         grid = config.grid;
36401     }
36402     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36403         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36404         
36405     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36406     
36407     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36408     
36409     if(this.toolbar){
36410         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36411     }
36412     // xtype created footer. - not sure if will work as we normally have to render first..
36413     if (this.footer && !this.footer.el && this.footer.xtype) {
36414         
36415         this.footer.container = this.grid.getView().getFooterPanel(true);
36416         this.footer.dataSource = this.grid.dataSource;
36417         this.footer = Roo.factory(this.footer, Roo);
36418         
36419     }
36420     
36421     grid.monitorWindowResize = false; // turn off autosizing
36422     grid.autoHeight = false;
36423     grid.autoWidth = false;
36424     this.grid = grid;
36425     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36426 };
36427
36428 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36429     getId : function(){
36430         return this.grid.id;
36431     },
36432     
36433     /**
36434      * Returns the grid for this panel
36435      * @return {Roo.grid.Grid} 
36436      */
36437     getGrid : function(){
36438         return this.grid;    
36439     },
36440     
36441     setSize : function(width, height){
36442         if(!this.ignoreResize(width, height)){
36443             var grid = this.grid;
36444             var size = this.adjustForComponents(width, height);
36445             grid.getGridEl().setSize(size.width, size.height);
36446             grid.autoSize();
36447         }
36448     },
36449     
36450     beforeSlide : function(){
36451         this.grid.getView().scroller.clip();
36452     },
36453     
36454     afterSlide : function(){
36455         this.grid.getView().scroller.unclip();
36456     },
36457     
36458     destroy : function(){
36459         this.grid.destroy();
36460         delete this.grid;
36461         Roo.GridPanel.superclass.destroy.call(this); 
36462     }
36463 });
36464
36465
36466 /**
36467  * @class Roo.NestedLayoutPanel
36468  * @extends Roo.ContentPanel
36469  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36470  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
36471  *
36472  * 
36473  * @constructor
36474  * Create a new NestedLayoutPanel.
36475  * 
36476  * 
36477  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36478  * @param {String/Object} config A string to set only the title or a config object
36479  */
36480 Roo.NestedLayoutPanel = function(layout, config)
36481 {
36482     // construct with only one argument..
36483     /* FIXME - implement nicer consturctors
36484     if (layout.layout) {
36485         config = layout;
36486         layout = config.layout;
36487         delete config.layout;
36488     }
36489     if (layout.xtype && !layout.getEl) {
36490         // then layout needs constructing..
36491         layout = Roo.factory(layout, Roo);
36492     }
36493     */
36494     
36495     
36496     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36497     
36498     layout.monitorWindowResize = false; // turn off autosizing
36499     this.layout = layout;
36500     this.layout.getEl().addClass("x-layout-nested-layout");
36501     
36502     
36503     
36504     
36505 };
36506
36507 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36508
36509     layout : false,
36510
36511     setSize : function(width, height){
36512         if(!this.ignoreResize(width, height)){
36513             var size = this.adjustForComponents(width, height);
36514             var el = this.layout.getEl();
36515             el.setSize(size.width, size.height);
36516             var touch = el.dom.offsetWidth;
36517             this.layout.layout();
36518             // ie requires a double layout on the first pass
36519             if(Roo.isIE && !this.initialized){
36520                 this.initialized = true;
36521                 this.layout.layout();
36522             }
36523         }
36524     },
36525     
36526     // activate all subpanels if not currently active..
36527     
36528     setActiveState : function(active){
36529         this.active = active;
36530         if(!active){
36531             this.fireEvent("deactivate", this);
36532             return;
36533         }
36534         
36535         this.fireEvent("activate", this);
36536         // not sure if this should happen before or after..
36537         if (!this.layout) {
36538             return; // should not happen..
36539         }
36540         var reg = false;
36541         for (var r in this.layout.regions) {
36542             reg = this.layout.getRegion(r);
36543             if (reg.getActivePanel()) {
36544                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36545                 reg.setActivePanel(reg.getActivePanel());
36546                 continue;
36547             }
36548             if (!reg.panels.length) {
36549                 continue;
36550             }
36551             reg.showPanel(reg.getPanel(0));
36552         }
36553         
36554         
36555         
36556         
36557     },
36558     
36559     /**
36560      * Returns the nested BorderLayout for this panel
36561      * @return {Roo.BorderLayout}
36562      */
36563     getLayout : function(){
36564         return this.layout;
36565     },
36566     
36567      /**
36568      * Adds a xtype elements to the layout of the nested panel
36569      * <pre><code>
36570
36571 panel.addxtype({
36572        xtype : 'ContentPanel',
36573        region: 'west',
36574        items: [ .... ]
36575    }
36576 );
36577
36578 panel.addxtype({
36579         xtype : 'NestedLayoutPanel',
36580         region: 'west',
36581         layout: {
36582            center: { },
36583            west: { }   
36584         },
36585         items : [ ... list of content panels or nested layout panels.. ]
36586    }
36587 );
36588 </code></pre>
36589      * @param {Object} cfg Xtype definition of item to add.
36590      */
36591     addxtype : function(cfg) {
36592         return this.layout.addxtype(cfg);
36593     
36594     }
36595 });
36596
36597 Roo.ScrollPanel = function(el, config, content){
36598     config = config || {};
36599     config.fitToFrame = true;
36600     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36601     
36602     this.el.dom.style.overflow = "hidden";
36603     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36604     this.el.removeClass("x-layout-inactive-content");
36605     this.el.on("mousewheel", this.onWheel, this);
36606
36607     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36608     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36609     up.unselectable(); down.unselectable();
36610     up.on("click", this.scrollUp, this);
36611     down.on("click", this.scrollDown, this);
36612     up.addClassOnOver("x-scroller-btn-over");
36613     down.addClassOnOver("x-scroller-btn-over");
36614     up.addClassOnClick("x-scroller-btn-click");
36615     down.addClassOnClick("x-scroller-btn-click");
36616     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36617
36618     this.resizeEl = this.el;
36619     this.el = wrap; this.up = up; this.down = down;
36620 };
36621
36622 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36623     increment : 100,
36624     wheelIncrement : 5,
36625     scrollUp : function(){
36626         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36627     },
36628
36629     scrollDown : function(){
36630         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36631     },
36632
36633     afterScroll : function(){
36634         var el = this.resizeEl;
36635         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36636         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36637         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36638     },
36639
36640     setSize : function(){
36641         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36642         this.afterScroll();
36643     },
36644
36645     onWheel : function(e){
36646         var d = e.getWheelDelta();
36647         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36648         this.afterScroll();
36649         e.stopEvent();
36650     },
36651
36652     setContent : function(content, loadScripts){
36653         this.resizeEl.update(content, loadScripts);
36654     }
36655
36656 });
36657
36658
36659
36660 /**
36661  * @class Roo.TreePanel
36662  * @extends Roo.ContentPanel
36663  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36664  * Treepanel component
36665  * 
36666  * @constructor
36667  * Create a new TreePanel. - defaults to fit/scoll contents.
36668  * @param {String/Object} config A string to set only the panel's title, or a config object
36669  */
36670 Roo.TreePanel = function(config){
36671     var el = config.el;
36672     var tree = config.tree;
36673     delete config.tree; 
36674     delete config.el; // hopefull!
36675     
36676     // wrapper for IE7 strict & safari scroll issue
36677     
36678     var treeEl = el.createChild();
36679     config.resizeEl = treeEl;
36680     
36681     
36682     
36683     Roo.TreePanel.superclass.constructor.call(this, el, config);
36684  
36685  
36686     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36687     //console.log(tree);
36688     this.on('activate', function()
36689     {
36690         if (this.tree.rendered) {
36691             return;
36692         }
36693         //console.log('render tree');
36694         this.tree.render();
36695     });
36696     // this should not be needed.. - it's actually the 'el' that resizes?
36697     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36698     
36699     //this.on('resize',  function (cp, w, h) {
36700     //        this.tree.innerCt.setWidth(w);
36701     //        this.tree.innerCt.setHeight(h);
36702     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36703     //});
36704
36705         
36706     
36707 };
36708
36709 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36710     fitToFrame : true,
36711     autoScroll : true,
36712     /*
36713      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36714      */
36715     tree : false
36716
36717 });
36718 /*
36719  * Based on:
36720  * Ext JS Library 1.1.1
36721  * Copyright(c) 2006-2007, Ext JS, LLC.
36722  *
36723  * Originally Released Under LGPL - original licence link has changed is not relivant.
36724  *
36725  * Fork - LGPL
36726  * <script type="text/javascript">
36727  */
36728  
36729
36730 /**
36731  * @class Roo.ReaderLayout
36732  * @extends Roo.BorderLayout
36733  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36734  * center region containing two nested regions (a top one for a list view and one for item preview below),
36735  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36736  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36737  * expedites the setup of the overall layout and regions for this common application style.
36738  * Example:
36739  <pre><code>
36740 var reader = new Roo.ReaderLayout();
36741 var CP = Roo.ContentPanel;  // shortcut for adding
36742
36743 reader.beginUpdate();
36744 reader.add("north", new CP("north", "North"));
36745 reader.add("west", new CP("west", {title: "West"}));
36746 reader.add("east", new CP("east", {title: "East"}));
36747
36748 reader.regions.listView.add(new CP("listView", "List"));
36749 reader.regions.preview.add(new CP("preview", "Preview"));
36750 reader.endUpdate();
36751 </code></pre>
36752 * @constructor
36753 * Create a new ReaderLayout
36754 * @param {Object} config Configuration options
36755 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36756 * document.body if omitted)
36757 */
36758 Roo.ReaderLayout = function(config, renderTo){
36759     var c = config || {size:{}};
36760     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36761         north: c.north !== false ? Roo.apply({
36762             split:false,
36763             initialSize: 32,
36764             titlebar: false
36765         }, c.north) : false,
36766         west: c.west !== false ? Roo.apply({
36767             split:true,
36768             initialSize: 200,
36769             minSize: 175,
36770             maxSize: 400,
36771             titlebar: true,
36772             collapsible: true,
36773             animate: true,
36774             margins:{left:5,right:0,bottom:5,top:5},
36775             cmargins:{left:5,right:5,bottom:5,top:5}
36776         }, c.west) : false,
36777         east: c.east !== false ? Roo.apply({
36778             split:true,
36779             initialSize: 200,
36780             minSize: 175,
36781             maxSize: 400,
36782             titlebar: true,
36783             collapsible: true,
36784             animate: true,
36785             margins:{left:0,right:5,bottom:5,top:5},
36786             cmargins:{left:5,right:5,bottom:5,top:5}
36787         }, c.east) : false,
36788         center: Roo.apply({
36789             tabPosition: 'top',
36790             autoScroll:false,
36791             closeOnTab: true,
36792             titlebar:false,
36793             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36794         }, c.center)
36795     });
36796
36797     this.el.addClass('x-reader');
36798
36799     this.beginUpdate();
36800
36801     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36802         south: c.preview !== false ? Roo.apply({
36803             split:true,
36804             initialSize: 200,
36805             minSize: 100,
36806             autoScroll:true,
36807             collapsible:true,
36808             titlebar: true,
36809             cmargins:{top:5,left:0, right:0, bottom:0}
36810         }, c.preview) : false,
36811         center: Roo.apply({
36812             autoScroll:false,
36813             titlebar:false,
36814             minHeight:200
36815         }, c.listView)
36816     });
36817     this.add('center', new Roo.NestedLayoutPanel(inner,
36818             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36819
36820     this.endUpdate();
36821
36822     this.regions.preview = inner.getRegion('south');
36823     this.regions.listView = inner.getRegion('center');
36824 };
36825
36826 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36827  * Based on:
36828  * Ext JS Library 1.1.1
36829  * Copyright(c) 2006-2007, Ext JS, LLC.
36830  *
36831  * Originally Released Under LGPL - original licence link has changed is not relivant.
36832  *
36833  * Fork - LGPL
36834  * <script type="text/javascript">
36835  */
36836  
36837 /**
36838  * @class Roo.grid.Grid
36839  * @extends Roo.util.Observable
36840  * This class represents the primary interface of a component based grid control.
36841  * <br><br>Usage:<pre><code>
36842  var grid = new Roo.grid.Grid("my-container-id", {
36843      ds: myDataStore,
36844      cm: myColModel,
36845      selModel: mySelectionModel,
36846      autoSizeColumns: true,
36847      monitorWindowResize: false,
36848      trackMouseOver: true
36849  });
36850  // set any options
36851  grid.render();
36852  * </code></pre>
36853  * <b>Common Problems:</b><br/>
36854  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36855  * element will correct this<br/>
36856  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36857  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36858  * are unpredictable.<br/>
36859  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36860  * grid to calculate dimensions/offsets.<br/>
36861   * @constructor
36862  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36863  * The container MUST have some type of size defined for the grid to fill. The container will be
36864  * automatically set to position relative if it isn't already.
36865  * @param {Object} config A config object that sets properties on this grid.
36866  */
36867 Roo.grid.Grid = function(container, config){
36868         // initialize the container
36869         this.container = Roo.get(container);
36870         this.container.update("");
36871         this.container.setStyle("overflow", "hidden");
36872     this.container.addClass('x-grid-container');
36873
36874     this.id = this.container.id;
36875
36876     Roo.apply(this, config);
36877     // check and correct shorthanded configs
36878     if(this.ds){
36879         this.dataSource = this.ds;
36880         delete this.ds;
36881     }
36882     if(this.cm){
36883         this.colModel = this.cm;
36884         delete this.cm;
36885     }
36886     if(this.sm){
36887         this.selModel = this.sm;
36888         delete this.sm;
36889     }
36890
36891     if (this.selModel) {
36892         this.selModel = Roo.factory(this.selModel, Roo.grid);
36893         this.sm = this.selModel;
36894         this.sm.xmodule = this.xmodule || false;
36895     }
36896     if (typeof(this.colModel.config) == 'undefined') {
36897         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36898         this.cm = this.colModel;
36899         this.cm.xmodule = this.xmodule || false;
36900     }
36901     if (this.dataSource) {
36902         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36903         this.ds = this.dataSource;
36904         this.ds.xmodule = this.xmodule || false;
36905          
36906     }
36907     
36908     
36909     
36910     if(this.width){
36911         this.container.setWidth(this.width);
36912     }
36913
36914     if(this.height){
36915         this.container.setHeight(this.height);
36916     }
36917     /** @private */
36918         this.addEvents({
36919         // raw events
36920         /**
36921          * @event click
36922          * The raw click event for the entire grid.
36923          * @param {Roo.EventObject} e
36924          */
36925         "click" : true,
36926         /**
36927          * @event dblclick
36928          * The raw dblclick event for the entire grid.
36929          * @param {Roo.EventObject} e
36930          */
36931         "dblclick" : true,
36932         /**
36933          * @event contextmenu
36934          * The raw contextmenu event for the entire grid.
36935          * @param {Roo.EventObject} e
36936          */
36937         "contextmenu" : true,
36938         /**
36939          * @event mousedown
36940          * The raw mousedown event for the entire grid.
36941          * @param {Roo.EventObject} e
36942          */
36943         "mousedown" : true,
36944         /**
36945          * @event mouseup
36946          * The raw mouseup event for the entire grid.
36947          * @param {Roo.EventObject} e
36948          */
36949         "mouseup" : true,
36950         /**
36951          * @event mouseover
36952          * The raw mouseover event for the entire grid.
36953          * @param {Roo.EventObject} e
36954          */
36955         "mouseover" : true,
36956         /**
36957          * @event mouseout
36958          * The raw mouseout event for the entire grid.
36959          * @param {Roo.EventObject} e
36960          */
36961         "mouseout" : true,
36962         /**
36963          * @event keypress
36964          * The raw keypress event for the entire grid.
36965          * @param {Roo.EventObject} e
36966          */
36967         "keypress" : true,
36968         /**
36969          * @event keydown
36970          * The raw keydown event for the entire grid.
36971          * @param {Roo.EventObject} e
36972          */
36973         "keydown" : true,
36974
36975         // custom events
36976
36977         /**
36978          * @event cellclick
36979          * Fires when a cell is clicked
36980          * @param {Grid} this
36981          * @param {Number} rowIndex
36982          * @param {Number} columnIndex
36983          * @param {Roo.EventObject} e
36984          */
36985         "cellclick" : true,
36986         /**
36987          * @event celldblclick
36988          * Fires when a cell is double clicked
36989          * @param {Grid} this
36990          * @param {Number} rowIndex
36991          * @param {Number} columnIndex
36992          * @param {Roo.EventObject} e
36993          */
36994         "celldblclick" : true,
36995         /**
36996          * @event rowclick
36997          * Fires when a row is clicked
36998          * @param {Grid} this
36999          * @param {Number} rowIndex
37000          * @param {Roo.EventObject} e
37001          */
37002         "rowclick" : true,
37003         /**
37004          * @event rowdblclick
37005          * Fires when a row is double clicked
37006          * @param {Grid} this
37007          * @param {Number} rowIndex
37008          * @param {Roo.EventObject} e
37009          */
37010         "rowdblclick" : true,
37011         /**
37012          * @event headerclick
37013          * Fires when a header is clicked
37014          * @param {Grid} this
37015          * @param {Number} columnIndex
37016          * @param {Roo.EventObject} e
37017          */
37018         "headerclick" : true,
37019         /**
37020          * @event headerdblclick
37021          * Fires when a header cell is double clicked
37022          * @param {Grid} this
37023          * @param {Number} columnIndex
37024          * @param {Roo.EventObject} e
37025          */
37026         "headerdblclick" : true,
37027         /**
37028          * @event rowcontextmenu
37029          * Fires when a row is right clicked
37030          * @param {Grid} this
37031          * @param {Number} rowIndex
37032          * @param {Roo.EventObject} e
37033          */
37034         "rowcontextmenu" : true,
37035         /**
37036          * @event cellcontextmenu
37037          * Fires when a cell is right clicked
37038          * @param {Grid} this
37039          * @param {Number} rowIndex
37040          * @param {Number} cellIndex
37041          * @param {Roo.EventObject} e
37042          */
37043          "cellcontextmenu" : true,
37044         /**
37045          * @event headercontextmenu
37046          * Fires when a header is right clicked
37047          * @param {Grid} this
37048          * @param {Number} columnIndex
37049          * @param {Roo.EventObject} e
37050          */
37051         "headercontextmenu" : true,
37052         /**
37053          * @event bodyscroll
37054          * Fires when the body element is scrolled
37055          * @param {Number} scrollLeft
37056          * @param {Number} scrollTop
37057          */
37058         "bodyscroll" : true,
37059         /**
37060          * @event columnresize
37061          * Fires when the user resizes a column
37062          * @param {Number} columnIndex
37063          * @param {Number} newSize
37064          */
37065         "columnresize" : true,
37066         /**
37067          * @event columnmove
37068          * Fires when the user moves a column
37069          * @param {Number} oldIndex
37070          * @param {Number} newIndex
37071          */
37072         "columnmove" : true,
37073         /**
37074          * @event startdrag
37075          * Fires when row(s) start being dragged
37076          * @param {Grid} this
37077          * @param {Roo.GridDD} dd The drag drop object
37078          * @param {event} e The raw browser event
37079          */
37080         "startdrag" : true,
37081         /**
37082          * @event enddrag
37083          * Fires when a drag operation is complete
37084          * @param {Grid} this
37085          * @param {Roo.GridDD} dd The drag drop object
37086          * @param {event} e The raw browser event
37087          */
37088         "enddrag" : true,
37089         /**
37090          * @event dragdrop
37091          * Fires when dragged row(s) are dropped on a valid DD target
37092          * @param {Grid} this
37093          * @param {Roo.GridDD} dd The drag drop object
37094          * @param {String} targetId The target drag drop object
37095          * @param {event} e The raw browser event
37096          */
37097         "dragdrop" : true,
37098         /**
37099          * @event dragover
37100          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37101          * @param {Grid} this
37102          * @param {Roo.GridDD} dd The drag drop object
37103          * @param {String} targetId The target drag drop object
37104          * @param {event} e The raw browser event
37105          */
37106         "dragover" : true,
37107         /**
37108          * @event dragenter
37109          *  Fires when the dragged row(s) first cross another DD target while being dragged
37110          * @param {Grid} this
37111          * @param {Roo.GridDD} dd The drag drop object
37112          * @param {String} targetId The target drag drop object
37113          * @param {event} e The raw browser event
37114          */
37115         "dragenter" : true,
37116         /**
37117          * @event dragout
37118          * Fires when the dragged row(s) leave another DD target while being dragged
37119          * @param {Grid} this
37120          * @param {Roo.GridDD} dd The drag drop object
37121          * @param {String} targetId The target drag drop object
37122          * @param {event} e The raw browser event
37123          */
37124         "dragout" : true,
37125         /**
37126          * @event rowclass
37127          * Fires when a row is rendered, so you can change add a style to it.
37128          * @param {GridView} gridview   The grid view
37129          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37130          */
37131         'rowclass' : true,
37132
37133         /**
37134          * @event render
37135          * Fires when the grid is rendered
37136          * @param {Grid} grid
37137          */
37138         'render' : true
37139     });
37140
37141     Roo.grid.Grid.superclass.constructor.call(this);
37142 };
37143 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
37144     
37145     /**
37146          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
37147          */
37148         /**
37149          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
37150          */
37151         /**
37152          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
37153          */
37154         /**
37155          * @cfg {Roo.data.Store} ds The data store for the grid
37156          */
37157         /**
37158          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
37159          */
37160         /**
37161      * @cfg {String} ddGroup - drag drop group.
37162      */
37163       /**
37164      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
37165      */
37166
37167     /**
37168      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
37169      */
37170     minColumnWidth : 25,
37171
37172     /**
37173      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
37174      * <b>on initial render.</b> It is more efficient to explicitly size the columns
37175      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
37176      */
37177     autoSizeColumns : false,
37178
37179     /**
37180      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
37181      */
37182     autoSizeHeaders : true,
37183
37184     /**
37185      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
37186      */
37187     monitorWindowResize : true,
37188
37189     /**
37190      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
37191      * rows measured to get a columns size. Default is 0 (all rows).
37192      */
37193     maxRowsToMeasure : 0,
37194
37195     /**
37196      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
37197      */
37198     trackMouseOver : true,
37199
37200     /**
37201     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
37202     */
37203       /**
37204     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
37205     */
37206     
37207     /**
37208     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37209     */
37210     enableDragDrop : false,
37211     
37212     /**
37213     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37214     */
37215     enableColumnMove : true,
37216     
37217     /**
37218     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37219     */
37220     enableColumnHide : true,
37221     
37222     /**
37223     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37224     */
37225     enableRowHeightSync : false,
37226     
37227     /**
37228     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
37229     */
37230     stripeRows : true,
37231     
37232     /**
37233     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37234     */
37235     autoHeight : false,
37236
37237     /**
37238      * @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.
37239      */
37240     autoExpandColumn : false,
37241
37242     /**
37243     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37244     * Default is 50.
37245     */
37246     autoExpandMin : 50,
37247
37248     /**
37249     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37250     */
37251     autoExpandMax : 1000,
37252
37253     /**
37254     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37255     */
37256     view : null,
37257
37258     /**
37259     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37260     */
37261     loadMask : false,
37262     /**
37263     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37264     */
37265     dropTarget: false,
37266      /**
37267     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37268     */ 
37269     sortColMenu : false,
37270     
37271     // private
37272     rendered : false,
37273
37274     /**
37275     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37276     * of a fixed width. Default is false.
37277     */
37278     /**
37279     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37280     */
37281     
37282     
37283     /**
37284     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37285     * %0 is replaced with the number of selected rows.
37286     */
37287     ddText : "{0} selected row{1}",
37288     
37289     
37290     /**
37291      * Called once after all setup has been completed and the grid is ready to be rendered.
37292      * @return {Roo.grid.Grid} this
37293      */
37294     render : function()
37295     {
37296         var c = this.container;
37297         // try to detect autoHeight/width mode
37298         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37299             this.autoHeight = true;
37300         }
37301         var view = this.getView();
37302         view.init(this);
37303
37304         c.on("click", this.onClick, this);
37305         c.on("dblclick", this.onDblClick, this);
37306         c.on("contextmenu", this.onContextMenu, this);
37307         c.on("keydown", this.onKeyDown, this);
37308         if (Roo.isTouch) {
37309             c.on("touchstart", this.onTouchStart, this);
37310         }
37311
37312         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37313
37314         this.getSelectionModel().init(this);
37315
37316         view.render();
37317
37318         if(this.loadMask){
37319             this.loadMask = new Roo.LoadMask(this.container,
37320                     Roo.apply({store:this.dataSource}, this.loadMask));
37321         }
37322         
37323         
37324         if (this.toolbar && this.toolbar.xtype) {
37325             this.toolbar.container = this.getView().getHeaderPanel(true);
37326             this.toolbar = new Roo.Toolbar(this.toolbar);
37327         }
37328         if (this.footer && this.footer.xtype) {
37329             this.footer.dataSource = this.getDataSource();
37330             this.footer.container = this.getView().getFooterPanel(true);
37331             this.footer = Roo.factory(this.footer, Roo);
37332         }
37333         if (this.dropTarget && this.dropTarget.xtype) {
37334             delete this.dropTarget.xtype;
37335             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37336         }
37337         
37338         
37339         this.rendered = true;
37340         this.fireEvent('render', this);
37341         return this;
37342     },
37343
37344     /**
37345      * Reconfigures the grid to use a different Store and Column Model.
37346      * The View will be bound to the new objects and refreshed.
37347      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37348      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37349      */
37350     reconfigure : function(dataSource, colModel){
37351         if(this.loadMask){
37352             this.loadMask.destroy();
37353             this.loadMask = new Roo.LoadMask(this.container,
37354                     Roo.apply({store:dataSource}, this.loadMask));
37355         }
37356         this.view.bind(dataSource, colModel);
37357         this.dataSource = dataSource;
37358         this.colModel = colModel;
37359         this.view.refresh(true);
37360     },
37361     /**
37362      * addColumns
37363      * Add's a column, default at the end..
37364      
37365      * @param {int} position to add (default end)
37366      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37367      */
37368     addColumns : function(pos, ar)
37369     {
37370         
37371         for (var i =0;i< ar.length;i++) {
37372             var cfg = ar[i];
37373             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37374             this.cm.lookup[cfg.id] = cfg;
37375         }
37376         
37377         
37378         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37379             pos = this.cm.config.length; //this.cm.config.push(cfg);
37380         } 
37381         pos = Math.max(0,pos);
37382         ar.unshift(0);
37383         ar.unshift(pos);
37384         this.cm.config.splice.apply(this.cm.config, ar);
37385         
37386         
37387         
37388         this.view.generateRules(this.cm);
37389         this.view.refresh(true);
37390         
37391     },
37392     
37393     
37394     
37395     
37396     // private
37397     onKeyDown : function(e){
37398         this.fireEvent("keydown", e);
37399     },
37400
37401     /**
37402      * Destroy this grid.
37403      * @param {Boolean} removeEl True to remove the element
37404      */
37405     destroy : function(removeEl, keepListeners){
37406         if(this.loadMask){
37407             this.loadMask.destroy();
37408         }
37409         var c = this.container;
37410         c.removeAllListeners();
37411         this.view.destroy();
37412         this.colModel.purgeListeners();
37413         if(!keepListeners){
37414             this.purgeListeners();
37415         }
37416         c.update("");
37417         if(removeEl === true){
37418             c.remove();
37419         }
37420     },
37421
37422     // private
37423     processEvent : function(name, e){
37424         // does this fire select???
37425         //Roo.log('grid:processEvent '  + name);
37426         
37427         if (name != 'touchstart' ) {
37428             this.fireEvent(name, e);    
37429         }
37430         
37431         var t = e.getTarget();
37432         var v = this.view;
37433         var header = v.findHeaderIndex(t);
37434         if(header !== false){
37435             var ename = name == 'touchstart' ? 'click' : name;
37436              
37437             this.fireEvent("header" + ename, this, header, e);
37438         }else{
37439             var row = v.findRowIndex(t);
37440             var cell = v.findCellIndex(t);
37441             if (name == 'touchstart') {
37442                 // first touch is always a click.
37443                 // hopefull this happens after selection is updated.?
37444                 name = false;
37445                 
37446                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37447                     var cs = this.selModel.getSelectedCell();
37448                     if (row == cs[0] && cell == cs[1]){
37449                         name = 'dblclick';
37450                     }
37451                 }
37452                 if (typeof(this.selModel.getSelections) != 'undefined') {
37453                     var cs = this.selModel.getSelections();
37454                     var ds = this.dataSource;
37455                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37456                         name = 'dblclick';
37457                     }
37458                 }
37459                 if (!name) {
37460                     return;
37461                 }
37462             }
37463             
37464             
37465             if(row !== false){
37466                 this.fireEvent("row" + name, this, row, e);
37467                 if(cell !== false){
37468                     this.fireEvent("cell" + name, this, row, cell, e);
37469                 }
37470             }
37471         }
37472     },
37473
37474     // private
37475     onClick : function(e){
37476         this.processEvent("click", e);
37477     },
37478    // private
37479     onTouchStart : function(e){
37480         this.processEvent("touchstart", e);
37481     },
37482
37483     // private
37484     onContextMenu : function(e, t){
37485         this.processEvent("contextmenu", e);
37486     },
37487
37488     // private
37489     onDblClick : function(e){
37490         this.processEvent("dblclick", e);
37491     },
37492
37493     // private
37494     walkCells : function(row, col, step, fn, scope){
37495         var cm = this.colModel, clen = cm.getColumnCount();
37496         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37497         if(step < 0){
37498             if(col < 0){
37499                 row--;
37500                 first = false;
37501             }
37502             while(row >= 0){
37503                 if(!first){
37504                     col = clen-1;
37505                 }
37506                 first = false;
37507                 while(col >= 0){
37508                     if(fn.call(scope || this, row, col, cm) === true){
37509                         return [row, col];
37510                     }
37511                     col--;
37512                 }
37513                 row--;
37514             }
37515         } else {
37516             if(col >= clen){
37517                 row++;
37518                 first = false;
37519             }
37520             while(row < rlen){
37521                 if(!first){
37522                     col = 0;
37523                 }
37524                 first = false;
37525                 while(col < clen){
37526                     if(fn.call(scope || this, row, col, cm) === true){
37527                         return [row, col];
37528                     }
37529                     col++;
37530                 }
37531                 row++;
37532             }
37533         }
37534         return null;
37535     },
37536
37537     // private
37538     getSelections : function(){
37539         return this.selModel.getSelections();
37540     },
37541
37542     /**
37543      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37544      * but if manual update is required this method will initiate it.
37545      */
37546     autoSize : function(){
37547         if(this.rendered){
37548             this.view.layout();
37549             if(this.view.adjustForScroll){
37550                 this.view.adjustForScroll();
37551             }
37552         }
37553     },
37554
37555     /**
37556      * Returns the grid's underlying element.
37557      * @return {Element} The element
37558      */
37559     getGridEl : function(){
37560         return this.container;
37561     },
37562
37563     // private for compatibility, overridden by editor grid
37564     stopEditing : function(){},
37565
37566     /**
37567      * Returns the grid's SelectionModel.
37568      * @return {SelectionModel}
37569      */
37570     getSelectionModel : function(){
37571         if(!this.selModel){
37572             this.selModel = new Roo.grid.RowSelectionModel();
37573         }
37574         return this.selModel;
37575     },
37576
37577     /**
37578      * Returns the grid's DataSource.
37579      * @return {DataSource}
37580      */
37581     getDataSource : function(){
37582         return this.dataSource;
37583     },
37584
37585     /**
37586      * Returns the grid's ColumnModel.
37587      * @return {ColumnModel}
37588      */
37589     getColumnModel : function(){
37590         return this.colModel;
37591     },
37592
37593     /**
37594      * Returns the grid's GridView object.
37595      * @return {GridView}
37596      */
37597     getView : function(){
37598         if(!this.view){
37599             this.view = new Roo.grid.GridView(this.viewConfig);
37600             this.relayEvents(this.view, [
37601                 "beforerowremoved", "beforerowsinserted",
37602                 "beforerefresh", "rowremoved",
37603                 "rowsinserted", "rowupdated" ,"refresh"
37604             ]);
37605         }
37606         return this.view;
37607     },
37608     /**
37609      * Called to get grid's drag proxy text, by default returns this.ddText.
37610      * Override this to put something different in the dragged text.
37611      * @return {String}
37612      */
37613     getDragDropText : function(){
37614         var count = this.selModel.getCount();
37615         return String.format(this.ddText, count, count == 1 ? '' : 's');
37616     }
37617 });
37618 /*
37619  * Based on:
37620  * Ext JS Library 1.1.1
37621  * Copyright(c) 2006-2007, Ext JS, LLC.
37622  *
37623  * Originally Released Under LGPL - original licence link has changed is not relivant.
37624  *
37625  * Fork - LGPL
37626  * <script type="text/javascript">
37627  */
37628  /**
37629  * @class Roo.grid.AbstractGridView
37630  * @extends Roo.util.Observable
37631  * @abstract
37632  * Abstract base class for grid Views
37633  * @constructor
37634  */
37635 Roo.grid.AbstractGridView = function(){
37636         this.grid = null;
37637         
37638         this.events = {
37639             "beforerowremoved" : true,
37640             "beforerowsinserted" : true,
37641             "beforerefresh" : true,
37642             "rowremoved" : true,
37643             "rowsinserted" : true,
37644             "rowupdated" : true,
37645             "refresh" : true
37646         };
37647     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37648 };
37649
37650 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37651     rowClass : "x-grid-row",
37652     cellClass : "x-grid-cell",
37653     tdClass : "x-grid-td",
37654     hdClass : "x-grid-hd",
37655     splitClass : "x-grid-hd-split",
37656     
37657     init: function(grid){
37658         this.grid = grid;
37659                 var cid = this.grid.getGridEl().id;
37660         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37661         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37662         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37663         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37664         },
37665         
37666     getColumnRenderers : function(){
37667         var renderers = [];
37668         var cm = this.grid.colModel;
37669         var colCount = cm.getColumnCount();
37670         for(var i = 0; i < colCount; i++){
37671             renderers[i] = cm.getRenderer(i);
37672         }
37673         return renderers;
37674     },
37675     
37676     getColumnIds : function(){
37677         var ids = [];
37678         var cm = this.grid.colModel;
37679         var colCount = cm.getColumnCount();
37680         for(var i = 0; i < colCount; i++){
37681             ids[i] = cm.getColumnId(i);
37682         }
37683         return ids;
37684     },
37685     
37686     getDataIndexes : function(){
37687         if(!this.indexMap){
37688             this.indexMap = this.buildIndexMap();
37689         }
37690         return this.indexMap.colToData;
37691     },
37692     
37693     getColumnIndexByDataIndex : function(dataIndex){
37694         if(!this.indexMap){
37695             this.indexMap = this.buildIndexMap();
37696         }
37697         return this.indexMap.dataToCol[dataIndex];
37698     },
37699     
37700     /**
37701      * Set a css style for a column dynamically. 
37702      * @param {Number} colIndex The index of the column
37703      * @param {String} name The css property name
37704      * @param {String} value The css value
37705      */
37706     setCSSStyle : function(colIndex, name, value){
37707         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37708         Roo.util.CSS.updateRule(selector, name, value);
37709     },
37710     
37711     generateRules : function(cm){
37712         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37713         Roo.util.CSS.removeStyleSheet(rulesId);
37714         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37715             var cid = cm.getColumnId(i);
37716             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37717                          this.tdSelector, cid, " {\n}\n",
37718                          this.hdSelector, cid, " {\n}\n",
37719                          this.splitSelector, cid, " {\n}\n");
37720         }
37721         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37722     }
37723 });/*
37724  * Based on:
37725  * Ext JS Library 1.1.1
37726  * Copyright(c) 2006-2007, Ext JS, LLC.
37727  *
37728  * Originally Released Under LGPL - original licence link has changed is not relivant.
37729  *
37730  * Fork - LGPL
37731  * <script type="text/javascript">
37732  */
37733
37734 // private
37735 // This is a support class used internally by the Grid components
37736 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37737     this.grid = grid;
37738     this.view = grid.getView();
37739     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37740     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37741     if(hd2){
37742         this.setHandleElId(Roo.id(hd));
37743         this.setOuterHandleElId(Roo.id(hd2));
37744     }
37745     this.scroll = false;
37746 };
37747 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37748     maxDragWidth: 120,
37749     getDragData : function(e){
37750         var t = Roo.lib.Event.getTarget(e);
37751         var h = this.view.findHeaderCell(t);
37752         if(h){
37753             return {ddel: h.firstChild, header:h};
37754         }
37755         return false;
37756     },
37757
37758     onInitDrag : function(e){
37759         this.view.headersDisabled = true;
37760         var clone = this.dragData.ddel.cloneNode(true);
37761         clone.id = Roo.id();
37762         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37763         this.proxy.update(clone);
37764         return true;
37765     },
37766
37767     afterValidDrop : function(){
37768         var v = this.view;
37769         setTimeout(function(){
37770             v.headersDisabled = false;
37771         }, 50);
37772     },
37773
37774     afterInvalidDrop : function(){
37775         var v = this.view;
37776         setTimeout(function(){
37777             v.headersDisabled = false;
37778         }, 50);
37779     }
37780 });
37781 /*
37782  * Based on:
37783  * Ext JS Library 1.1.1
37784  * Copyright(c) 2006-2007, Ext JS, LLC.
37785  *
37786  * Originally Released Under LGPL - original licence link has changed is not relivant.
37787  *
37788  * Fork - LGPL
37789  * <script type="text/javascript">
37790  */
37791 // private
37792 // This is a support class used internally by the Grid components
37793 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37794     this.grid = grid;
37795     this.view = grid.getView();
37796     // split the proxies so they don't interfere with mouse events
37797     this.proxyTop = Roo.DomHelper.append(document.body, {
37798         cls:"col-move-top", html:"&#160;"
37799     }, true);
37800     this.proxyBottom = Roo.DomHelper.append(document.body, {
37801         cls:"col-move-bottom", html:"&#160;"
37802     }, true);
37803     this.proxyTop.hide = this.proxyBottom.hide = function(){
37804         this.setLeftTop(-100,-100);
37805         this.setStyle("visibility", "hidden");
37806     };
37807     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37808     // temporarily disabled
37809     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37810     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37811 };
37812 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37813     proxyOffsets : [-4, -9],
37814     fly: Roo.Element.fly,
37815
37816     getTargetFromEvent : function(e){
37817         var t = Roo.lib.Event.getTarget(e);
37818         var cindex = this.view.findCellIndex(t);
37819         if(cindex !== false){
37820             return this.view.getHeaderCell(cindex);
37821         }
37822         return null;
37823     },
37824
37825     nextVisible : function(h){
37826         var v = this.view, cm = this.grid.colModel;
37827         h = h.nextSibling;
37828         while(h){
37829             if(!cm.isHidden(v.getCellIndex(h))){
37830                 return h;
37831             }
37832             h = h.nextSibling;
37833         }
37834         return null;
37835     },
37836
37837     prevVisible : function(h){
37838         var v = this.view, cm = this.grid.colModel;
37839         h = h.prevSibling;
37840         while(h){
37841             if(!cm.isHidden(v.getCellIndex(h))){
37842                 return h;
37843             }
37844             h = h.prevSibling;
37845         }
37846         return null;
37847     },
37848
37849     positionIndicator : function(h, n, e){
37850         var x = Roo.lib.Event.getPageX(e);
37851         var r = Roo.lib.Dom.getRegion(n.firstChild);
37852         var px, pt, py = r.top + this.proxyOffsets[1];
37853         if((r.right - x) <= (r.right-r.left)/2){
37854             px = r.right+this.view.borderWidth;
37855             pt = "after";
37856         }else{
37857             px = r.left;
37858             pt = "before";
37859         }
37860         var oldIndex = this.view.getCellIndex(h);
37861         var newIndex = this.view.getCellIndex(n);
37862
37863         if(this.grid.colModel.isFixed(newIndex)){
37864             return false;
37865         }
37866
37867         var locked = this.grid.colModel.isLocked(newIndex);
37868
37869         if(pt == "after"){
37870             newIndex++;
37871         }
37872         if(oldIndex < newIndex){
37873             newIndex--;
37874         }
37875         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37876             return false;
37877         }
37878         px +=  this.proxyOffsets[0];
37879         this.proxyTop.setLeftTop(px, py);
37880         this.proxyTop.show();
37881         if(!this.bottomOffset){
37882             this.bottomOffset = this.view.mainHd.getHeight();
37883         }
37884         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37885         this.proxyBottom.show();
37886         return pt;
37887     },
37888
37889     onNodeEnter : function(n, dd, e, data){
37890         if(data.header != n){
37891             this.positionIndicator(data.header, n, e);
37892         }
37893     },
37894
37895     onNodeOver : function(n, dd, e, data){
37896         var result = false;
37897         if(data.header != n){
37898             result = this.positionIndicator(data.header, n, e);
37899         }
37900         if(!result){
37901             this.proxyTop.hide();
37902             this.proxyBottom.hide();
37903         }
37904         return result ? this.dropAllowed : this.dropNotAllowed;
37905     },
37906
37907     onNodeOut : function(n, dd, e, data){
37908         this.proxyTop.hide();
37909         this.proxyBottom.hide();
37910     },
37911
37912     onNodeDrop : function(n, dd, e, data){
37913         var h = data.header;
37914         if(h != n){
37915             var cm = this.grid.colModel;
37916             var x = Roo.lib.Event.getPageX(e);
37917             var r = Roo.lib.Dom.getRegion(n.firstChild);
37918             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37919             var oldIndex = this.view.getCellIndex(h);
37920             var newIndex = this.view.getCellIndex(n);
37921             var locked = cm.isLocked(newIndex);
37922             if(pt == "after"){
37923                 newIndex++;
37924             }
37925             if(oldIndex < newIndex){
37926                 newIndex--;
37927             }
37928             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37929                 return false;
37930             }
37931             cm.setLocked(oldIndex, locked, true);
37932             cm.moveColumn(oldIndex, newIndex);
37933             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37934             return true;
37935         }
37936         return false;
37937     }
37938 });
37939 /*
37940  * Based on:
37941  * Ext JS Library 1.1.1
37942  * Copyright(c) 2006-2007, Ext JS, LLC.
37943  *
37944  * Originally Released Under LGPL - original licence link has changed is not relivant.
37945  *
37946  * Fork - LGPL
37947  * <script type="text/javascript">
37948  */
37949   
37950 /**
37951  * @class Roo.grid.GridView
37952  * @extends Roo.util.Observable
37953  *
37954  * @constructor
37955  * @param {Object} config
37956  */
37957 Roo.grid.GridView = function(config){
37958     Roo.grid.GridView.superclass.constructor.call(this);
37959     this.el = null;
37960
37961     Roo.apply(this, config);
37962 };
37963
37964 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37965
37966     unselectable :  'unselectable="on"',
37967     unselectableCls :  'x-unselectable',
37968     
37969     
37970     rowClass : "x-grid-row",
37971
37972     cellClass : "x-grid-col",
37973
37974     tdClass : "x-grid-td",
37975
37976     hdClass : "x-grid-hd",
37977
37978     splitClass : "x-grid-split",
37979
37980     sortClasses : ["sort-asc", "sort-desc"],
37981
37982     enableMoveAnim : false,
37983
37984     hlColor: "C3DAF9",
37985
37986     dh : Roo.DomHelper,
37987
37988     fly : Roo.Element.fly,
37989
37990     css : Roo.util.CSS,
37991
37992     borderWidth: 1,
37993
37994     splitOffset: 3,
37995
37996     scrollIncrement : 22,
37997
37998     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37999
38000     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
38001
38002     bind : function(ds, cm){
38003         if(this.ds){
38004             this.ds.un("load", this.onLoad, this);
38005             this.ds.un("datachanged", this.onDataChange, this);
38006             this.ds.un("add", this.onAdd, this);
38007             this.ds.un("remove", this.onRemove, this);
38008             this.ds.un("update", this.onUpdate, this);
38009             this.ds.un("clear", this.onClear, this);
38010         }
38011         if(ds){
38012             ds.on("load", this.onLoad, this);
38013             ds.on("datachanged", this.onDataChange, this);
38014             ds.on("add", this.onAdd, this);
38015             ds.on("remove", this.onRemove, this);
38016             ds.on("update", this.onUpdate, this);
38017             ds.on("clear", this.onClear, this);
38018         }
38019         this.ds = ds;
38020
38021         if(this.cm){
38022             this.cm.un("widthchange", this.onColWidthChange, this);
38023             this.cm.un("headerchange", this.onHeaderChange, this);
38024             this.cm.un("hiddenchange", this.onHiddenChange, this);
38025             this.cm.un("columnmoved", this.onColumnMove, this);
38026             this.cm.un("columnlockchange", this.onColumnLock, this);
38027         }
38028         if(cm){
38029             this.generateRules(cm);
38030             cm.on("widthchange", this.onColWidthChange, this);
38031             cm.on("headerchange", this.onHeaderChange, this);
38032             cm.on("hiddenchange", this.onHiddenChange, this);
38033             cm.on("columnmoved", this.onColumnMove, this);
38034             cm.on("columnlockchange", this.onColumnLock, this);
38035         }
38036         this.cm = cm;
38037     },
38038
38039     init: function(grid){
38040         Roo.grid.GridView.superclass.init.call(this, grid);
38041
38042         this.bind(grid.dataSource, grid.colModel);
38043
38044         grid.on("headerclick", this.handleHeaderClick, this);
38045
38046         if(grid.trackMouseOver){
38047             grid.on("mouseover", this.onRowOver, this);
38048             grid.on("mouseout", this.onRowOut, this);
38049         }
38050         grid.cancelTextSelection = function(){};
38051         this.gridId = grid.id;
38052
38053         var tpls = this.templates || {};
38054
38055         if(!tpls.master){
38056             tpls.master = new Roo.Template(
38057                '<div class="x-grid" hidefocus="true">',
38058                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
38059                   '<div class="x-grid-topbar"></div>',
38060                   '<div class="x-grid-scroller"><div></div></div>',
38061                   '<div class="x-grid-locked">',
38062                       '<div class="x-grid-header">{lockedHeader}</div>',
38063                       '<div class="x-grid-body">{lockedBody}</div>',
38064                   "</div>",
38065                   '<div class="x-grid-viewport">',
38066                       '<div class="x-grid-header">{header}</div>',
38067                       '<div class="x-grid-body">{body}</div>',
38068                   "</div>",
38069                   '<div class="x-grid-bottombar"></div>',
38070                  
38071                   '<div class="x-grid-resize-proxy">&#160;</div>',
38072                "</div>"
38073             );
38074             tpls.master.disableformats = true;
38075         }
38076
38077         if(!tpls.header){
38078             tpls.header = new Roo.Template(
38079                '<table border="0" cellspacing="0" cellpadding="0">',
38080                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
38081                "</table>{splits}"
38082             );
38083             tpls.header.disableformats = true;
38084         }
38085         tpls.header.compile();
38086
38087         if(!tpls.hcell){
38088             tpls.hcell = new Roo.Template(
38089                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
38090                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
38091                 "</div></td>"
38092              );
38093              tpls.hcell.disableFormats = true;
38094         }
38095         tpls.hcell.compile();
38096
38097         if(!tpls.hsplit){
38098             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
38099                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
38100             tpls.hsplit.disableFormats = true;
38101         }
38102         tpls.hsplit.compile();
38103
38104         if(!tpls.body){
38105             tpls.body = new Roo.Template(
38106                '<table border="0" cellspacing="0" cellpadding="0">',
38107                "<tbody>{rows}</tbody>",
38108                "</table>"
38109             );
38110             tpls.body.disableFormats = true;
38111         }
38112         tpls.body.compile();
38113
38114         if(!tpls.row){
38115             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
38116             tpls.row.disableFormats = true;
38117         }
38118         tpls.row.compile();
38119
38120         if(!tpls.cell){
38121             tpls.cell = new Roo.Template(
38122                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
38123                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
38124                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
38125                 "</td>"
38126             );
38127             tpls.cell.disableFormats = true;
38128         }
38129         tpls.cell.compile();
38130
38131         this.templates = tpls;
38132     },
38133
38134     // remap these for backwards compat
38135     onColWidthChange : function(){
38136         this.updateColumns.apply(this, arguments);
38137     },
38138     onHeaderChange : function(){
38139         this.updateHeaders.apply(this, arguments);
38140     }, 
38141     onHiddenChange : function(){
38142         this.handleHiddenChange.apply(this, arguments);
38143     },
38144     onColumnMove : function(){
38145         this.handleColumnMove.apply(this, arguments);
38146     },
38147     onColumnLock : function(){
38148         this.handleLockChange.apply(this, arguments);
38149     },
38150
38151     onDataChange : function(){
38152         this.refresh();
38153         this.updateHeaderSortState();
38154     },
38155
38156     onClear : function(){
38157         this.refresh();
38158     },
38159
38160     onUpdate : function(ds, record){
38161         this.refreshRow(record);
38162     },
38163
38164     refreshRow : function(record){
38165         var ds = this.ds, index;
38166         if(typeof record == 'number'){
38167             index = record;
38168             record = ds.getAt(index);
38169         }else{
38170             index = ds.indexOf(record);
38171         }
38172         this.insertRows(ds, index, index, true);
38173         this.onRemove(ds, record, index+1, true);
38174         this.syncRowHeights(index, index);
38175         this.layout();
38176         this.fireEvent("rowupdated", this, index, record);
38177     },
38178
38179     onAdd : function(ds, records, index){
38180         this.insertRows(ds, index, index + (records.length-1));
38181     },
38182
38183     onRemove : function(ds, record, index, isUpdate){
38184         if(isUpdate !== true){
38185             this.fireEvent("beforerowremoved", this, index, record);
38186         }
38187         var bt = this.getBodyTable(), lt = this.getLockedTable();
38188         if(bt.rows[index]){
38189             bt.firstChild.removeChild(bt.rows[index]);
38190         }
38191         if(lt.rows[index]){
38192             lt.firstChild.removeChild(lt.rows[index]);
38193         }
38194         if(isUpdate !== true){
38195             this.stripeRows(index);
38196             this.syncRowHeights(index, index);
38197             this.layout();
38198             this.fireEvent("rowremoved", this, index, record);
38199         }
38200     },
38201
38202     onLoad : function(){
38203         this.scrollToTop();
38204     },
38205
38206     /**
38207      * Scrolls the grid to the top
38208      */
38209     scrollToTop : function(){
38210         if(this.scroller){
38211             this.scroller.dom.scrollTop = 0;
38212             this.syncScroll();
38213         }
38214     },
38215
38216     /**
38217      * Gets a panel in the header of the grid that can be used for toolbars etc.
38218      * After modifying the contents of this panel a call to grid.autoSize() may be
38219      * required to register any changes in size.
38220      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38221      * @return Roo.Element
38222      */
38223     getHeaderPanel : function(doShow){
38224         if(doShow){
38225             this.headerPanel.show();
38226         }
38227         return this.headerPanel;
38228     },
38229
38230     /**
38231      * Gets a panel in the footer of the grid that can be used for toolbars etc.
38232      * After modifying the contents of this panel a call to grid.autoSize() may be
38233      * required to register any changes in size.
38234      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38235      * @return Roo.Element
38236      */
38237     getFooterPanel : function(doShow){
38238         if(doShow){
38239             this.footerPanel.show();
38240         }
38241         return this.footerPanel;
38242     },
38243
38244     initElements : function(){
38245         var E = Roo.Element;
38246         var el = this.grid.getGridEl().dom.firstChild;
38247         var cs = el.childNodes;
38248
38249         this.el = new E(el);
38250         
38251          this.focusEl = new E(el.firstChild);
38252         this.focusEl.swallowEvent("click", true);
38253         
38254         this.headerPanel = new E(cs[1]);
38255         this.headerPanel.enableDisplayMode("block");
38256
38257         this.scroller = new E(cs[2]);
38258         this.scrollSizer = new E(this.scroller.dom.firstChild);
38259
38260         this.lockedWrap = new E(cs[3]);
38261         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38262         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38263
38264         this.mainWrap = new E(cs[4]);
38265         this.mainHd = new E(this.mainWrap.dom.firstChild);
38266         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38267
38268         this.footerPanel = new E(cs[5]);
38269         this.footerPanel.enableDisplayMode("block");
38270
38271         this.resizeProxy = new E(cs[6]);
38272
38273         this.headerSelector = String.format(
38274            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38275            this.lockedHd.id, this.mainHd.id
38276         );
38277
38278         this.splitterSelector = String.format(
38279            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38280            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38281         );
38282     },
38283     idToCssName : function(s)
38284     {
38285         return s.replace(/[^a-z0-9]+/ig, '-');
38286     },
38287
38288     getHeaderCell : function(index){
38289         return Roo.DomQuery.select(this.headerSelector)[index];
38290     },
38291
38292     getHeaderCellMeasure : function(index){
38293         return this.getHeaderCell(index).firstChild;
38294     },
38295
38296     getHeaderCellText : function(index){
38297         return this.getHeaderCell(index).firstChild.firstChild;
38298     },
38299
38300     getLockedTable : function(){
38301         return this.lockedBody.dom.firstChild;
38302     },
38303
38304     getBodyTable : function(){
38305         return this.mainBody.dom.firstChild;
38306     },
38307
38308     getLockedRow : function(index){
38309         return this.getLockedTable().rows[index];
38310     },
38311
38312     getRow : function(index){
38313         return this.getBodyTable().rows[index];
38314     },
38315
38316     getRowComposite : function(index){
38317         if(!this.rowEl){
38318             this.rowEl = new Roo.CompositeElementLite();
38319         }
38320         var els = [], lrow, mrow;
38321         if(lrow = this.getLockedRow(index)){
38322             els.push(lrow);
38323         }
38324         if(mrow = this.getRow(index)){
38325             els.push(mrow);
38326         }
38327         this.rowEl.elements = els;
38328         return this.rowEl;
38329     },
38330     /**
38331      * Gets the 'td' of the cell
38332      * 
38333      * @param {Integer} rowIndex row to select
38334      * @param {Integer} colIndex column to select
38335      * 
38336      * @return {Object} 
38337      */
38338     getCell : function(rowIndex, colIndex){
38339         var locked = this.cm.getLockedCount();
38340         var source;
38341         if(colIndex < locked){
38342             source = this.lockedBody.dom.firstChild;
38343         }else{
38344             source = this.mainBody.dom.firstChild;
38345             colIndex -= locked;
38346         }
38347         return source.rows[rowIndex].childNodes[colIndex];
38348     },
38349
38350     getCellText : function(rowIndex, colIndex){
38351         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38352     },
38353
38354     getCellBox : function(cell){
38355         var b = this.fly(cell).getBox();
38356         if(Roo.isOpera){ // opera fails to report the Y
38357             b.y = cell.offsetTop + this.mainBody.getY();
38358         }
38359         return b;
38360     },
38361
38362     getCellIndex : function(cell){
38363         var id = String(cell.className).match(this.cellRE);
38364         if(id){
38365             return parseInt(id[1], 10);
38366         }
38367         return 0;
38368     },
38369
38370     findHeaderIndex : function(n){
38371         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38372         return r ? this.getCellIndex(r) : false;
38373     },
38374
38375     findHeaderCell : function(n){
38376         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38377         return r ? r : false;
38378     },
38379
38380     findRowIndex : function(n){
38381         if(!n){
38382             return false;
38383         }
38384         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38385         return r ? r.rowIndex : false;
38386     },
38387
38388     findCellIndex : function(node){
38389         var stop = this.el.dom;
38390         while(node && node != stop){
38391             if(this.findRE.test(node.className)){
38392                 return this.getCellIndex(node);
38393             }
38394             node = node.parentNode;
38395         }
38396         return false;
38397     },
38398
38399     getColumnId : function(index){
38400         return this.cm.getColumnId(index);
38401     },
38402
38403     getSplitters : function()
38404     {
38405         if(this.splitterSelector){
38406            return Roo.DomQuery.select(this.splitterSelector);
38407         }else{
38408             return null;
38409       }
38410     },
38411
38412     getSplitter : function(index){
38413         return this.getSplitters()[index];
38414     },
38415
38416     onRowOver : function(e, t){
38417         var row;
38418         if((row = this.findRowIndex(t)) !== false){
38419             this.getRowComposite(row).addClass("x-grid-row-over");
38420         }
38421     },
38422
38423     onRowOut : function(e, t){
38424         var row;
38425         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38426             this.getRowComposite(row).removeClass("x-grid-row-over");
38427         }
38428     },
38429
38430     renderHeaders : function(){
38431         var cm = this.cm;
38432         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38433         var cb = [], lb = [], sb = [], lsb = [], p = {};
38434         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38435             p.cellId = "x-grid-hd-0-" + i;
38436             p.splitId = "x-grid-csplit-0-" + i;
38437             p.id = cm.getColumnId(i);
38438             p.value = cm.getColumnHeader(i) || "";
38439             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38440             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38441             if(!cm.isLocked(i)){
38442                 cb[cb.length] = ct.apply(p);
38443                 sb[sb.length] = st.apply(p);
38444             }else{
38445                 lb[lb.length] = ct.apply(p);
38446                 lsb[lsb.length] = st.apply(p);
38447             }
38448         }
38449         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38450                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38451     },
38452
38453     updateHeaders : function(){
38454         var html = this.renderHeaders();
38455         this.lockedHd.update(html[0]);
38456         this.mainHd.update(html[1]);
38457     },
38458
38459     /**
38460      * Focuses the specified row.
38461      * @param {Number} row The row index
38462      */
38463     focusRow : function(row)
38464     {
38465         //Roo.log('GridView.focusRow');
38466         var x = this.scroller.dom.scrollLeft;
38467         this.focusCell(row, 0, false);
38468         this.scroller.dom.scrollLeft = x;
38469     },
38470
38471     /**
38472      * Focuses the specified cell.
38473      * @param {Number} row The row index
38474      * @param {Number} col The column index
38475      * @param {Boolean} hscroll false to disable horizontal scrolling
38476      */
38477     focusCell : function(row, col, hscroll)
38478     {
38479         //Roo.log('GridView.focusCell');
38480         var el = this.ensureVisible(row, col, hscroll);
38481         this.focusEl.alignTo(el, "tl-tl");
38482         if(Roo.isGecko){
38483             this.focusEl.focus();
38484         }else{
38485             this.focusEl.focus.defer(1, this.focusEl);
38486         }
38487     },
38488
38489     /**
38490      * Scrolls the specified cell into view
38491      * @param {Number} row The row index
38492      * @param {Number} col The column index
38493      * @param {Boolean} hscroll false to disable horizontal scrolling
38494      */
38495     ensureVisible : function(row, col, hscroll)
38496     {
38497         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38498         //return null; //disable for testing.
38499         if(typeof row != "number"){
38500             row = row.rowIndex;
38501         }
38502         if(row < 0 && row >= this.ds.getCount()){
38503             return  null;
38504         }
38505         col = (col !== undefined ? col : 0);
38506         var cm = this.grid.colModel;
38507         while(cm.isHidden(col)){
38508             col++;
38509         }
38510
38511         var el = this.getCell(row, col);
38512         if(!el){
38513             return null;
38514         }
38515         var c = this.scroller.dom;
38516
38517         var ctop = parseInt(el.offsetTop, 10);
38518         var cleft = parseInt(el.offsetLeft, 10);
38519         var cbot = ctop + el.offsetHeight;
38520         var cright = cleft + el.offsetWidth;
38521         
38522         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38523         var stop = parseInt(c.scrollTop, 10);
38524         var sleft = parseInt(c.scrollLeft, 10);
38525         var sbot = stop + ch;
38526         var sright = sleft + c.clientWidth;
38527         /*
38528         Roo.log('GridView.ensureVisible:' +
38529                 ' ctop:' + ctop +
38530                 ' c.clientHeight:' + c.clientHeight +
38531                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38532                 ' stop:' + stop +
38533                 ' cbot:' + cbot +
38534                 ' sbot:' + sbot +
38535                 ' ch:' + ch  
38536                 );
38537         */
38538         if(ctop < stop){
38539             c.scrollTop = ctop;
38540             //Roo.log("set scrolltop to ctop DISABLE?");
38541         }else if(cbot > sbot){
38542             //Roo.log("set scrolltop to cbot-ch");
38543             c.scrollTop = cbot-ch;
38544         }
38545         
38546         if(hscroll !== false){
38547             if(cleft < sleft){
38548                 c.scrollLeft = cleft;
38549             }else if(cright > sright){
38550                 c.scrollLeft = cright-c.clientWidth;
38551             }
38552         }
38553          
38554         return el;
38555     },
38556
38557     updateColumns : function(){
38558         this.grid.stopEditing();
38559         var cm = this.grid.colModel, colIds = this.getColumnIds();
38560         //var totalWidth = cm.getTotalWidth();
38561         var pos = 0;
38562         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38563             //if(cm.isHidden(i)) continue;
38564             var w = cm.getColumnWidth(i);
38565             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38566             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38567         }
38568         this.updateSplitters();
38569     },
38570
38571     generateRules : function(cm){
38572         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38573         Roo.util.CSS.removeStyleSheet(rulesId);
38574         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38575             var cid = cm.getColumnId(i);
38576             var align = '';
38577             if(cm.config[i].align){
38578                 align = 'text-align:'+cm.config[i].align+';';
38579             }
38580             var hidden = '';
38581             if(cm.isHidden(i)){
38582                 hidden = 'display:none;';
38583             }
38584             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38585             ruleBuf.push(
38586                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38587                     this.hdSelector, cid, " {\n", align, width, "}\n",
38588                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38589                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38590         }
38591         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38592     },
38593
38594     updateSplitters : function(){
38595         var cm = this.cm, s = this.getSplitters();
38596         if(s){ // splitters not created yet
38597             var pos = 0, locked = true;
38598             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38599                 if(cm.isHidden(i)) {
38600                     continue;
38601                 }
38602                 var w = cm.getColumnWidth(i); // make sure it's a number
38603                 if(!cm.isLocked(i) && locked){
38604                     pos = 0;
38605                     locked = false;
38606                 }
38607                 pos += w;
38608                 s[i].style.left = (pos-this.splitOffset) + "px";
38609             }
38610         }
38611     },
38612
38613     handleHiddenChange : function(colModel, colIndex, hidden){
38614         if(hidden){
38615             this.hideColumn(colIndex);
38616         }else{
38617             this.unhideColumn(colIndex);
38618         }
38619     },
38620
38621     hideColumn : function(colIndex){
38622         var cid = this.getColumnId(colIndex);
38623         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38624         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38625         if(Roo.isSafari){
38626             this.updateHeaders();
38627         }
38628         this.updateSplitters();
38629         this.layout();
38630     },
38631
38632     unhideColumn : function(colIndex){
38633         var cid = this.getColumnId(colIndex);
38634         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38635         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38636
38637         if(Roo.isSafari){
38638             this.updateHeaders();
38639         }
38640         this.updateSplitters();
38641         this.layout();
38642     },
38643
38644     insertRows : function(dm, firstRow, lastRow, isUpdate){
38645         if(firstRow == 0 && lastRow == dm.getCount()-1){
38646             this.refresh();
38647         }else{
38648             if(!isUpdate){
38649                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38650             }
38651             var s = this.getScrollState();
38652             var markup = this.renderRows(firstRow, lastRow);
38653             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38654             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38655             this.restoreScroll(s);
38656             if(!isUpdate){
38657                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38658                 this.syncRowHeights(firstRow, lastRow);
38659                 this.stripeRows(firstRow);
38660                 this.layout();
38661             }
38662         }
38663     },
38664
38665     bufferRows : function(markup, target, index){
38666         var before = null, trows = target.rows, tbody = target.tBodies[0];
38667         if(index < trows.length){
38668             before = trows[index];
38669         }
38670         var b = document.createElement("div");
38671         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38672         var rows = b.firstChild.rows;
38673         for(var i = 0, len = rows.length; i < len; i++){
38674             if(before){
38675                 tbody.insertBefore(rows[0], before);
38676             }else{
38677                 tbody.appendChild(rows[0]);
38678             }
38679         }
38680         b.innerHTML = "";
38681         b = null;
38682     },
38683
38684     deleteRows : function(dm, firstRow, lastRow){
38685         if(dm.getRowCount()<1){
38686             this.fireEvent("beforerefresh", this);
38687             this.mainBody.update("");
38688             this.lockedBody.update("");
38689             this.fireEvent("refresh", this);
38690         }else{
38691             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38692             var bt = this.getBodyTable();
38693             var tbody = bt.firstChild;
38694             var rows = bt.rows;
38695             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38696                 tbody.removeChild(rows[firstRow]);
38697             }
38698             this.stripeRows(firstRow);
38699             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38700         }
38701     },
38702
38703     updateRows : function(dataSource, firstRow, lastRow){
38704         var s = this.getScrollState();
38705         this.refresh();
38706         this.restoreScroll(s);
38707     },
38708
38709     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38710         if(!noRefresh){
38711            this.refresh();
38712         }
38713         this.updateHeaderSortState();
38714     },
38715
38716     getScrollState : function(){
38717         
38718         var sb = this.scroller.dom;
38719         return {left: sb.scrollLeft, top: sb.scrollTop};
38720     },
38721
38722     stripeRows : function(startRow){
38723         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38724             return;
38725         }
38726         startRow = startRow || 0;
38727         var rows = this.getBodyTable().rows;
38728         var lrows = this.getLockedTable().rows;
38729         var cls = ' x-grid-row-alt ';
38730         for(var i = startRow, len = rows.length; i < len; i++){
38731             var row = rows[i], lrow = lrows[i];
38732             var isAlt = ((i+1) % 2 == 0);
38733             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38734             if(isAlt == hasAlt){
38735                 continue;
38736             }
38737             if(isAlt){
38738                 row.className += " x-grid-row-alt";
38739             }else{
38740                 row.className = row.className.replace("x-grid-row-alt", "");
38741             }
38742             if(lrow){
38743                 lrow.className = row.className;
38744             }
38745         }
38746     },
38747
38748     restoreScroll : function(state){
38749         //Roo.log('GridView.restoreScroll');
38750         var sb = this.scroller.dom;
38751         sb.scrollLeft = state.left;
38752         sb.scrollTop = state.top;
38753         this.syncScroll();
38754     },
38755
38756     syncScroll : function(){
38757         //Roo.log('GridView.syncScroll');
38758         var sb = this.scroller.dom;
38759         var sh = this.mainHd.dom;
38760         var bs = this.mainBody.dom;
38761         var lv = this.lockedBody.dom;
38762         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38763         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38764     },
38765
38766     handleScroll : function(e){
38767         this.syncScroll();
38768         var sb = this.scroller.dom;
38769         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38770         e.stopEvent();
38771     },
38772
38773     handleWheel : function(e){
38774         var d = e.getWheelDelta();
38775         this.scroller.dom.scrollTop -= d*22;
38776         // set this here to prevent jumpy scrolling on large tables
38777         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38778         e.stopEvent();
38779     },
38780
38781     renderRows : function(startRow, endRow){
38782         // pull in all the crap needed to render rows
38783         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38784         var colCount = cm.getColumnCount();
38785
38786         if(ds.getCount() < 1){
38787             return ["", ""];
38788         }
38789
38790         // build a map for all the columns
38791         var cs = [];
38792         for(var i = 0; i < colCount; i++){
38793             var name = cm.getDataIndex(i);
38794             cs[i] = {
38795                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38796                 renderer : cm.getRenderer(i),
38797                 id : cm.getColumnId(i),
38798                 locked : cm.isLocked(i),
38799                 has_editor : cm.isCellEditable(i)
38800             };
38801         }
38802
38803         startRow = startRow || 0;
38804         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38805
38806         // records to render
38807         var rs = ds.getRange(startRow, endRow);
38808
38809         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38810     },
38811
38812     // As much as I hate to duplicate code, this was branched because FireFox really hates
38813     // [].join("") on strings. The performance difference was substantial enough to
38814     // branch this function
38815     doRender : Roo.isGecko ?
38816             function(cs, rs, ds, startRow, colCount, stripe){
38817                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38818                 // buffers
38819                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38820                 
38821                 var hasListener = this.grid.hasListener('rowclass');
38822                 var rowcfg = {};
38823                 for(var j = 0, len = rs.length; j < len; j++){
38824                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38825                     for(var i = 0; i < colCount; i++){
38826                         c = cs[i];
38827                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38828                         p.id = c.id;
38829                         p.css = p.attr = "";
38830                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38831                         if(p.value == undefined || p.value === "") {
38832                             p.value = "&#160;";
38833                         }
38834                         if(c.has_editor){
38835                             p.css += ' x-grid-editable-cell';
38836                         }
38837                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38838                             p.css +=  ' x-grid-dirty-cell';
38839                         }
38840                         var markup = ct.apply(p);
38841                         if(!c.locked){
38842                             cb+= markup;
38843                         }else{
38844                             lcb+= markup;
38845                         }
38846                     }
38847                     var alt = [];
38848                     if(stripe && ((rowIndex+1) % 2 == 0)){
38849                         alt.push("x-grid-row-alt")
38850                     }
38851                     if(r.dirty){
38852                         alt.push(  " x-grid-dirty-row");
38853                     }
38854                     rp.cells = lcb;
38855                     if(this.getRowClass){
38856                         alt.push(this.getRowClass(r, rowIndex));
38857                     }
38858                     if (hasListener) {
38859                         rowcfg = {
38860                              
38861                             record: r,
38862                             rowIndex : rowIndex,
38863                             rowClass : ''
38864                         };
38865                         this.grid.fireEvent('rowclass', this, rowcfg);
38866                         alt.push(rowcfg.rowClass);
38867                     }
38868                     rp.alt = alt.join(" ");
38869                     lbuf+= rt.apply(rp);
38870                     rp.cells = cb;
38871                     buf+=  rt.apply(rp);
38872                 }
38873                 return [lbuf, buf];
38874             } :
38875             function(cs, rs, ds, startRow, colCount, stripe){
38876                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38877                 // buffers
38878                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38879                 var hasListener = this.grid.hasListener('rowclass');
38880  
38881                 var rowcfg = {};
38882                 for(var j = 0, len = rs.length; j < len; j++){
38883                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38884                     for(var i = 0; i < colCount; i++){
38885                         c = cs[i];
38886                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38887                         p.id = c.id;
38888                         p.css = p.attr = "";
38889                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38890                         if(p.value == undefined || p.value === "") {
38891                             p.value = "&#160;";
38892                         }
38893                         //Roo.log(c);
38894                          if(c.has_editor){
38895                             p.css += ' x-grid-editable-cell';
38896                         }
38897                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38898                             p.css += ' x-grid-dirty-cell' 
38899                         }
38900                         
38901                         var markup = ct.apply(p);
38902                         if(!c.locked){
38903                             cb[cb.length] = markup;
38904                         }else{
38905                             lcb[lcb.length] = markup;
38906                         }
38907                     }
38908                     var alt = [];
38909                     if(stripe && ((rowIndex+1) % 2 == 0)){
38910                         alt.push( "x-grid-row-alt");
38911                     }
38912                     if(r.dirty){
38913                         alt.push(" x-grid-dirty-row");
38914                     }
38915                     rp.cells = lcb;
38916                     if(this.getRowClass){
38917                         alt.push( this.getRowClass(r, rowIndex));
38918                     }
38919                     if (hasListener) {
38920                         rowcfg = {
38921                              
38922                             record: r,
38923                             rowIndex : rowIndex,
38924                             rowClass : ''
38925                         };
38926                         this.grid.fireEvent('rowclass', this, rowcfg);
38927                         alt.push(rowcfg.rowClass);
38928                     }
38929                     
38930                     rp.alt = alt.join(" ");
38931                     rp.cells = lcb.join("");
38932                     lbuf[lbuf.length] = rt.apply(rp);
38933                     rp.cells = cb.join("");
38934                     buf[buf.length] =  rt.apply(rp);
38935                 }
38936                 return [lbuf.join(""), buf.join("")];
38937             },
38938
38939     renderBody : function(){
38940         var markup = this.renderRows();
38941         var bt = this.templates.body;
38942         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38943     },
38944
38945     /**
38946      * Refreshes the grid
38947      * @param {Boolean} headersToo
38948      */
38949     refresh : function(headersToo){
38950         this.fireEvent("beforerefresh", this);
38951         this.grid.stopEditing();
38952         var result = this.renderBody();
38953         this.lockedBody.update(result[0]);
38954         this.mainBody.update(result[1]);
38955         if(headersToo === true){
38956             this.updateHeaders();
38957             this.updateColumns();
38958             this.updateSplitters();
38959             this.updateHeaderSortState();
38960         }
38961         this.syncRowHeights();
38962         this.layout();
38963         this.fireEvent("refresh", this);
38964     },
38965
38966     handleColumnMove : function(cm, oldIndex, newIndex){
38967         this.indexMap = null;
38968         var s = this.getScrollState();
38969         this.refresh(true);
38970         this.restoreScroll(s);
38971         this.afterMove(newIndex);
38972     },
38973
38974     afterMove : function(colIndex){
38975         if(this.enableMoveAnim && Roo.enableFx){
38976             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38977         }
38978         // if multisort - fix sortOrder, and reload..
38979         if (this.grid.dataSource.multiSort) {
38980             // the we can call sort again..
38981             var dm = this.grid.dataSource;
38982             var cm = this.grid.colModel;
38983             var so = [];
38984             for(var i = 0; i < cm.config.length; i++ ) {
38985                 
38986                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38987                     continue; // dont' bother, it's not in sort list or being set.
38988                 }
38989                 
38990                 so.push(cm.config[i].dataIndex);
38991             };
38992             dm.sortOrder = so;
38993             dm.load(dm.lastOptions);
38994             
38995             
38996         }
38997         
38998     },
38999
39000     updateCell : function(dm, rowIndex, dataIndex){
39001         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
39002         if(typeof colIndex == "undefined"){ // not present in grid
39003             return;
39004         }
39005         var cm = this.grid.colModel;
39006         var cell = this.getCell(rowIndex, colIndex);
39007         var cellText = this.getCellText(rowIndex, colIndex);
39008
39009         var p = {
39010             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
39011             id : cm.getColumnId(colIndex),
39012             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
39013         };
39014         var renderer = cm.getRenderer(colIndex);
39015         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
39016         if(typeof val == "undefined" || val === "") {
39017             val = "&#160;";
39018         }
39019         cellText.innerHTML = val;
39020         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
39021         this.syncRowHeights(rowIndex, rowIndex);
39022     },
39023
39024     calcColumnWidth : function(colIndex, maxRowsToMeasure){
39025         var maxWidth = 0;
39026         if(this.grid.autoSizeHeaders){
39027             var h = this.getHeaderCellMeasure(colIndex);
39028             maxWidth = Math.max(maxWidth, h.scrollWidth);
39029         }
39030         var tb, index;
39031         if(this.cm.isLocked(colIndex)){
39032             tb = this.getLockedTable();
39033             index = colIndex;
39034         }else{
39035             tb = this.getBodyTable();
39036             index = colIndex - this.cm.getLockedCount();
39037         }
39038         if(tb && tb.rows){
39039             var rows = tb.rows;
39040             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
39041             for(var i = 0; i < stopIndex; i++){
39042                 var cell = rows[i].childNodes[index].firstChild;
39043                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
39044             }
39045         }
39046         return maxWidth + /*margin for error in IE*/ 5;
39047     },
39048     /**
39049      * Autofit a column to its content.
39050      * @param {Number} colIndex
39051      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
39052      */
39053      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
39054          if(this.cm.isHidden(colIndex)){
39055              return; // can't calc a hidden column
39056          }
39057         if(forceMinSize){
39058             var cid = this.cm.getColumnId(colIndex);
39059             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
39060            if(this.grid.autoSizeHeaders){
39061                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
39062            }
39063         }
39064         var newWidth = this.calcColumnWidth(colIndex);
39065         this.cm.setColumnWidth(colIndex,
39066             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
39067         if(!suppressEvent){
39068             this.grid.fireEvent("columnresize", colIndex, newWidth);
39069         }
39070     },
39071
39072     /**
39073      * Autofits all columns to their content and then expands to fit any extra space in the grid
39074      */
39075      autoSizeColumns : function(){
39076         var cm = this.grid.colModel;
39077         var colCount = cm.getColumnCount();
39078         for(var i = 0; i < colCount; i++){
39079             this.autoSizeColumn(i, true, true);
39080         }
39081         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
39082             this.fitColumns();
39083         }else{
39084             this.updateColumns();
39085             this.layout();
39086         }
39087     },
39088
39089     /**
39090      * Autofits all columns to the grid's width proportionate with their current size
39091      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
39092      */
39093     fitColumns : function(reserveScrollSpace){
39094         var cm = this.grid.colModel;
39095         var colCount = cm.getColumnCount();
39096         var cols = [];
39097         var width = 0;
39098         var i, w;
39099         for (i = 0; i < colCount; i++){
39100             if(!cm.isHidden(i) && !cm.isFixed(i)){
39101                 w = cm.getColumnWidth(i);
39102                 cols.push(i);
39103                 cols.push(w);
39104                 width += w;
39105             }
39106         }
39107         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
39108         if(reserveScrollSpace){
39109             avail -= 17;
39110         }
39111         var frac = (avail - cm.getTotalWidth())/width;
39112         while (cols.length){
39113             w = cols.pop();
39114             i = cols.pop();
39115             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
39116         }
39117         this.updateColumns();
39118         this.layout();
39119     },
39120
39121     onRowSelect : function(rowIndex){
39122         var row = this.getRowComposite(rowIndex);
39123         row.addClass("x-grid-row-selected");
39124     },
39125
39126     onRowDeselect : function(rowIndex){
39127         var row = this.getRowComposite(rowIndex);
39128         row.removeClass("x-grid-row-selected");
39129     },
39130
39131     onCellSelect : function(row, col){
39132         var cell = this.getCell(row, col);
39133         if(cell){
39134             Roo.fly(cell).addClass("x-grid-cell-selected");
39135         }
39136     },
39137
39138     onCellDeselect : function(row, col){
39139         var cell = this.getCell(row, col);
39140         if(cell){
39141             Roo.fly(cell).removeClass("x-grid-cell-selected");
39142         }
39143     },
39144
39145     updateHeaderSortState : function(){
39146         
39147         // sort state can be single { field: xxx, direction : yyy}
39148         // or   { xxx=>ASC , yyy : DESC ..... }
39149         
39150         var mstate = {};
39151         if (!this.ds.multiSort) { 
39152             var state = this.ds.getSortState();
39153             if(!state){
39154                 return;
39155             }
39156             mstate[state.field] = state.direction;
39157             // FIXME... - this is not used here.. but might be elsewhere..
39158             this.sortState = state;
39159             
39160         } else {
39161             mstate = this.ds.sortToggle;
39162         }
39163         //remove existing sort classes..
39164         
39165         var sc = this.sortClasses;
39166         var hds = this.el.select(this.headerSelector).removeClass(sc);
39167         
39168         for(var f in mstate) {
39169         
39170             var sortColumn = this.cm.findColumnIndex(f);
39171             
39172             if(sortColumn != -1){
39173                 var sortDir = mstate[f];        
39174                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
39175             }
39176         }
39177         
39178          
39179         
39180     },
39181
39182
39183     handleHeaderClick : function(g, index,e){
39184         
39185         Roo.log("header click");
39186         
39187         if (Roo.isTouch) {
39188             // touch events on header are handled by context
39189             this.handleHdCtx(g,index,e);
39190             return;
39191         }
39192         
39193         
39194         if(this.headersDisabled){
39195             return;
39196         }
39197         var dm = g.dataSource, cm = g.colModel;
39198         if(!cm.isSortable(index)){
39199             return;
39200         }
39201         g.stopEditing();
39202         
39203         if (dm.multiSort) {
39204             // update the sortOrder
39205             var so = [];
39206             for(var i = 0; i < cm.config.length; i++ ) {
39207                 
39208                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39209                     continue; // dont' bother, it's not in sort list or being set.
39210                 }
39211                 
39212                 so.push(cm.config[i].dataIndex);
39213             };
39214             dm.sortOrder = so;
39215         }
39216         
39217         
39218         dm.sort(cm.getDataIndex(index));
39219     },
39220
39221
39222     destroy : function(){
39223         if(this.colMenu){
39224             this.colMenu.removeAll();
39225             Roo.menu.MenuMgr.unregister(this.colMenu);
39226             this.colMenu.getEl().remove();
39227             delete this.colMenu;
39228         }
39229         if(this.hmenu){
39230             this.hmenu.removeAll();
39231             Roo.menu.MenuMgr.unregister(this.hmenu);
39232             this.hmenu.getEl().remove();
39233             delete this.hmenu;
39234         }
39235         if(this.grid.enableColumnMove){
39236             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39237             if(dds){
39238                 for(var dd in dds){
39239                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
39240                         var elid = dds[dd].dragElId;
39241                         dds[dd].unreg();
39242                         Roo.get(elid).remove();
39243                     } else if(dds[dd].config.isTarget){
39244                         dds[dd].proxyTop.remove();
39245                         dds[dd].proxyBottom.remove();
39246                         dds[dd].unreg();
39247                     }
39248                     if(Roo.dd.DDM.locationCache[dd]){
39249                         delete Roo.dd.DDM.locationCache[dd];
39250                     }
39251                 }
39252                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39253             }
39254         }
39255         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39256         this.bind(null, null);
39257         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39258     },
39259
39260     handleLockChange : function(){
39261         this.refresh(true);
39262     },
39263
39264     onDenyColumnLock : function(){
39265
39266     },
39267
39268     onDenyColumnHide : function(){
39269
39270     },
39271
39272     handleHdMenuClick : function(item){
39273         var index = this.hdCtxIndex;
39274         var cm = this.cm, ds = this.ds;
39275         switch(item.id){
39276             case "asc":
39277                 ds.sort(cm.getDataIndex(index), "ASC");
39278                 break;
39279             case "desc":
39280                 ds.sort(cm.getDataIndex(index), "DESC");
39281                 break;
39282             case "lock":
39283                 var lc = cm.getLockedCount();
39284                 if(cm.getColumnCount(true) <= lc+1){
39285                     this.onDenyColumnLock();
39286                     return;
39287                 }
39288                 if(lc != index){
39289                     cm.setLocked(index, true, true);
39290                     cm.moveColumn(index, lc);
39291                     this.grid.fireEvent("columnmove", index, lc);
39292                 }else{
39293                     cm.setLocked(index, true);
39294                 }
39295             break;
39296             case "unlock":
39297                 var lc = cm.getLockedCount();
39298                 if((lc-1) != index){
39299                     cm.setLocked(index, false, true);
39300                     cm.moveColumn(index, lc-1);
39301                     this.grid.fireEvent("columnmove", index, lc-1);
39302                 }else{
39303                     cm.setLocked(index, false);
39304                 }
39305             break;
39306             case 'wider': // used to expand cols on touch..
39307             case 'narrow':
39308                 var cw = cm.getColumnWidth(index);
39309                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39310                 cw = Math.max(0, cw);
39311                 cw = Math.min(cw,4000);
39312                 cm.setColumnWidth(index, cw);
39313                 break;
39314                 
39315             default:
39316                 index = cm.getIndexById(item.id.substr(4));
39317                 if(index != -1){
39318                     if(item.checked && cm.getColumnCount(true) <= 1){
39319                         this.onDenyColumnHide();
39320                         return false;
39321                     }
39322                     cm.setHidden(index, item.checked);
39323                 }
39324         }
39325         return true;
39326     },
39327
39328     beforeColMenuShow : function(){
39329         var cm = this.cm,  colCount = cm.getColumnCount();
39330         this.colMenu.removeAll();
39331         
39332         var items = [];
39333         for(var i = 0; i < colCount; i++){
39334             items.push({
39335                 id: "col-"+cm.getColumnId(i),
39336                 text: cm.getColumnHeader(i),
39337                 checked: !cm.isHidden(i),
39338                 hideOnClick:false
39339             });
39340         }
39341         
39342         if (this.grid.sortColMenu) {
39343             items.sort(function(a,b) {
39344                 if (a.text == b.text) {
39345                     return 0;
39346                 }
39347                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39348             });
39349         }
39350         
39351         for(var i = 0; i < colCount; i++){
39352             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39353         }
39354     },
39355
39356     handleHdCtx : function(g, index, e){
39357         e.stopEvent();
39358         var hd = this.getHeaderCell(index);
39359         this.hdCtxIndex = index;
39360         var ms = this.hmenu.items, cm = this.cm;
39361         ms.get("asc").setDisabled(!cm.isSortable(index));
39362         ms.get("desc").setDisabled(!cm.isSortable(index));
39363         if(this.grid.enableColLock !== false){
39364             ms.get("lock").setDisabled(cm.isLocked(index));
39365             ms.get("unlock").setDisabled(!cm.isLocked(index));
39366         }
39367         this.hmenu.show(hd, "tl-bl");
39368     },
39369
39370     handleHdOver : function(e){
39371         var hd = this.findHeaderCell(e.getTarget());
39372         if(hd && !this.headersDisabled){
39373             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39374                this.fly(hd).addClass("x-grid-hd-over");
39375             }
39376         }
39377     },
39378
39379     handleHdOut : function(e){
39380         var hd = this.findHeaderCell(e.getTarget());
39381         if(hd){
39382             this.fly(hd).removeClass("x-grid-hd-over");
39383         }
39384     },
39385
39386     handleSplitDblClick : function(e, t){
39387         var i = this.getCellIndex(t);
39388         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39389             this.autoSizeColumn(i, true);
39390             this.layout();
39391         }
39392     },
39393
39394     render : function(){
39395
39396         var cm = this.cm;
39397         var colCount = cm.getColumnCount();
39398
39399         if(this.grid.monitorWindowResize === true){
39400             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39401         }
39402         var header = this.renderHeaders();
39403         var body = this.templates.body.apply({rows:""});
39404         var html = this.templates.master.apply({
39405             lockedBody: body,
39406             body: body,
39407             lockedHeader: header[0],
39408             header: header[1]
39409         });
39410
39411         //this.updateColumns();
39412
39413         this.grid.getGridEl().dom.innerHTML = html;
39414
39415         this.initElements();
39416         
39417         // a kludge to fix the random scolling effect in webkit
39418         this.el.on("scroll", function() {
39419             this.el.dom.scrollTop=0; // hopefully not recursive..
39420         },this);
39421
39422         this.scroller.on("scroll", this.handleScroll, this);
39423         this.lockedBody.on("mousewheel", this.handleWheel, this);
39424         this.mainBody.on("mousewheel", this.handleWheel, this);
39425
39426         this.mainHd.on("mouseover", this.handleHdOver, this);
39427         this.mainHd.on("mouseout", this.handleHdOut, this);
39428         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39429                 {delegate: "."+this.splitClass});
39430
39431         this.lockedHd.on("mouseover", this.handleHdOver, this);
39432         this.lockedHd.on("mouseout", this.handleHdOut, this);
39433         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39434                 {delegate: "."+this.splitClass});
39435
39436         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39437             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39438         }
39439
39440         this.updateSplitters();
39441
39442         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39443             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39444             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39445         }
39446
39447         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39448             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39449             this.hmenu.add(
39450                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39451                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39452             );
39453             if(this.grid.enableColLock !== false){
39454                 this.hmenu.add('-',
39455                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39456                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39457                 );
39458             }
39459             if (Roo.isTouch) {
39460                  this.hmenu.add('-',
39461                     {id:"wider", text: this.columnsWiderText},
39462                     {id:"narrow", text: this.columnsNarrowText }
39463                 );
39464                 
39465                  
39466             }
39467             
39468             if(this.grid.enableColumnHide !== false){
39469
39470                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39471                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39472                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39473
39474                 this.hmenu.add('-',
39475                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39476                 );
39477             }
39478             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39479
39480             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39481         }
39482
39483         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39484             this.dd = new Roo.grid.GridDragZone(this.grid, {
39485                 ddGroup : this.grid.ddGroup || 'GridDD'
39486             });
39487             
39488         }
39489
39490         /*
39491         for(var i = 0; i < colCount; i++){
39492             if(cm.isHidden(i)){
39493                 this.hideColumn(i);
39494             }
39495             if(cm.config[i].align){
39496                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39497                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39498             }
39499         }*/
39500         
39501         this.updateHeaderSortState();
39502
39503         this.beforeInitialResize();
39504         this.layout(true);
39505
39506         // two part rendering gives faster view to the user
39507         this.renderPhase2.defer(1, this);
39508     },
39509
39510     renderPhase2 : function(){
39511         // render the rows now
39512         this.refresh();
39513         if(this.grid.autoSizeColumns){
39514             this.autoSizeColumns();
39515         }
39516     },
39517
39518     beforeInitialResize : function(){
39519
39520     },
39521
39522     onColumnSplitterMoved : function(i, w){
39523         this.userResized = true;
39524         var cm = this.grid.colModel;
39525         cm.setColumnWidth(i, w, true);
39526         var cid = cm.getColumnId(i);
39527         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39528         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39529         this.updateSplitters();
39530         this.layout();
39531         this.grid.fireEvent("columnresize", i, w);
39532     },
39533
39534     syncRowHeights : function(startIndex, endIndex){
39535         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39536             startIndex = startIndex || 0;
39537             var mrows = this.getBodyTable().rows;
39538             var lrows = this.getLockedTable().rows;
39539             var len = mrows.length-1;
39540             endIndex = Math.min(endIndex || len, len);
39541             for(var i = startIndex; i <= endIndex; i++){
39542                 var m = mrows[i], l = lrows[i];
39543                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39544                 m.style.height = l.style.height = h + "px";
39545             }
39546         }
39547     },
39548
39549     layout : function(initialRender, is2ndPass)
39550     {
39551         var g = this.grid;
39552         var auto = g.autoHeight;
39553         var scrollOffset = 16;
39554         var c = g.getGridEl(), cm = this.cm,
39555                 expandCol = g.autoExpandColumn,
39556                 gv = this;
39557         //c.beginMeasure();
39558
39559         if(!c.dom.offsetWidth){ // display:none?
39560             if(initialRender){
39561                 this.lockedWrap.show();
39562                 this.mainWrap.show();
39563             }
39564             return;
39565         }
39566
39567         var hasLock = this.cm.isLocked(0);
39568
39569         var tbh = this.headerPanel.getHeight();
39570         var bbh = this.footerPanel.getHeight();
39571
39572         if(auto){
39573             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39574             var newHeight = ch + c.getBorderWidth("tb");
39575             if(g.maxHeight){
39576                 newHeight = Math.min(g.maxHeight, newHeight);
39577             }
39578             c.setHeight(newHeight);
39579         }
39580
39581         if(g.autoWidth){
39582             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39583         }
39584
39585         var s = this.scroller;
39586
39587         var csize = c.getSize(true);
39588
39589         this.el.setSize(csize.width, csize.height);
39590
39591         this.headerPanel.setWidth(csize.width);
39592         this.footerPanel.setWidth(csize.width);
39593
39594         var hdHeight = this.mainHd.getHeight();
39595         var vw = csize.width;
39596         var vh = csize.height - (tbh + bbh);
39597
39598         s.setSize(vw, vh);
39599
39600         var bt = this.getBodyTable();
39601         
39602         if(cm.getLockedCount() == cm.config.length){
39603             bt = this.getLockedTable();
39604         }
39605         
39606         var ltWidth = hasLock ?
39607                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39608
39609         var scrollHeight = bt.offsetHeight;
39610         var scrollWidth = ltWidth + bt.offsetWidth;
39611         var vscroll = false, hscroll = false;
39612
39613         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39614
39615         var lw = this.lockedWrap, mw = this.mainWrap;
39616         var lb = this.lockedBody, mb = this.mainBody;
39617
39618         setTimeout(function(){
39619             var t = s.dom.offsetTop;
39620             var w = s.dom.clientWidth,
39621                 h = s.dom.clientHeight;
39622
39623             lw.setTop(t);
39624             lw.setSize(ltWidth, h);
39625
39626             mw.setLeftTop(ltWidth, t);
39627             mw.setSize(w-ltWidth, h);
39628
39629             lb.setHeight(h-hdHeight);
39630             mb.setHeight(h-hdHeight);
39631
39632             if(is2ndPass !== true && !gv.userResized && expandCol){
39633                 // high speed resize without full column calculation
39634                 
39635                 var ci = cm.getIndexById(expandCol);
39636                 if (ci < 0) {
39637                     ci = cm.findColumnIndex(expandCol);
39638                 }
39639                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39640                 var expandId = cm.getColumnId(ci);
39641                 var  tw = cm.getTotalWidth(false);
39642                 var currentWidth = cm.getColumnWidth(ci);
39643                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39644                 if(currentWidth != cw){
39645                     cm.setColumnWidth(ci, cw, true);
39646                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39647                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39648                     gv.updateSplitters();
39649                     gv.layout(false, true);
39650                 }
39651             }
39652
39653             if(initialRender){
39654                 lw.show();
39655                 mw.show();
39656             }
39657             //c.endMeasure();
39658         }, 10);
39659     },
39660
39661     onWindowResize : function(){
39662         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39663             return;
39664         }
39665         this.layout();
39666     },
39667
39668     appendFooter : function(parentEl){
39669         return null;
39670     },
39671
39672     sortAscText : "Sort Ascending",
39673     sortDescText : "Sort Descending",
39674     lockText : "Lock Column",
39675     unlockText : "Unlock Column",
39676     columnsText : "Columns",
39677  
39678     columnsWiderText : "Wider",
39679     columnsNarrowText : "Thinner"
39680 });
39681
39682
39683 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39684     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39685     this.proxy.el.addClass('x-grid3-col-dd');
39686 };
39687
39688 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39689     handleMouseDown : function(e){
39690
39691     },
39692
39693     callHandleMouseDown : function(e){
39694         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39695     }
39696 });
39697 /*
39698  * Based on:
39699  * Ext JS Library 1.1.1
39700  * Copyright(c) 2006-2007, Ext JS, LLC.
39701  *
39702  * Originally Released Under LGPL - original licence link has changed is not relivant.
39703  *
39704  * Fork - LGPL
39705  * <script type="text/javascript">
39706  */
39707  /**
39708  * @extends Roo.dd.DDProxy
39709  * @class Roo.grid.SplitDragZone
39710  * Support for Column Header resizing
39711  * @constructor
39712  * @param {Object} config
39713  */
39714 // private
39715 // This is a support class used internally by the Grid components
39716 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39717     this.grid = grid;
39718     this.view = grid.getView();
39719     this.proxy = this.view.resizeProxy;
39720     Roo.grid.SplitDragZone.superclass.constructor.call(
39721         this,
39722         hd, // ID
39723         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39724         {  // CONFIG
39725             dragElId : Roo.id(this.proxy.dom),
39726             resizeFrame:false
39727         }
39728     );
39729     
39730     this.setHandleElId(Roo.id(hd));
39731     if (hd2 !== false) {
39732         this.setOuterHandleElId(Roo.id(hd2));
39733     }
39734     
39735     this.scroll = false;
39736 };
39737 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39738     fly: Roo.Element.fly,
39739
39740     b4StartDrag : function(x, y){
39741         this.view.headersDisabled = true;
39742         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39743                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39744         );
39745         this.proxy.setHeight(h);
39746         
39747         // for old system colWidth really stored the actual width?
39748         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39749         // which in reality did not work.. - it worked only for fixed sizes
39750         // for resizable we need to use actual sizes.
39751         var w = this.cm.getColumnWidth(this.cellIndex);
39752         if (!this.view.mainWrap) {
39753             // bootstrap.
39754             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39755         }
39756         
39757         
39758         
39759         // this was w-this.grid.minColumnWidth;
39760         // doesnt really make sense? - w = thie curren width or the rendered one?
39761         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39762         this.resetConstraints();
39763         this.setXConstraint(minw, 1000);
39764         this.setYConstraint(0, 0);
39765         this.minX = x - minw;
39766         this.maxX = x + 1000;
39767         this.startPos = x;
39768         if (!this.view.mainWrap) { // this is Bootstrap code..
39769             this.getDragEl().style.display='block';
39770         }
39771         
39772         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39773     },
39774
39775
39776     handleMouseDown : function(e){
39777         ev = Roo.EventObject.setEvent(e);
39778         var t = this.fly(ev.getTarget());
39779         if(t.hasClass("x-grid-split")){
39780             this.cellIndex = this.view.getCellIndex(t.dom);
39781             this.split = t.dom;
39782             this.cm = this.grid.colModel;
39783             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39784                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39785             }
39786         }
39787     },
39788
39789     endDrag : function(e){
39790         this.view.headersDisabled = false;
39791         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39792         var diff = endX - this.startPos;
39793         // 
39794         var w = this.cm.getColumnWidth(this.cellIndex);
39795         if (!this.view.mainWrap) {
39796             w = 0;
39797         }
39798         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39799     },
39800
39801     autoOffset : function(){
39802         this.setDelta(0,0);
39803     }
39804 });/*
39805  * Based on:
39806  * Ext JS Library 1.1.1
39807  * Copyright(c) 2006-2007, Ext JS, LLC.
39808  *
39809  * Originally Released Under LGPL - original licence link has changed is not relivant.
39810  *
39811  * Fork - LGPL
39812  * <script type="text/javascript">
39813  */
39814  
39815 // private
39816 // This is a support class used internally by the Grid components
39817 Roo.grid.GridDragZone = function(grid, config){
39818     this.view = grid.getView();
39819     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39820     if(this.view.lockedBody){
39821         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39822         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39823     }
39824     this.scroll = false;
39825     this.grid = grid;
39826     this.ddel = document.createElement('div');
39827     this.ddel.className = 'x-grid-dd-wrap';
39828 };
39829
39830 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39831     ddGroup : "GridDD",
39832
39833     getDragData : function(e){
39834         var t = Roo.lib.Event.getTarget(e);
39835         var rowIndex = this.view.findRowIndex(t);
39836         var sm = this.grid.selModel;
39837             
39838         //Roo.log(rowIndex);
39839         
39840         if (sm.getSelectedCell) {
39841             // cell selection..
39842             if (!sm.getSelectedCell()) {
39843                 return false;
39844             }
39845             if (rowIndex != sm.getSelectedCell()[0]) {
39846                 return false;
39847             }
39848         
39849         }
39850         if (sm.getSelections && sm.getSelections().length < 1) {
39851             return false;
39852         }
39853         
39854         
39855         // before it used to all dragging of unseleted... - now we dont do that.
39856         if(rowIndex !== false){
39857             
39858             // if editorgrid.. 
39859             
39860             
39861             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39862                
39863             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39864               //  
39865             //}
39866             if (e.hasModifier()){
39867                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39868             }
39869             
39870             Roo.log("getDragData");
39871             
39872             return {
39873                 grid: this.grid,
39874                 ddel: this.ddel,
39875                 rowIndex: rowIndex,
39876                 selections: sm.getSelections ? sm.getSelections() : (
39877                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39878             };
39879         }
39880         return false;
39881     },
39882     
39883     
39884     onInitDrag : function(e){
39885         var data = this.dragData;
39886         this.ddel.innerHTML = this.grid.getDragDropText();
39887         this.proxy.update(this.ddel);
39888         // fire start drag?
39889     },
39890
39891     afterRepair : function(){
39892         this.dragging = false;
39893     },
39894
39895     getRepairXY : function(e, data){
39896         return false;
39897     },
39898
39899     onEndDrag : function(data, e){
39900         // fire end drag?
39901     },
39902
39903     onValidDrop : function(dd, e, id){
39904         // fire drag drop?
39905         this.hideProxy();
39906     },
39907
39908     beforeInvalidDrop : function(e, id){
39909
39910     }
39911 });/*
39912  * Based on:
39913  * Ext JS Library 1.1.1
39914  * Copyright(c) 2006-2007, Ext JS, LLC.
39915  *
39916  * Originally Released Under LGPL - original licence link has changed is not relivant.
39917  *
39918  * Fork - LGPL
39919  * <script type="text/javascript">
39920  */
39921  
39922
39923 /**
39924  * @class Roo.grid.ColumnModel
39925  * @extends Roo.util.Observable
39926  * This is the default implementation of a ColumnModel used by the Grid. It defines
39927  * the columns in the grid.
39928  * <br>Usage:<br>
39929  <pre><code>
39930  var colModel = new Roo.grid.ColumnModel([
39931         {header: "Ticker", width: 60, sortable: true, locked: true},
39932         {header: "Company Name", width: 150, sortable: true},
39933         {header: "Market Cap.", width: 100, sortable: true},
39934         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39935         {header: "Employees", width: 100, sortable: true, resizable: false}
39936  ]);
39937  </code></pre>
39938  * <p>
39939  
39940  * The config options listed for this class are options which may appear in each
39941  * individual column definition.
39942  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39943  * @constructor
39944  * @param {Object} config An Array of column config objects. See this class's
39945  * config objects for details.
39946 */
39947 Roo.grid.ColumnModel = function(config){
39948         /**
39949      * The config passed into the constructor
39950      */
39951     this.config = []; //config;
39952     this.lookup = {};
39953
39954     // if no id, create one
39955     // if the column does not have a dataIndex mapping,
39956     // map it to the order it is in the config
39957     for(var i = 0, len = config.length; i < len; i++){
39958         this.addColumn(config[i]);
39959         
39960     }
39961
39962     /**
39963      * The width of columns which have no width specified (defaults to 100)
39964      * @type Number
39965      */
39966     this.defaultWidth = 100;
39967
39968     /**
39969      * Default sortable of columns which have no sortable specified (defaults to false)
39970      * @type Boolean
39971      */
39972     this.defaultSortable = false;
39973
39974     this.addEvents({
39975         /**
39976              * @event widthchange
39977              * Fires when the width of a column changes.
39978              * @param {ColumnModel} this
39979              * @param {Number} columnIndex The column index
39980              * @param {Number} newWidth The new width
39981              */
39982             "widthchange": true,
39983         /**
39984              * @event headerchange
39985              * Fires when the text of a header changes.
39986              * @param {ColumnModel} this
39987              * @param {Number} columnIndex The column index
39988              * @param {Number} newText The new header text
39989              */
39990             "headerchange": true,
39991         /**
39992              * @event hiddenchange
39993              * Fires when a column is hidden or "unhidden".
39994              * @param {ColumnModel} this
39995              * @param {Number} columnIndex The column index
39996              * @param {Boolean} hidden true if hidden, false otherwise
39997              */
39998             "hiddenchange": true,
39999             /**
40000          * @event columnmoved
40001          * Fires when a column is moved.
40002          * @param {ColumnModel} this
40003          * @param {Number} oldIndex
40004          * @param {Number} newIndex
40005          */
40006         "columnmoved" : true,
40007         /**
40008          * @event columlockchange
40009          * Fires when a column's locked state is changed
40010          * @param {ColumnModel} this
40011          * @param {Number} colIndex
40012          * @param {Boolean} locked true if locked
40013          */
40014         "columnlockchange" : true
40015     });
40016     Roo.grid.ColumnModel.superclass.constructor.call(this);
40017 };
40018 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
40019     /**
40020      * @cfg {String} header [required] The header text to display in the Grid view.
40021      */
40022         /**
40023      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
40024      */
40025         /**
40026      * @cfg {String} smHeader Header at Bootsrap Small width
40027      */
40028         /**
40029      * @cfg {String} mdHeader Header at Bootsrap Medium width
40030      */
40031         /**
40032      * @cfg {String} lgHeader Header at Bootsrap Large width
40033      */
40034         /**
40035      * @cfg {String} xlHeader Header at Bootsrap extra Large width
40036      */
40037     /**
40038      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
40039      * {@link Roo.data.Record} definition from which to draw the column's value. If not
40040      * specified, the column's index is used as an index into the Record's data Array.
40041      */
40042     /**
40043      * @cfg {Number} width  The initial width in pixels of the column. Using this
40044      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
40045      */
40046     /**
40047      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
40048      * Defaults to the value of the {@link #defaultSortable} property.
40049      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
40050      */
40051     /**
40052      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
40053      */
40054     /**
40055      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
40056      */
40057     /**
40058      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
40059      */
40060     /**
40061      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
40062      */
40063     /**
40064      * @cfg {Function} renderer A function used to generate HTML markup for a cell
40065      * given the cell's data value. See {@link #setRenderer}. If not specified, the
40066      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
40067      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
40068      */
40069        /**
40070      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
40071      */
40072     /**
40073      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
40074      */
40075     /**
40076      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
40077      */
40078     /**
40079      * @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)
40080      */
40081     /**
40082      * @cfg {String} tooltip mouse over tooltip text
40083      */
40084     /**
40085      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
40086      */
40087     /**
40088      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
40089      */
40090     /**
40091      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
40092      */
40093     /**
40094      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
40095      */
40096         /**
40097      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
40098      */
40099     /**
40100      * Returns the id of the column at the specified index.
40101      * @param {Number} index The column index
40102      * @return {String} the id
40103      */
40104     getColumnId : function(index){
40105         return this.config[index].id;
40106     },
40107
40108     /**
40109      * Returns the column for a specified id.
40110      * @param {String} id The column id
40111      * @return {Object} the column
40112      */
40113     getColumnById : function(id){
40114         return this.lookup[id];
40115     },
40116
40117     
40118     /**
40119      * Returns the column Object for a specified dataIndex.
40120      * @param {String} dataIndex The column dataIndex
40121      * @return {Object|Boolean} the column or false if not found
40122      */
40123     getColumnByDataIndex: function(dataIndex){
40124         var index = this.findColumnIndex(dataIndex);
40125         return index > -1 ? this.config[index] : false;
40126     },
40127     
40128     /**
40129      * Returns the index for a specified column id.
40130      * @param {String} id The column id
40131      * @return {Number} the index, or -1 if not found
40132      */
40133     getIndexById : function(id){
40134         for(var i = 0, len = this.config.length; i < len; i++){
40135             if(this.config[i].id == id){
40136                 return i;
40137             }
40138         }
40139         return -1;
40140     },
40141     
40142     /**
40143      * Returns the index for a specified column dataIndex.
40144      * @param {String} dataIndex The column dataIndex
40145      * @return {Number} the index, or -1 if not found
40146      */
40147     
40148     findColumnIndex : function(dataIndex){
40149         for(var i = 0, len = this.config.length; i < len; i++){
40150             if(this.config[i].dataIndex == dataIndex){
40151                 return i;
40152             }
40153         }
40154         return -1;
40155     },
40156     
40157     
40158     moveColumn : function(oldIndex, newIndex){
40159         var c = this.config[oldIndex];
40160         this.config.splice(oldIndex, 1);
40161         this.config.splice(newIndex, 0, c);
40162         this.dataMap = null;
40163         this.fireEvent("columnmoved", this, oldIndex, newIndex);
40164     },
40165
40166     isLocked : function(colIndex){
40167         return this.config[colIndex].locked === true;
40168     },
40169
40170     setLocked : function(colIndex, value, suppressEvent){
40171         if(this.isLocked(colIndex) == value){
40172             return;
40173         }
40174         this.config[colIndex].locked = value;
40175         if(!suppressEvent){
40176             this.fireEvent("columnlockchange", this, colIndex, value);
40177         }
40178     },
40179
40180     getTotalLockedWidth : function(){
40181         var totalWidth = 0;
40182         for(var i = 0; i < this.config.length; i++){
40183             if(this.isLocked(i) && !this.isHidden(i)){
40184                 this.totalWidth += this.getColumnWidth(i);
40185             }
40186         }
40187         return totalWidth;
40188     },
40189
40190     getLockedCount : function(){
40191         for(var i = 0, len = this.config.length; i < len; i++){
40192             if(!this.isLocked(i)){
40193                 return i;
40194             }
40195         }
40196         
40197         return this.config.length;
40198     },
40199
40200     /**
40201      * Returns the number of columns.
40202      * @return {Number}
40203      */
40204     getColumnCount : function(visibleOnly){
40205         if(visibleOnly === true){
40206             var c = 0;
40207             for(var i = 0, len = this.config.length; i < len; i++){
40208                 if(!this.isHidden(i)){
40209                     c++;
40210                 }
40211             }
40212             return c;
40213         }
40214         return this.config.length;
40215     },
40216
40217     /**
40218      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40219      * @param {Function} fn
40220      * @param {Object} scope (optional)
40221      * @return {Array} result
40222      */
40223     getColumnsBy : function(fn, scope){
40224         var r = [];
40225         for(var i = 0, len = this.config.length; i < len; i++){
40226             var c = this.config[i];
40227             if(fn.call(scope||this, c, i) === true){
40228                 r[r.length] = c;
40229             }
40230         }
40231         return r;
40232     },
40233
40234     /**
40235      * Returns true if the specified column is sortable.
40236      * @param {Number} col The column index
40237      * @return {Boolean}
40238      */
40239     isSortable : function(col){
40240         if(typeof this.config[col].sortable == "undefined"){
40241             return this.defaultSortable;
40242         }
40243         return this.config[col].sortable;
40244     },
40245
40246     /**
40247      * Returns the rendering (formatting) function defined for the column.
40248      * @param {Number} col The column index.
40249      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40250      */
40251     getRenderer : function(col){
40252         if(!this.config[col].renderer){
40253             return Roo.grid.ColumnModel.defaultRenderer;
40254         }
40255         return this.config[col].renderer;
40256     },
40257
40258     /**
40259      * Sets the rendering (formatting) function for a column.
40260      * @param {Number} col The column index
40261      * @param {Function} fn The function to use to process the cell's raw data
40262      * to return HTML markup for the grid view. The render function is called with
40263      * the following parameters:<ul>
40264      * <li>Data value.</li>
40265      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40266      * <li>css A CSS style string to apply to the table cell.</li>
40267      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40268      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40269      * <li>Row index</li>
40270      * <li>Column index</li>
40271      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40272      */
40273     setRenderer : function(col, fn){
40274         this.config[col].renderer = fn;
40275     },
40276
40277     /**
40278      * Returns the width for the specified column.
40279      * @param {Number} col The column index
40280      * @param (optional) {String} gridSize bootstrap width size.
40281      * @return {Number}
40282      */
40283     getColumnWidth : function(col, gridSize)
40284         {
40285                 var cfg = this.config[col];
40286                 
40287                 if (typeof(gridSize) == 'undefined') {
40288                         return cfg.width * 1 || this.defaultWidth;
40289                 }
40290                 if (gridSize === false) { // if we set it..
40291                         return cfg.width || false;
40292                 }
40293                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40294                 
40295                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40296                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40297                                 continue;
40298                         }
40299                         return cfg[ sizes[i] ];
40300                 }
40301                 return 1;
40302                 
40303     },
40304
40305     /**
40306      * Sets the width for a column.
40307      * @param {Number} col The column index
40308      * @param {Number} width The new width
40309      */
40310     setColumnWidth : function(col, width, suppressEvent){
40311         this.config[col].width = width;
40312         this.totalWidth = null;
40313         if(!suppressEvent){
40314              this.fireEvent("widthchange", this, col, width);
40315         }
40316     },
40317
40318     /**
40319      * Returns the total width of all columns.
40320      * @param {Boolean} includeHidden True to include hidden column widths
40321      * @return {Number}
40322      */
40323     getTotalWidth : function(includeHidden){
40324         if(!this.totalWidth){
40325             this.totalWidth = 0;
40326             for(var i = 0, len = this.config.length; i < len; i++){
40327                 if(includeHidden || !this.isHidden(i)){
40328                     this.totalWidth += this.getColumnWidth(i);
40329                 }
40330             }
40331         }
40332         return this.totalWidth;
40333     },
40334
40335     /**
40336      * Returns the header for the specified column.
40337      * @param {Number} col The column index
40338      * @return {String}
40339      */
40340     getColumnHeader : function(col){
40341         return this.config[col].header;
40342     },
40343
40344     /**
40345      * Sets the header for a column.
40346      * @param {Number} col The column index
40347      * @param {String} header The new header
40348      */
40349     setColumnHeader : function(col, header){
40350         this.config[col].header = header;
40351         this.fireEvent("headerchange", this, col, header);
40352     },
40353
40354     /**
40355      * Returns the tooltip for the specified column.
40356      * @param {Number} col The column index
40357      * @return {String}
40358      */
40359     getColumnTooltip : function(col){
40360             return this.config[col].tooltip;
40361     },
40362     /**
40363      * Sets the tooltip for a column.
40364      * @param {Number} col The column index
40365      * @param {String} tooltip The new tooltip
40366      */
40367     setColumnTooltip : function(col, tooltip){
40368             this.config[col].tooltip = tooltip;
40369     },
40370
40371     /**
40372      * Returns the dataIndex for the specified column.
40373      * @param {Number} col The column index
40374      * @return {Number}
40375      */
40376     getDataIndex : function(col){
40377         return this.config[col].dataIndex;
40378     },
40379
40380     /**
40381      * Sets the dataIndex for a column.
40382      * @param {Number} col The column index
40383      * @param {Number} dataIndex The new dataIndex
40384      */
40385     setDataIndex : function(col, dataIndex){
40386         this.config[col].dataIndex = dataIndex;
40387     },
40388
40389     
40390     
40391     /**
40392      * Returns true if the cell is editable.
40393      * @param {Number} colIndex The column index
40394      * @param {Number} rowIndex The row index - this is nto actually used..?
40395      * @return {Boolean}
40396      */
40397     isCellEditable : function(colIndex, rowIndex){
40398         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40399     },
40400
40401     /**
40402      * Returns the editor defined for the cell/column.
40403      * return false or null to disable editing.
40404      * @param {Number} colIndex The column index
40405      * @param {Number} rowIndex The row index
40406      * @return {Object}
40407      */
40408     getCellEditor : function(colIndex, rowIndex){
40409         return this.config[colIndex].editor;
40410     },
40411
40412     /**
40413      * Sets if a column is editable.
40414      * @param {Number} col The column index
40415      * @param {Boolean} editable True if the column is editable
40416      */
40417     setEditable : function(col, editable){
40418         this.config[col].editable = editable;
40419     },
40420
40421
40422     /**
40423      * Returns true if the column is hidden.
40424      * @param {Number} colIndex The column index
40425      * @return {Boolean}
40426      */
40427     isHidden : function(colIndex){
40428         return this.config[colIndex].hidden;
40429     },
40430
40431
40432     /**
40433      * Returns true if the column width cannot be changed
40434      */
40435     isFixed : function(colIndex){
40436         return this.config[colIndex].fixed;
40437     },
40438
40439     /**
40440      * Returns true if the column can be resized
40441      * @return {Boolean}
40442      */
40443     isResizable : function(colIndex){
40444         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40445     },
40446     /**
40447      * Sets if a column is hidden.
40448      * @param {Number} colIndex The column index
40449      * @param {Boolean} hidden True if the column is hidden
40450      */
40451     setHidden : function(colIndex, hidden){
40452         this.config[colIndex].hidden = hidden;
40453         this.totalWidth = null;
40454         this.fireEvent("hiddenchange", this, colIndex, hidden);
40455     },
40456
40457     /**
40458      * Sets the editor for a column.
40459      * @param {Number} col The column index
40460      * @param {Object} editor The editor object
40461      */
40462     setEditor : function(col, editor){
40463         this.config[col].editor = editor;
40464     },
40465     /**
40466      * Add a column (experimental...) - defaults to adding to the end..
40467      * @param {Object} config 
40468     */
40469     addColumn : function(c)
40470     {
40471     
40472         var i = this.config.length;
40473         this.config[i] = c;
40474         
40475         if(typeof c.dataIndex == "undefined"){
40476             c.dataIndex = i;
40477         }
40478         if(typeof c.renderer == "string"){
40479             c.renderer = Roo.util.Format[c.renderer];
40480         }
40481         if(typeof c.id == "undefined"){
40482             c.id = Roo.id();
40483         }
40484         if(c.editor && c.editor.xtype){
40485             c.editor  = Roo.factory(c.editor, Roo.grid);
40486         }
40487         if(c.editor && c.editor.isFormField){
40488             c.editor = new Roo.grid.GridEditor(c.editor);
40489         }
40490         this.lookup[c.id] = c;
40491     }
40492     
40493 });
40494
40495 Roo.grid.ColumnModel.defaultRenderer = function(value)
40496 {
40497     if(typeof value == "object") {
40498         return value;
40499     }
40500         if(typeof value == "string" && value.length < 1){
40501             return "&#160;";
40502         }
40503     
40504         return String.format("{0}", value);
40505 };
40506
40507 // Alias for backwards compatibility
40508 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40509 /*
40510  * Based on:
40511  * Ext JS Library 1.1.1
40512  * Copyright(c) 2006-2007, Ext JS, LLC.
40513  *
40514  * Originally Released Under LGPL - original licence link has changed is not relivant.
40515  *
40516  * Fork - LGPL
40517  * <script type="text/javascript">
40518  */
40519
40520 /**
40521  * @class Roo.grid.AbstractSelectionModel
40522  * @extends Roo.util.Observable
40523  * @abstract
40524  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40525  * implemented by descendant classes.  This class should not be directly instantiated.
40526  * @constructor
40527  */
40528 Roo.grid.AbstractSelectionModel = function(){
40529     this.locked = false;
40530     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40531 };
40532
40533 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40534     /** @ignore Called by the grid automatically. Do not call directly. */
40535     init : function(grid){
40536         this.grid = grid;
40537         this.initEvents();
40538     },
40539
40540     /**
40541      * Locks the selections.
40542      */
40543     lock : function(){
40544         this.locked = true;
40545     },
40546
40547     /**
40548      * Unlocks the selections.
40549      */
40550     unlock : function(){
40551         this.locked = false;
40552     },
40553
40554     /**
40555      * Returns true if the selections are locked.
40556      * @return {Boolean}
40557      */
40558     isLocked : function(){
40559         return this.locked;
40560     }
40561 });/*
40562  * Based on:
40563  * Ext JS Library 1.1.1
40564  * Copyright(c) 2006-2007, Ext JS, LLC.
40565  *
40566  * Originally Released Under LGPL - original licence link has changed is not relivant.
40567  *
40568  * Fork - LGPL
40569  * <script type="text/javascript">
40570  */
40571 /**
40572  * @extends Roo.grid.AbstractSelectionModel
40573  * @class Roo.grid.RowSelectionModel
40574  * The default SelectionModel used by {@link Roo.grid.Grid}.
40575  * It supports multiple selections and keyboard selection/navigation. 
40576  * @constructor
40577  * @param {Object} config
40578  */
40579 Roo.grid.RowSelectionModel = function(config){
40580     Roo.apply(this, config);
40581     this.selections = new Roo.util.MixedCollection(false, function(o){
40582         return o.id;
40583     });
40584
40585     this.last = false;
40586     this.lastActive = false;
40587
40588     this.addEvents({
40589         /**
40590         * @event selectionchange
40591         * Fires when the selection changes
40592         * @param {SelectionModel} this
40593         */
40594        "selectionchange" : true,
40595        /**
40596         * @event afterselectionchange
40597         * Fires after the selection changes (eg. by key press or clicking)
40598         * @param {SelectionModel} this
40599         */
40600        "afterselectionchange" : true,
40601        /**
40602         * @event beforerowselect
40603         * Fires when a row is selected being selected, return false to cancel.
40604         * @param {SelectionModel} this
40605         * @param {Number} rowIndex The selected index
40606         * @param {Boolean} keepExisting False if other selections will be cleared
40607         */
40608        "beforerowselect" : true,
40609        /**
40610         * @event rowselect
40611         * Fires when a row is selected.
40612         * @param {SelectionModel} this
40613         * @param {Number} rowIndex The selected index
40614         * @param {Roo.data.Record} r The record
40615         */
40616        "rowselect" : true,
40617        /**
40618         * @event rowdeselect
40619         * Fires when a row is deselected.
40620         * @param {SelectionModel} this
40621         * @param {Number} rowIndex The selected index
40622         */
40623         "rowdeselect" : true
40624     });
40625     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40626     this.locked = false;
40627 };
40628
40629 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40630     /**
40631      * @cfg {Boolean} singleSelect
40632      * True to allow selection of only one row at a time (defaults to false)
40633      */
40634     singleSelect : false,
40635
40636     // private
40637     initEvents : function(){
40638
40639         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40640             this.grid.on("mousedown", this.handleMouseDown, this);
40641         }else{ // allow click to work like normal
40642             this.grid.on("rowclick", this.handleDragableRowClick, this);
40643         }
40644         // bootstrap does not have a view..
40645         var view = this.grid.view ? this.grid.view : this.grid;
40646         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40647             "up" : function(e){
40648                 if(!e.shiftKey){
40649                     this.selectPrevious(e.shiftKey);
40650                 }else if(this.last !== false && this.lastActive !== false){
40651                     var last = this.last;
40652                     this.selectRange(this.last,  this.lastActive-1);
40653                     view.focusRow(this.lastActive);
40654                     if(last !== false){
40655                         this.last = last;
40656                     }
40657                 }else{
40658                     this.selectFirstRow();
40659                 }
40660                 this.fireEvent("afterselectionchange", this);
40661             },
40662             "down" : function(e){
40663                 if(!e.shiftKey){
40664                     this.selectNext(e.shiftKey);
40665                 }else if(this.last !== false && this.lastActive !== false){
40666                     var last = this.last;
40667                     this.selectRange(this.last,  this.lastActive+1);
40668                     view.focusRow(this.lastActive);
40669                     if(last !== false){
40670                         this.last = last;
40671                     }
40672                 }else{
40673                     this.selectFirstRow();
40674                 }
40675                 this.fireEvent("afterselectionchange", this);
40676             },
40677             scope: this
40678         });
40679
40680          
40681         view.on("refresh", this.onRefresh, this);
40682         view.on("rowupdated", this.onRowUpdated, this);
40683         view.on("rowremoved", this.onRemove, this);
40684     },
40685
40686     // private
40687     onRefresh : function(){
40688         var ds = this.grid.ds, i, v = this.grid.view;
40689         var s = this.selections;
40690         s.each(function(r){
40691             if((i = ds.indexOfId(r.id)) != -1){
40692                 v.onRowSelect(i);
40693                 s.add(ds.getAt(i)); // updating the selection relate data
40694             }else{
40695                 s.remove(r);
40696             }
40697         });
40698     },
40699
40700     // private
40701     onRemove : function(v, index, r){
40702         this.selections.remove(r);
40703     },
40704
40705     // private
40706     onRowUpdated : function(v, index, r){
40707         if(this.isSelected(r)){
40708             v.onRowSelect(index);
40709         }
40710     },
40711
40712     /**
40713      * Select records.
40714      * @param {Array} records The records to select
40715      * @param {Boolean} keepExisting (optional) True to keep existing selections
40716      */
40717     selectRecords : function(records, keepExisting){
40718         if(!keepExisting){
40719             this.clearSelections();
40720         }
40721         var ds = this.grid.ds;
40722         for(var i = 0, len = records.length; i < len; i++){
40723             this.selectRow(ds.indexOf(records[i]), true);
40724         }
40725     },
40726
40727     /**
40728      * Gets the number of selected rows.
40729      * @return {Number}
40730      */
40731     getCount : function(){
40732         return this.selections.length;
40733     },
40734
40735     /**
40736      * Selects the first row in the grid.
40737      */
40738     selectFirstRow : function(){
40739         this.selectRow(0);
40740     },
40741
40742     /**
40743      * Select the last row.
40744      * @param {Boolean} keepExisting (optional) True to keep existing selections
40745      */
40746     selectLastRow : function(keepExisting){
40747         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40748     },
40749
40750     /**
40751      * Selects the row immediately following the last selected row.
40752      * @param {Boolean} keepExisting (optional) True to keep existing selections
40753      */
40754     selectNext : function(keepExisting){
40755         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40756             this.selectRow(this.last+1, keepExisting);
40757             var view = this.grid.view ? this.grid.view : this.grid;
40758             view.focusRow(this.last);
40759         }
40760     },
40761
40762     /**
40763      * Selects the row that precedes the last selected row.
40764      * @param {Boolean} keepExisting (optional) True to keep existing selections
40765      */
40766     selectPrevious : function(keepExisting){
40767         if(this.last){
40768             this.selectRow(this.last-1, keepExisting);
40769             var view = this.grid.view ? this.grid.view : this.grid;
40770             view.focusRow(this.last);
40771         }
40772     },
40773
40774     /**
40775      * Returns the selected records
40776      * @return {Array} Array of selected records
40777      */
40778     getSelections : function(){
40779         return [].concat(this.selections.items);
40780     },
40781
40782     /**
40783      * Returns the first selected record.
40784      * @return {Record}
40785      */
40786     getSelected : function(){
40787         return this.selections.itemAt(0);
40788     },
40789
40790
40791     /**
40792      * Clears all selections.
40793      */
40794     clearSelections : function(fast){
40795         if(this.locked) {
40796             return;
40797         }
40798         if(fast !== true){
40799             var ds = this.grid.ds;
40800             var s = this.selections;
40801             s.each(function(r){
40802                 this.deselectRow(ds.indexOfId(r.id));
40803             }, this);
40804             s.clear();
40805         }else{
40806             this.selections.clear();
40807         }
40808         this.last = false;
40809     },
40810
40811
40812     /**
40813      * Selects all rows.
40814      */
40815     selectAll : function(){
40816         if(this.locked) {
40817             return;
40818         }
40819         this.selections.clear();
40820         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40821             this.selectRow(i, true);
40822         }
40823     },
40824
40825     /**
40826      * Returns True if there is a selection.
40827      * @return {Boolean}
40828      */
40829     hasSelection : function(){
40830         return this.selections.length > 0;
40831     },
40832
40833     /**
40834      * Returns True if the specified row is selected.
40835      * @param {Number/Record} record The record or index of the record to check
40836      * @return {Boolean}
40837      */
40838     isSelected : function(index){
40839         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40840         return (r && this.selections.key(r.id) ? true : false);
40841     },
40842
40843     /**
40844      * Returns True if the specified record id is selected.
40845      * @param {String} id The id of record to check
40846      * @return {Boolean}
40847      */
40848     isIdSelected : function(id){
40849         return (this.selections.key(id) ? true : false);
40850     },
40851
40852     // private
40853     handleMouseDown : function(e, t)
40854     {
40855         var view = this.grid.view ? this.grid.view : this.grid;
40856         var rowIndex;
40857         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40858             return;
40859         };
40860         if(e.shiftKey && this.last !== false){
40861             var last = this.last;
40862             this.selectRange(last, rowIndex, e.ctrlKey);
40863             this.last = last; // reset the last
40864             view.focusRow(rowIndex);
40865         }else{
40866             var isSelected = this.isSelected(rowIndex);
40867             if(e.button !== 0 && isSelected){
40868                 view.focusRow(rowIndex);
40869             }else if(e.ctrlKey && isSelected){
40870                 this.deselectRow(rowIndex);
40871             }else if(!isSelected){
40872                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40873                 view.focusRow(rowIndex);
40874             }
40875         }
40876         this.fireEvent("afterselectionchange", this);
40877     },
40878     // private
40879     handleDragableRowClick :  function(grid, rowIndex, e) 
40880     {
40881         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40882             this.selectRow(rowIndex, false);
40883             var view = this.grid.view ? this.grid.view : this.grid;
40884             view.focusRow(rowIndex);
40885              this.fireEvent("afterselectionchange", this);
40886         }
40887     },
40888     
40889     /**
40890      * Selects multiple rows.
40891      * @param {Array} rows Array of the indexes of the row to select
40892      * @param {Boolean} keepExisting (optional) True to keep existing selections
40893      */
40894     selectRows : function(rows, keepExisting){
40895         if(!keepExisting){
40896             this.clearSelections();
40897         }
40898         for(var i = 0, len = rows.length; i < len; i++){
40899             this.selectRow(rows[i], true);
40900         }
40901     },
40902
40903     /**
40904      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40905      * @param {Number} startRow The index of the first row in the range
40906      * @param {Number} endRow The index of the last row in the range
40907      * @param {Boolean} keepExisting (optional) True to retain existing selections
40908      */
40909     selectRange : function(startRow, endRow, keepExisting){
40910         if(this.locked) {
40911             return;
40912         }
40913         if(!keepExisting){
40914             this.clearSelections();
40915         }
40916         if(startRow <= endRow){
40917             for(var i = startRow; i <= endRow; i++){
40918                 this.selectRow(i, true);
40919             }
40920         }else{
40921             for(var i = startRow; i >= endRow; i--){
40922                 this.selectRow(i, true);
40923             }
40924         }
40925     },
40926
40927     /**
40928      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40929      * @param {Number} startRow The index of the first row in the range
40930      * @param {Number} endRow The index of the last row in the range
40931      */
40932     deselectRange : function(startRow, endRow, preventViewNotify){
40933         if(this.locked) {
40934             return;
40935         }
40936         for(var i = startRow; i <= endRow; i++){
40937             this.deselectRow(i, preventViewNotify);
40938         }
40939     },
40940
40941     /**
40942      * Selects a row.
40943      * @param {Number} row The index of the row to select
40944      * @param {Boolean} keepExisting (optional) True to keep existing selections
40945      */
40946     selectRow : function(index, keepExisting, preventViewNotify){
40947         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40948             return;
40949         }
40950         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40951             if(!keepExisting || this.singleSelect){
40952                 this.clearSelections();
40953             }
40954             var r = this.grid.ds.getAt(index);
40955             this.selections.add(r);
40956             this.last = this.lastActive = index;
40957             if(!preventViewNotify){
40958                 var view = this.grid.view ? this.grid.view : this.grid;
40959                 view.onRowSelect(index);
40960             }
40961             this.fireEvent("rowselect", this, index, r);
40962             this.fireEvent("selectionchange", this);
40963         }
40964     },
40965
40966     /**
40967      * Deselects a row.
40968      * @param {Number} row The index of the row to deselect
40969      */
40970     deselectRow : function(index, preventViewNotify){
40971         if(this.locked) {
40972             return;
40973         }
40974         if(this.last == index){
40975             this.last = false;
40976         }
40977         if(this.lastActive == index){
40978             this.lastActive = false;
40979         }
40980         var r = this.grid.ds.getAt(index);
40981         this.selections.remove(r);
40982         if(!preventViewNotify){
40983             var view = this.grid.view ? this.grid.view : this.grid;
40984             view.onRowDeselect(index);
40985         }
40986         this.fireEvent("rowdeselect", this, index);
40987         this.fireEvent("selectionchange", this);
40988     },
40989
40990     // private
40991     restoreLast : function(){
40992         if(this._last){
40993             this.last = this._last;
40994         }
40995     },
40996
40997     // private
40998     acceptsNav : function(row, col, cm){
40999         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41000     },
41001
41002     // private
41003     onEditorKey : function(field, e){
41004         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
41005         if(k == e.TAB){
41006             e.stopEvent();
41007             ed.completeEdit();
41008             if(e.shiftKey){
41009                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41010             }else{
41011                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41012             }
41013         }else if(k == e.ENTER && !e.ctrlKey){
41014             e.stopEvent();
41015             ed.completeEdit();
41016             if(e.shiftKey){
41017                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
41018             }else{
41019                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
41020             }
41021         }else if(k == e.ESC){
41022             ed.cancelEdit();
41023         }
41024         if(newCell){
41025             g.startEditing(newCell[0], newCell[1]);
41026         }
41027     }
41028 });/*
41029  * Based on:
41030  * Ext JS Library 1.1.1
41031  * Copyright(c) 2006-2007, Ext JS, LLC.
41032  *
41033  * Originally Released Under LGPL - original licence link has changed is not relivant.
41034  *
41035  * Fork - LGPL
41036  * <script type="text/javascript">
41037  */
41038 /**
41039  * @class Roo.grid.CellSelectionModel
41040  * @extends Roo.grid.AbstractSelectionModel
41041  * This class provides the basic implementation for cell selection in a grid.
41042  * @constructor
41043  * @param {Object} config The object containing the configuration of this model.
41044  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
41045  */
41046 Roo.grid.CellSelectionModel = function(config){
41047     Roo.apply(this, config);
41048
41049     this.selection = null;
41050
41051     this.addEvents({
41052         /**
41053              * @event beforerowselect
41054              * Fires before a cell is selected.
41055              * @param {SelectionModel} this
41056              * @param {Number} rowIndex The selected row index
41057              * @param {Number} colIndex The selected cell index
41058              */
41059             "beforecellselect" : true,
41060         /**
41061              * @event cellselect
41062              * Fires when a cell is selected.
41063              * @param {SelectionModel} this
41064              * @param {Number} rowIndex The selected row index
41065              * @param {Number} colIndex The selected cell index
41066              */
41067             "cellselect" : true,
41068         /**
41069              * @event selectionchange
41070              * Fires when the active selection changes.
41071              * @param {SelectionModel} this
41072              * @param {Object} selection null for no selection or an object (o) with two properties
41073                 <ul>
41074                 <li>o.record: the record object for the row the selection is in</li>
41075                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
41076                 </ul>
41077              */
41078             "selectionchange" : true,
41079         /**
41080              * @event tabend
41081              * Fires when the tab (or enter) was pressed on the last editable cell
41082              * You can use this to trigger add new row.
41083              * @param {SelectionModel} this
41084              */
41085             "tabend" : true,
41086          /**
41087              * @event beforeeditnext
41088              * Fires before the next editable sell is made active
41089              * You can use this to skip to another cell or fire the tabend
41090              *    if you set cell to false
41091              * @param {Object} eventdata object : { cell : [ row, col ] } 
41092              */
41093             "beforeeditnext" : true
41094     });
41095     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
41096 };
41097
41098 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
41099     
41100     enter_is_tab: false,
41101
41102     /** @ignore */
41103     initEvents : function(){
41104         this.grid.on("mousedown", this.handleMouseDown, this);
41105         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
41106         var view = this.grid.view;
41107         view.on("refresh", this.onViewChange, this);
41108         view.on("rowupdated", this.onRowUpdated, this);
41109         view.on("beforerowremoved", this.clearSelections, this);
41110         view.on("beforerowsinserted", this.clearSelections, this);
41111         if(this.grid.isEditor){
41112             this.grid.on("beforeedit", this.beforeEdit,  this);
41113         }
41114     },
41115
41116         //private
41117     beforeEdit : function(e){
41118         this.select(e.row, e.column, false, true, e.record);
41119     },
41120
41121         //private
41122     onRowUpdated : function(v, index, r){
41123         if(this.selection && this.selection.record == r){
41124             v.onCellSelect(index, this.selection.cell[1]);
41125         }
41126     },
41127
41128         //private
41129     onViewChange : function(){
41130         this.clearSelections(true);
41131     },
41132
41133         /**
41134          * Returns the currently selected cell,.
41135          * @return {Array} The selected cell (row, column) or null if none selected.
41136          */
41137     getSelectedCell : function(){
41138         return this.selection ? this.selection.cell : null;
41139     },
41140
41141     /**
41142      * Clears all selections.
41143      * @param {Boolean} true to prevent the gridview from being notified about the change.
41144      */
41145     clearSelections : function(preventNotify){
41146         var s = this.selection;
41147         if(s){
41148             if(preventNotify !== true){
41149                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
41150             }
41151             this.selection = null;
41152             this.fireEvent("selectionchange", this, null);
41153         }
41154     },
41155
41156     /**
41157      * Returns true if there is a selection.
41158      * @return {Boolean}
41159      */
41160     hasSelection : function(){
41161         return this.selection ? true : false;
41162     },
41163
41164     /** @ignore */
41165     handleMouseDown : function(e, t){
41166         var v = this.grid.getView();
41167         if(this.isLocked()){
41168             return;
41169         };
41170         var row = v.findRowIndex(t);
41171         var cell = v.findCellIndex(t);
41172         if(row !== false && cell !== false){
41173             this.select(row, cell);
41174         }
41175     },
41176
41177     /**
41178      * Selects a cell.
41179      * @param {Number} rowIndex
41180      * @param {Number} collIndex
41181      */
41182     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
41183         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
41184             this.clearSelections();
41185             r = r || this.grid.dataSource.getAt(rowIndex);
41186             this.selection = {
41187                 record : r,
41188                 cell : [rowIndex, colIndex]
41189             };
41190             if(!preventViewNotify){
41191                 var v = this.grid.getView();
41192                 v.onCellSelect(rowIndex, colIndex);
41193                 if(preventFocus !== true){
41194                     v.focusCell(rowIndex, colIndex);
41195                 }
41196             }
41197             this.fireEvent("cellselect", this, rowIndex, colIndex);
41198             this.fireEvent("selectionchange", this, this.selection);
41199         }
41200     },
41201
41202         //private
41203     isSelectable : function(rowIndex, colIndex, cm){
41204         return !cm.isHidden(colIndex);
41205     },
41206
41207     /** @ignore */
41208     handleKeyDown : function(e){
41209         //Roo.log('Cell Sel Model handleKeyDown');
41210         if(!e.isNavKeyPress()){
41211             return;
41212         }
41213         var g = this.grid, s = this.selection;
41214         if(!s){
41215             e.stopEvent();
41216             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
41217             if(cell){
41218                 this.select(cell[0], cell[1]);
41219             }
41220             return;
41221         }
41222         var sm = this;
41223         var walk = function(row, col, step){
41224             return g.walkCells(row, col, step, sm.isSelectable,  sm);
41225         };
41226         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41227         var newCell;
41228
41229       
41230
41231         switch(k){
41232             case e.TAB:
41233                 // handled by onEditorKey
41234                 if (g.isEditor && g.editing) {
41235                     return;
41236                 }
41237                 if(e.shiftKey) {
41238                     newCell = walk(r, c-1, -1);
41239                 } else {
41240                     newCell = walk(r, c+1, 1);
41241                 }
41242                 break;
41243             
41244             case e.DOWN:
41245                newCell = walk(r+1, c, 1);
41246                 break;
41247             
41248             case e.UP:
41249                 newCell = walk(r-1, c, -1);
41250                 break;
41251             
41252             case e.RIGHT:
41253                 newCell = walk(r, c+1, 1);
41254                 break;
41255             
41256             case e.LEFT:
41257                 newCell = walk(r, c-1, -1);
41258                 break;
41259             
41260             case e.ENTER:
41261                 
41262                 if(g.isEditor && !g.editing){
41263                    g.startEditing(r, c);
41264                    e.stopEvent();
41265                    return;
41266                 }
41267                 
41268                 
41269              break;
41270         };
41271         if(newCell){
41272             this.select(newCell[0], newCell[1]);
41273             e.stopEvent();
41274             
41275         }
41276     },
41277
41278     acceptsNav : function(row, col, cm){
41279         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41280     },
41281     /**
41282      * Selects a cell.
41283      * @param {Number} field (not used) - as it's normally used as a listener
41284      * @param {Number} e - event - fake it by using
41285      *
41286      * var e = Roo.EventObjectImpl.prototype;
41287      * e.keyCode = e.TAB
41288      *
41289      * 
41290      */
41291     onEditorKey : function(field, e){
41292         
41293         var k = e.getKey(),
41294             newCell,
41295             g = this.grid,
41296             ed = g.activeEditor,
41297             forward = false;
41298         ///Roo.log('onEditorKey' + k);
41299         
41300         
41301         if (this.enter_is_tab && k == e.ENTER) {
41302             k = e.TAB;
41303         }
41304         
41305         if(k == e.TAB){
41306             if(e.shiftKey){
41307                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41308             }else{
41309                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41310                 forward = true;
41311             }
41312             
41313             e.stopEvent();
41314             
41315         } else if(k == e.ENTER &&  !e.ctrlKey){
41316             ed.completeEdit();
41317             e.stopEvent();
41318             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41319         
41320                 } else if(k == e.ESC){
41321             ed.cancelEdit();
41322         }
41323                 
41324         if (newCell) {
41325             var ecall = { cell : newCell, forward : forward };
41326             this.fireEvent('beforeeditnext', ecall );
41327             newCell = ecall.cell;
41328                         forward = ecall.forward;
41329         }
41330                 
41331         if(newCell){
41332             //Roo.log('next cell after edit');
41333             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41334         } else if (forward) {
41335             // tabbed past last
41336             this.fireEvent.defer(100, this, ['tabend',this]);
41337         }
41338     }
41339 });/*
41340  * Based on:
41341  * Ext JS Library 1.1.1
41342  * Copyright(c) 2006-2007, Ext JS, LLC.
41343  *
41344  * Originally Released Under LGPL - original licence link has changed is not relivant.
41345  *
41346  * Fork - LGPL
41347  * <script type="text/javascript">
41348  */
41349  
41350 /**
41351  * @class Roo.grid.EditorGrid
41352  * @extends Roo.grid.Grid
41353  * Class for creating and editable grid.
41354  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41355  * The container MUST have some type of size defined for the grid to fill. The container will be 
41356  * automatically set to position relative if it isn't already.
41357  * @param {Object} dataSource The data model to bind to
41358  * @param {Object} colModel The column model with info about this grid's columns
41359  */
41360 Roo.grid.EditorGrid = function(container, config){
41361     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41362     this.getGridEl().addClass("xedit-grid");
41363
41364     if(!this.selModel){
41365         this.selModel = new Roo.grid.CellSelectionModel();
41366     }
41367
41368     this.activeEditor = null;
41369
41370         this.addEvents({
41371             /**
41372              * @event beforeedit
41373              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41374              * <ul style="padding:5px;padding-left:16px;">
41375              * <li>grid - This grid</li>
41376              * <li>record - The record being edited</li>
41377              * <li>field - The field name being edited</li>
41378              * <li>value - The value for the field being edited.</li>
41379              * <li>row - The grid row index</li>
41380              * <li>column - The grid column index</li>
41381              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41382              * </ul>
41383              * @param {Object} e An edit event (see above for description)
41384              */
41385             "beforeedit" : true,
41386             /**
41387              * @event afteredit
41388              * Fires after a cell is edited. <br />
41389              * <ul style="padding:5px;padding-left:16px;">
41390              * <li>grid - This grid</li>
41391              * <li>record - The record being edited</li>
41392              * <li>field - The field name being edited</li>
41393              * <li>value - The value being set</li>
41394              * <li>originalValue - The original value for the field, before the edit.</li>
41395              * <li>row - The grid row index</li>
41396              * <li>column - The grid column index</li>
41397              * </ul>
41398              * @param {Object} e An edit event (see above for description)
41399              */
41400             "afteredit" : true,
41401             /**
41402              * @event validateedit
41403              * Fires after a cell is edited, but before the value is set in the record. 
41404          * You can use this to modify the value being set in the field, Return false
41405              * to cancel the change. The edit event object has the following properties <br />
41406              * <ul style="padding:5px;padding-left:16px;">
41407          * <li>editor - This editor</li>
41408              * <li>grid - This grid</li>
41409              * <li>record - The record being edited</li>
41410              * <li>field - The field name being edited</li>
41411              * <li>value - The value being set</li>
41412              * <li>originalValue - The original value for the field, before the edit.</li>
41413              * <li>row - The grid row index</li>
41414              * <li>column - The grid column index</li>
41415              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41416              * </ul>
41417              * @param {Object} e An edit event (see above for description)
41418              */
41419             "validateedit" : true
41420         });
41421     this.on("bodyscroll", this.stopEditing,  this);
41422     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41423 };
41424
41425 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41426     /**
41427      * @cfg {Number} clicksToEdit
41428      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41429      */
41430     clicksToEdit: 2,
41431
41432     // private
41433     isEditor : true,
41434     // private
41435     trackMouseOver: false, // causes very odd FF errors
41436
41437     onCellDblClick : function(g, row, col){
41438         this.startEditing(row, col);
41439     },
41440
41441     onEditComplete : function(ed, value, startValue){
41442         this.editing = false;
41443         this.activeEditor = null;
41444         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41445         var r = ed.record;
41446         var field = this.colModel.getDataIndex(ed.col);
41447         var e = {
41448             grid: this,
41449             record: r,
41450             field: field,
41451             originalValue: startValue,
41452             value: value,
41453             row: ed.row,
41454             column: ed.col,
41455             cancel:false,
41456             editor: ed
41457         };
41458         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41459         cell.show();
41460           
41461         if(String(value) !== String(startValue)){
41462             
41463             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41464                 r.set(field, e.value);
41465                 // if we are dealing with a combo box..
41466                 // then we also set the 'name' colum to be the displayField
41467                 if (ed.field.displayField && ed.field.name) {
41468                     r.set(ed.field.name, ed.field.el.dom.value);
41469                 }
41470                 
41471                 delete e.cancel; //?? why!!!
41472                 this.fireEvent("afteredit", e);
41473             }
41474         } else {
41475             this.fireEvent("afteredit", e); // always fire it!
41476         }
41477         this.view.focusCell(ed.row, ed.col);
41478     },
41479
41480     /**
41481      * Starts editing the specified for the specified row/column
41482      * @param {Number} rowIndex
41483      * @param {Number} colIndex
41484      */
41485     startEditing : function(row, col){
41486         this.stopEditing();
41487         if(this.colModel.isCellEditable(col, row)){
41488             this.view.ensureVisible(row, col, true);
41489           
41490             var r = this.dataSource.getAt(row);
41491             var field = this.colModel.getDataIndex(col);
41492             var cell = Roo.get(this.view.getCell(row,col));
41493             var e = {
41494                 grid: this,
41495                 record: r,
41496                 field: field,
41497                 value: r.data[field],
41498                 row: row,
41499                 column: col,
41500                 cancel:false 
41501             };
41502             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41503                 this.editing = true;
41504                 var ed = this.colModel.getCellEditor(col, row);
41505                 
41506                 if (!ed) {
41507                     return;
41508                 }
41509                 if(!ed.rendered){
41510                     ed.render(ed.parentEl || document.body);
41511                 }
41512                 ed.field.reset();
41513                
41514                 cell.hide();
41515                 
41516                 (function(){ // complex but required for focus issues in safari, ie and opera
41517                     ed.row = row;
41518                     ed.col = col;
41519                     ed.record = r;
41520                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41521                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41522                     this.activeEditor = ed;
41523                     var v = r.data[field];
41524                     ed.startEdit(this.view.getCell(row, col), v);
41525                     // combo's with 'displayField and name set
41526                     if (ed.field.displayField && ed.field.name) {
41527                         ed.field.el.dom.value = r.data[ed.field.name];
41528                     }
41529                     
41530                     
41531                 }).defer(50, this);
41532             }
41533         }
41534     },
41535         
41536     /**
41537      * Stops any active editing
41538      */
41539     stopEditing : function(){
41540         if(this.activeEditor){
41541             this.activeEditor.completeEdit();
41542         }
41543         this.activeEditor = null;
41544     },
41545         
41546          /**
41547      * Called to get grid's drag proxy text, by default returns this.ddText.
41548      * @return {String}
41549      */
41550     getDragDropText : function(){
41551         var count = this.selModel.getSelectedCell() ? 1 : 0;
41552         return String.format(this.ddText, count, count == 1 ? '' : 's');
41553     }
41554         
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 // private - not really -- you end up using it !
41567 // This is a support class used internally by the Grid components
41568
41569 /**
41570  * @class Roo.grid.GridEditor
41571  * @extends Roo.Editor
41572  * Class for creating and editable grid elements.
41573  * @param {Object} config any settings (must include field)
41574  */
41575 Roo.grid.GridEditor = function(field, config){
41576     if (!config && field.field) {
41577         config = field;
41578         field = Roo.factory(config.field, Roo.form);
41579     }
41580     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41581     field.monitorTab = false;
41582 };
41583
41584 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41585     
41586     /**
41587      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41588      */
41589     
41590     alignment: "tl-tl",
41591     autoSize: "width",
41592     hideEl : false,
41593     cls: "x-small-editor x-grid-editor",
41594     shim:false,
41595     shadow:"frame"
41596 });/*
41597  * Based on:
41598  * Ext JS Library 1.1.1
41599  * Copyright(c) 2006-2007, Ext JS, LLC.
41600  *
41601  * Originally Released Under LGPL - original licence link has changed is not relivant.
41602  *
41603  * Fork - LGPL
41604  * <script type="text/javascript">
41605  */
41606   
41607
41608   
41609 Roo.grid.PropertyRecord = Roo.data.Record.create([
41610     {name:'name',type:'string'},  'value'
41611 ]);
41612
41613
41614 Roo.grid.PropertyStore = function(grid, source){
41615     this.grid = grid;
41616     this.store = new Roo.data.Store({
41617         recordType : Roo.grid.PropertyRecord
41618     });
41619     this.store.on('update', this.onUpdate,  this);
41620     if(source){
41621         this.setSource(source);
41622     }
41623     Roo.grid.PropertyStore.superclass.constructor.call(this);
41624 };
41625
41626
41627
41628 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41629     setSource : function(o){
41630         this.source = o;
41631         this.store.removeAll();
41632         var data = [];
41633         for(var k in o){
41634             if(this.isEditableValue(o[k])){
41635                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41636             }
41637         }
41638         this.store.loadRecords({records: data}, {}, true);
41639     },
41640
41641     onUpdate : function(ds, record, type){
41642         if(type == Roo.data.Record.EDIT){
41643             var v = record.data['value'];
41644             var oldValue = record.modified['value'];
41645             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41646                 this.source[record.id] = v;
41647                 record.commit();
41648                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41649             }else{
41650                 record.reject();
41651             }
41652         }
41653     },
41654
41655     getProperty : function(row){
41656        return this.store.getAt(row);
41657     },
41658
41659     isEditableValue: function(val){
41660         if(val && val instanceof Date){
41661             return true;
41662         }else if(typeof val == 'object' || typeof val == 'function'){
41663             return false;
41664         }
41665         return true;
41666     },
41667
41668     setValue : function(prop, value){
41669         this.source[prop] = value;
41670         this.store.getById(prop).set('value', value);
41671     },
41672
41673     getSource : function(){
41674         return this.source;
41675     }
41676 });
41677
41678 Roo.grid.PropertyColumnModel = function(grid, store){
41679     this.grid = grid;
41680     var g = Roo.grid;
41681     g.PropertyColumnModel.superclass.constructor.call(this, [
41682         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41683         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41684     ]);
41685     this.store = store;
41686     this.bselect = Roo.DomHelper.append(document.body, {
41687         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41688             {tag: 'option', value: 'true', html: 'true'},
41689             {tag: 'option', value: 'false', html: 'false'}
41690         ]
41691     });
41692     Roo.id(this.bselect);
41693     var f = Roo.form;
41694     this.editors = {
41695         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41696         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41697         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41698         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41699         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41700     };
41701     this.renderCellDelegate = this.renderCell.createDelegate(this);
41702     this.renderPropDelegate = this.renderProp.createDelegate(this);
41703 };
41704
41705 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41706     
41707     
41708     nameText : 'Name',
41709     valueText : 'Value',
41710     
41711     dateFormat : 'm/j/Y',
41712     
41713     
41714     renderDate : function(dateVal){
41715         return dateVal.dateFormat(this.dateFormat);
41716     },
41717
41718     renderBool : function(bVal){
41719         return bVal ? 'true' : 'false';
41720     },
41721
41722     isCellEditable : function(colIndex, rowIndex){
41723         return colIndex == 1;
41724     },
41725
41726     getRenderer : function(col){
41727         return col == 1 ?
41728             this.renderCellDelegate : this.renderPropDelegate;
41729     },
41730
41731     renderProp : function(v){
41732         return this.getPropertyName(v);
41733     },
41734
41735     renderCell : function(val){
41736         var rv = val;
41737         if(val instanceof Date){
41738             rv = this.renderDate(val);
41739         }else if(typeof val == 'boolean'){
41740             rv = this.renderBool(val);
41741         }
41742         return Roo.util.Format.htmlEncode(rv);
41743     },
41744
41745     getPropertyName : function(name){
41746         var pn = this.grid.propertyNames;
41747         return pn && pn[name] ? pn[name] : name;
41748     },
41749
41750     getCellEditor : function(colIndex, rowIndex){
41751         var p = this.store.getProperty(rowIndex);
41752         var n = p.data['name'], val = p.data['value'];
41753         
41754         if(typeof(this.grid.customEditors[n]) == 'string'){
41755             return this.editors[this.grid.customEditors[n]];
41756         }
41757         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41758             return this.grid.customEditors[n];
41759         }
41760         if(val instanceof Date){
41761             return this.editors['date'];
41762         }else if(typeof val == 'number'){
41763             return this.editors['number'];
41764         }else if(typeof val == 'boolean'){
41765             return this.editors['boolean'];
41766         }else{
41767             return this.editors['string'];
41768         }
41769     }
41770 });
41771
41772 /**
41773  * @class Roo.grid.PropertyGrid
41774  * @extends Roo.grid.EditorGrid
41775  * This class represents the  interface of a component based property grid control.
41776  * <br><br>Usage:<pre><code>
41777  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41778       
41779  });
41780  // set any options
41781  grid.render();
41782  * </code></pre>
41783   
41784  * @constructor
41785  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41786  * The container MUST have some type of size defined for the grid to fill. The container will be
41787  * automatically set to position relative if it isn't already.
41788  * @param {Object} config A config object that sets properties on this grid.
41789  */
41790 Roo.grid.PropertyGrid = function(container, config){
41791     config = config || {};
41792     var store = new Roo.grid.PropertyStore(this);
41793     this.store = store;
41794     var cm = new Roo.grid.PropertyColumnModel(this, store);
41795     store.store.sort('name', 'ASC');
41796     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41797         ds: store.store,
41798         cm: cm,
41799         enableColLock:false,
41800         enableColumnMove:false,
41801         stripeRows:false,
41802         trackMouseOver: false,
41803         clicksToEdit:1
41804     }, config));
41805     this.getGridEl().addClass('x-props-grid');
41806     this.lastEditRow = null;
41807     this.on('columnresize', this.onColumnResize, this);
41808     this.addEvents({
41809          /**
41810              * @event beforepropertychange
41811              * Fires before a property changes (return false to stop?)
41812              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41813              * @param {String} id Record Id
41814              * @param {String} newval New Value
41815          * @param {String} oldval Old Value
41816              */
41817         "beforepropertychange": true,
41818         /**
41819              * @event propertychange
41820              * Fires after a property changes
41821              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41822              * @param {String} id Record Id
41823              * @param {String} newval New Value
41824          * @param {String} oldval Old Value
41825              */
41826         "propertychange": true
41827     });
41828     this.customEditors = this.customEditors || {};
41829 };
41830 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41831     
41832      /**
41833      * @cfg {Object} customEditors map of colnames=> custom editors.
41834      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41835      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41836      * false disables editing of the field.
41837          */
41838     
41839       /**
41840      * @cfg {Object} propertyNames map of property Names to their displayed value
41841          */
41842     
41843     render : function(){
41844         Roo.grid.PropertyGrid.superclass.render.call(this);
41845         this.autoSize.defer(100, this);
41846     },
41847
41848     autoSize : function(){
41849         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41850         if(this.view){
41851             this.view.fitColumns();
41852         }
41853     },
41854
41855     onColumnResize : function(){
41856         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41857         this.autoSize();
41858     },
41859     /**
41860      * Sets the data for the Grid
41861      * accepts a Key => Value object of all the elements avaiable.
41862      * @param {Object} data  to appear in grid.
41863      */
41864     setSource : function(source){
41865         this.store.setSource(source);
41866         //this.autoSize();
41867     },
41868     /**
41869      * Gets all the data from the grid.
41870      * @return {Object} data  data stored in grid
41871      */
41872     getSource : function(){
41873         return this.store.getSource();
41874     }
41875 });/*
41876   
41877  * Licence LGPL
41878  
41879  */
41880  
41881 /**
41882  * @class Roo.grid.Calendar
41883  * @extends Roo.grid.Grid
41884  * This class extends the Grid to provide a calendar widget
41885  * <br><br>Usage:<pre><code>
41886  var grid = new Roo.grid.Calendar("my-container-id", {
41887      ds: myDataStore,
41888      cm: myColModel,
41889      selModel: mySelectionModel,
41890      autoSizeColumns: true,
41891      monitorWindowResize: false,
41892      trackMouseOver: true
41893      eventstore : real data store..
41894  });
41895  // set any options
41896  grid.render();
41897   
41898   * @constructor
41899  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41900  * The container MUST have some type of size defined for the grid to fill. The container will be
41901  * automatically set to position relative if it isn't already.
41902  * @param {Object} config A config object that sets properties on this grid.
41903  */
41904 Roo.grid.Calendar = function(container, config){
41905         // initialize the container
41906         this.container = Roo.get(container);
41907         this.container.update("");
41908         this.container.setStyle("overflow", "hidden");
41909     this.container.addClass('x-grid-container');
41910
41911     this.id = this.container.id;
41912
41913     Roo.apply(this, config);
41914     // check and correct shorthanded configs
41915     
41916     var rows = [];
41917     var d =1;
41918     for (var r = 0;r < 6;r++) {
41919         
41920         rows[r]=[];
41921         for (var c =0;c < 7;c++) {
41922             rows[r][c]= '';
41923         }
41924     }
41925     if (this.eventStore) {
41926         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41927         this.eventStore.on('load',this.onLoad, this);
41928         this.eventStore.on('beforeload',this.clearEvents, this);
41929          
41930     }
41931     
41932     this.dataSource = new Roo.data.Store({
41933             proxy: new Roo.data.MemoryProxy(rows),
41934             reader: new Roo.data.ArrayReader({}, [
41935                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41936     });
41937
41938     this.dataSource.load();
41939     this.ds = this.dataSource;
41940     this.ds.xmodule = this.xmodule || false;
41941     
41942     
41943     var cellRender = function(v,x,r)
41944     {
41945         return String.format(
41946             '<div class="fc-day  fc-widget-content"><div>' +
41947                 '<div class="fc-event-container"></div>' +
41948                 '<div class="fc-day-number">{0}</div>'+
41949                 
41950                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41951             '</div></div>', v);
41952     
41953     }
41954     
41955     
41956     this.colModel = new Roo.grid.ColumnModel( [
41957         {
41958             xtype: 'ColumnModel',
41959             xns: Roo.grid,
41960             dataIndex : 'weekday0',
41961             header : 'Sunday',
41962             renderer : cellRender
41963         },
41964         {
41965             xtype: 'ColumnModel',
41966             xns: Roo.grid,
41967             dataIndex : 'weekday1',
41968             header : 'Monday',
41969             renderer : cellRender
41970         },
41971         {
41972             xtype: 'ColumnModel',
41973             xns: Roo.grid,
41974             dataIndex : 'weekday2',
41975             header : 'Tuesday',
41976             renderer : cellRender
41977         },
41978         {
41979             xtype: 'ColumnModel',
41980             xns: Roo.grid,
41981             dataIndex : 'weekday3',
41982             header : 'Wednesday',
41983             renderer : cellRender
41984         },
41985         {
41986             xtype: 'ColumnModel',
41987             xns: Roo.grid,
41988             dataIndex : 'weekday4',
41989             header : 'Thursday',
41990             renderer : cellRender
41991         },
41992         {
41993             xtype: 'ColumnModel',
41994             xns: Roo.grid,
41995             dataIndex : 'weekday5',
41996             header : 'Friday',
41997             renderer : cellRender
41998         },
41999         {
42000             xtype: 'ColumnModel',
42001             xns: Roo.grid,
42002             dataIndex : 'weekday6',
42003             header : 'Saturday',
42004             renderer : cellRender
42005         }
42006     ]);
42007     this.cm = this.colModel;
42008     this.cm.xmodule = this.xmodule || false;
42009  
42010         
42011           
42012     //this.selModel = new Roo.grid.CellSelectionModel();
42013     //this.sm = this.selModel;
42014     //this.selModel.init(this);
42015     
42016     
42017     if(this.width){
42018         this.container.setWidth(this.width);
42019     }
42020
42021     if(this.height){
42022         this.container.setHeight(this.height);
42023     }
42024     /** @private */
42025         this.addEvents({
42026         // raw events
42027         /**
42028          * @event click
42029          * The raw click event for the entire grid.
42030          * @param {Roo.EventObject} e
42031          */
42032         "click" : true,
42033         /**
42034          * @event dblclick
42035          * The raw dblclick event for the entire grid.
42036          * @param {Roo.EventObject} e
42037          */
42038         "dblclick" : true,
42039         /**
42040          * @event contextmenu
42041          * The raw contextmenu event for the entire grid.
42042          * @param {Roo.EventObject} e
42043          */
42044         "contextmenu" : true,
42045         /**
42046          * @event mousedown
42047          * The raw mousedown event for the entire grid.
42048          * @param {Roo.EventObject} e
42049          */
42050         "mousedown" : true,
42051         /**
42052          * @event mouseup
42053          * The raw mouseup event for the entire grid.
42054          * @param {Roo.EventObject} e
42055          */
42056         "mouseup" : true,
42057         /**
42058          * @event mouseover
42059          * The raw mouseover event for the entire grid.
42060          * @param {Roo.EventObject} e
42061          */
42062         "mouseover" : true,
42063         /**
42064          * @event mouseout
42065          * The raw mouseout event for the entire grid.
42066          * @param {Roo.EventObject} e
42067          */
42068         "mouseout" : true,
42069         /**
42070          * @event keypress
42071          * The raw keypress event for the entire grid.
42072          * @param {Roo.EventObject} e
42073          */
42074         "keypress" : true,
42075         /**
42076          * @event keydown
42077          * The raw keydown event for the entire grid.
42078          * @param {Roo.EventObject} e
42079          */
42080         "keydown" : true,
42081
42082         // custom events
42083
42084         /**
42085          * @event cellclick
42086          * Fires when a cell is clicked
42087          * @param {Grid} this
42088          * @param {Number} rowIndex
42089          * @param {Number} columnIndex
42090          * @param {Roo.EventObject} e
42091          */
42092         "cellclick" : true,
42093         /**
42094          * @event celldblclick
42095          * Fires when a cell is double clicked
42096          * @param {Grid} this
42097          * @param {Number} rowIndex
42098          * @param {Number} columnIndex
42099          * @param {Roo.EventObject} e
42100          */
42101         "celldblclick" : true,
42102         /**
42103          * @event rowclick
42104          * Fires when a row is clicked
42105          * @param {Grid} this
42106          * @param {Number} rowIndex
42107          * @param {Roo.EventObject} e
42108          */
42109         "rowclick" : true,
42110         /**
42111          * @event rowdblclick
42112          * Fires when a row is double clicked
42113          * @param {Grid} this
42114          * @param {Number} rowIndex
42115          * @param {Roo.EventObject} e
42116          */
42117         "rowdblclick" : true,
42118         /**
42119          * @event headerclick
42120          * Fires when a header is clicked
42121          * @param {Grid} this
42122          * @param {Number} columnIndex
42123          * @param {Roo.EventObject} e
42124          */
42125         "headerclick" : true,
42126         /**
42127          * @event headerdblclick
42128          * Fires when a header cell is double clicked
42129          * @param {Grid} this
42130          * @param {Number} columnIndex
42131          * @param {Roo.EventObject} e
42132          */
42133         "headerdblclick" : true,
42134         /**
42135          * @event rowcontextmenu
42136          * Fires when a row is right clicked
42137          * @param {Grid} this
42138          * @param {Number} rowIndex
42139          * @param {Roo.EventObject} e
42140          */
42141         "rowcontextmenu" : true,
42142         /**
42143          * @event cellcontextmenu
42144          * Fires when a cell is right clicked
42145          * @param {Grid} this
42146          * @param {Number} rowIndex
42147          * @param {Number} cellIndex
42148          * @param {Roo.EventObject} e
42149          */
42150          "cellcontextmenu" : true,
42151         /**
42152          * @event headercontextmenu
42153          * Fires when a header is right clicked
42154          * @param {Grid} this
42155          * @param {Number} columnIndex
42156          * @param {Roo.EventObject} e
42157          */
42158         "headercontextmenu" : true,
42159         /**
42160          * @event bodyscroll
42161          * Fires when the body element is scrolled
42162          * @param {Number} scrollLeft
42163          * @param {Number} scrollTop
42164          */
42165         "bodyscroll" : true,
42166         /**
42167          * @event columnresize
42168          * Fires when the user resizes a column
42169          * @param {Number} columnIndex
42170          * @param {Number} newSize
42171          */
42172         "columnresize" : true,
42173         /**
42174          * @event columnmove
42175          * Fires when the user moves a column
42176          * @param {Number} oldIndex
42177          * @param {Number} newIndex
42178          */
42179         "columnmove" : true,
42180         /**
42181          * @event startdrag
42182          * Fires when row(s) start being dragged
42183          * @param {Grid} this
42184          * @param {Roo.GridDD} dd The drag drop object
42185          * @param {event} e The raw browser event
42186          */
42187         "startdrag" : true,
42188         /**
42189          * @event enddrag
42190          * Fires when a drag operation is complete
42191          * @param {Grid} this
42192          * @param {Roo.GridDD} dd The drag drop object
42193          * @param {event} e The raw browser event
42194          */
42195         "enddrag" : true,
42196         /**
42197          * @event dragdrop
42198          * Fires when dragged row(s) are dropped on a valid DD target
42199          * @param {Grid} this
42200          * @param {Roo.GridDD} dd The drag drop object
42201          * @param {String} targetId The target drag drop object
42202          * @param {event} e The raw browser event
42203          */
42204         "dragdrop" : true,
42205         /**
42206          * @event dragover
42207          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42208          * @param {Grid} this
42209          * @param {Roo.GridDD} dd The drag drop object
42210          * @param {String} targetId The target drag drop object
42211          * @param {event} e The raw browser event
42212          */
42213         "dragover" : true,
42214         /**
42215          * @event dragenter
42216          *  Fires when the dragged row(s) first cross another DD target while being dragged
42217          * @param {Grid} this
42218          * @param {Roo.GridDD} dd The drag drop object
42219          * @param {String} targetId The target drag drop object
42220          * @param {event} e The raw browser event
42221          */
42222         "dragenter" : true,
42223         /**
42224          * @event dragout
42225          * Fires when the dragged row(s) leave another DD target while being dragged
42226          * @param {Grid} this
42227          * @param {Roo.GridDD} dd The drag drop object
42228          * @param {String} targetId The target drag drop object
42229          * @param {event} e The raw browser event
42230          */
42231         "dragout" : true,
42232         /**
42233          * @event rowclass
42234          * Fires when a row is rendered, so you can change add a style to it.
42235          * @param {GridView} gridview   The grid view
42236          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
42237          */
42238         'rowclass' : true,
42239
42240         /**
42241          * @event render
42242          * Fires when the grid is rendered
42243          * @param {Grid} grid
42244          */
42245         'render' : true,
42246             /**
42247              * @event select
42248              * Fires when a date is selected
42249              * @param {DatePicker} this
42250              * @param {Date} date The selected date
42251              */
42252         'select': true,
42253         /**
42254              * @event monthchange
42255              * Fires when the displayed month changes 
42256              * @param {DatePicker} this
42257              * @param {Date} date The selected month
42258              */
42259         'monthchange': true,
42260         /**
42261              * @event evententer
42262              * Fires when mouse over an event
42263              * @param {Calendar} this
42264              * @param {event} Event
42265              */
42266         'evententer': true,
42267         /**
42268              * @event eventleave
42269              * Fires when the mouse leaves an
42270              * @param {Calendar} this
42271              * @param {event}
42272              */
42273         'eventleave': true,
42274         /**
42275              * @event eventclick
42276              * Fires when the mouse click an
42277              * @param {Calendar} this
42278              * @param {event}
42279              */
42280         'eventclick': true,
42281         /**
42282              * @event eventrender
42283              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42284              * @param {Calendar} this
42285              * @param {data} data to be modified
42286              */
42287         'eventrender': true
42288         
42289     });
42290
42291     Roo.grid.Grid.superclass.constructor.call(this);
42292     this.on('render', function() {
42293         this.view.el.addClass('x-grid-cal'); 
42294         
42295         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42296
42297     },this);
42298     
42299     if (!Roo.grid.Calendar.style) {
42300         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42301             
42302             
42303             '.x-grid-cal .x-grid-col' :  {
42304                 height: 'auto !important',
42305                 'vertical-align': 'top'
42306             },
42307             '.x-grid-cal  .fc-event-hori' : {
42308                 height: '14px'
42309             }
42310              
42311             
42312         }, Roo.id());
42313     }
42314
42315     
42316     
42317 };
42318 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42319     /**
42320      * @cfg {Store} eventStore The store that loads events.
42321      */
42322     eventStore : 25,
42323
42324      
42325     activeDate : false,
42326     startDay : 0,
42327     autoWidth : true,
42328     monitorWindowResize : false,
42329
42330     
42331     resizeColumns : function() {
42332         var col = (this.view.el.getWidth() / 7) - 3;
42333         // loop through cols, and setWidth
42334         for(var i =0 ; i < 7 ; i++){
42335             this.cm.setColumnWidth(i, col);
42336         }
42337     },
42338      setDate :function(date) {
42339         
42340         Roo.log('setDate?');
42341         
42342         this.resizeColumns();
42343         var vd = this.activeDate;
42344         this.activeDate = date;
42345 //        if(vd && this.el){
42346 //            var t = date.getTime();
42347 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42348 //                Roo.log('using add remove');
42349 //                
42350 //                this.fireEvent('monthchange', this, date);
42351 //                
42352 //                this.cells.removeClass("fc-state-highlight");
42353 //                this.cells.each(function(c){
42354 //                   if(c.dateValue == t){
42355 //                       c.addClass("fc-state-highlight");
42356 //                       setTimeout(function(){
42357 //                            try{c.dom.firstChild.focus();}catch(e){}
42358 //                       }, 50);
42359 //                       return false;
42360 //                   }
42361 //                   return true;
42362 //                });
42363 //                return;
42364 //            }
42365 //        }
42366         
42367         var days = date.getDaysInMonth();
42368         
42369         var firstOfMonth = date.getFirstDateOfMonth();
42370         var startingPos = firstOfMonth.getDay()-this.startDay;
42371         
42372         if(startingPos < this.startDay){
42373             startingPos += 7;
42374         }
42375         
42376         var pm = date.add(Date.MONTH, -1);
42377         var prevStart = pm.getDaysInMonth()-startingPos;
42378 //        
42379         
42380         
42381         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42382         
42383         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42384         //this.cells.addClassOnOver('fc-state-hover');
42385         
42386         var cells = this.cells.elements;
42387         var textEls = this.textNodes;
42388         
42389         //Roo.each(cells, function(cell){
42390         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42391         //});
42392         
42393         days += startingPos;
42394
42395         // convert everything to numbers so it's fast
42396         var day = 86400000;
42397         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42398         //Roo.log(d);
42399         //Roo.log(pm);
42400         //Roo.log(prevStart);
42401         
42402         var today = new Date().clearTime().getTime();
42403         var sel = date.clearTime().getTime();
42404         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42405         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42406         var ddMatch = this.disabledDatesRE;
42407         var ddText = this.disabledDatesText;
42408         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42409         var ddaysText = this.disabledDaysText;
42410         var format = this.format;
42411         
42412         var setCellClass = function(cal, cell){
42413             
42414             //Roo.log('set Cell Class');
42415             cell.title = "";
42416             var t = d.getTime();
42417             
42418             //Roo.log(d);
42419             
42420             
42421             cell.dateValue = t;
42422             if(t == today){
42423                 cell.className += " fc-today";
42424                 cell.className += " fc-state-highlight";
42425                 cell.title = cal.todayText;
42426             }
42427             if(t == sel){
42428                 // disable highlight in other month..
42429                 cell.className += " fc-state-highlight";
42430                 
42431             }
42432             // disabling
42433             if(t < min) {
42434                 //cell.className = " fc-state-disabled";
42435                 cell.title = cal.minText;
42436                 return;
42437             }
42438             if(t > max) {
42439                 //cell.className = " fc-state-disabled";
42440                 cell.title = cal.maxText;
42441                 return;
42442             }
42443             if(ddays){
42444                 if(ddays.indexOf(d.getDay()) != -1){
42445                     // cell.title = ddaysText;
42446                    // cell.className = " fc-state-disabled";
42447                 }
42448             }
42449             if(ddMatch && format){
42450                 var fvalue = d.dateFormat(format);
42451                 if(ddMatch.test(fvalue)){
42452                     cell.title = ddText.replace("%0", fvalue);
42453                    cell.className = " fc-state-disabled";
42454                 }
42455             }
42456             
42457             if (!cell.initialClassName) {
42458                 cell.initialClassName = cell.dom.className;
42459             }
42460             
42461             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42462         };
42463
42464         var i = 0;
42465         
42466         for(; i < startingPos; i++) {
42467             cells[i].dayName =  (++prevStart);
42468             Roo.log(textEls[i]);
42469             d.setDate(d.getDate()+1);
42470             
42471             //cells[i].className = "fc-past fc-other-month";
42472             setCellClass(this, cells[i]);
42473         }
42474         
42475         var intDay = 0;
42476         
42477         for(; i < days; i++){
42478             intDay = i - startingPos + 1;
42479             cells[i].dayName =  (intDay);
42480             d.setDate(d.getDate()+1);
42481             
42482             cells[i].className = ''; // "x-date-active";
42483             setCellClass(this, cells[i]);
42484         }
42485         var extraDays = 0;
42486         
42487         for(; i < 42; i++) {
42488             //textEls[i].innerHTML = (++extraDays);
42489             
42490             d.setDate(d.getDate()+1);
42491             cells[i].dayName = (++extraDays);
42492             cells[i].className = "fc-future fc-other-month";
42493             setCellClass(this, cells[i]);
42494         }
42495         
42496         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42497         
42498         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42499         
42500         // this will cause all the cells to mis
42501         var rows= [];
42502         var i =0;
42503         for (var r = 0;r < 6;r++) {
42504             for (var c =0;c < 7;c++) {
42505                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42506             }    
42507         }
42508         
42509         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42510         for(i=0;i<cells.length;i++) {
42511             
42512             this.cells.elements[i].dayName = cells[i].dayName ;
42513             this.cells.elements[i].className = cells[i].className;
42514             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42515             this.cells.elements[i].title = cells[i].title ;
42516             this.cells.elements[i].dateValue = cells[i].dateValue ;
42517         }
42518         
42519         
42520         
42521         
42522         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42523         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42524         
42525         ////if(totalRows != 6){
42526             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42527            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42528        // }
42529         
42530         this.fireEvent('monthchange', this, date);
42531         
42532         
42533     },
42534  /**
42535      * Returns the grid's SelectionModel.
42536      * @return {SelectionModel}
42537      */
42538     getSelectionModel : function(){
42539         if(!this.selModel){
42540             this.selModel = new Roo.grid.CellSelectionModel();
42541         }
42542         return this.selModel;
42543     },
42544
42545     load: function() {
42546         this.eventStore.load()
42547         
42548         
42549         
42550     },
42551     
42552     findCell : function(dt) {
42553         dt = dt.clearTime().getTime();
42554         var ret = false;
42555         this.cells.each(function(c){
42556             //Roo.log("check " +c.dateValue + '?=' + dt);
42557             if(c.dateValue == dt){
42558                 ret = c;
42559                 return false;
42560             }
42561             return true;
42562         });
42563         
42564         return ret;
42565     },
42566     
42567     findCells : function(rec) {
42568         var s = rec.data.start_dt.clone().clearTime().getTime();
42569        // Roo.log(s);
42570         var e= rec.data.end_dt.clone().clearTime().getTime();
42571        // Roo.log(e);
42572         var ret = [];
42573         this.cells.each(function(c){
42574              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42575             
42576             if(c.dateValue > e){
42577                 return ;
42578             }
42579             if(c.dateValue < s){
42580                 return ;
42581             }
42582             ret.push(c);
42583         });
42584         
42585         return ret;    
42586     },
42587     
42588     findBestRow: function(cells)
42589     {
42590         var ret = 0;
42591         
42592         for (var i =0 ; i < cells.length;i++) {
42593             ret  = Math.max(cells[i].rows || 0,ret);
42594         }
42595         return ret;
42596         
42597     },
42598     
42599     
42600     addItem : function(rec)
42601     {
42602         // look for vertical location slot in
42603         var cells = this.findCells(rec);
42604         
42605         rec.row = this.findBestRow(cells);
42606         
42607         // work out the location.
42608         
42609         var crow = false;
42610         var rows = [];
42611         for(var i =0; i < cells.length; i++) {
42612             if (!crow) {
42613                 crow = {
42614                     start : cells[i],
42615                     end :  cells[i]
42616                 };
42617                 continue;
42618             }
42619             if (crow.start.getY() == cells[i].getY()) {
42620                 // on same row.
42621                 crow.end = cells[i];
42622                 continue;
42623             }
42624             // different row.
42625             rows.push(crow);
42626             crow = {
42627                 start: cells[i],
42628                 end : cells[i]
42629             };
42630             
42631         }
42632         
42633         rows.push(crow);
42634         rec.els = [];
42635         rec.rows = rows;
42636         rec.cells = cells;
42637         for (var i = 0; i < cells.length;i++) {
42638             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42639             
42640         }
42641         
42642         
42643     },
42644     
42645     clearEvents: function() {
42646         
42647         if (!this.eventStore.getCount()) {
42648             return;
42649         }
42650         // reset number of rows in cells.
42651         Roo.each(this.cells.elements, function(c){
42652             c.rows = 0;
42653         });
42654         
42655         this.eventStore.each(function(e) {
42656             this.clearEvent(e);
42657         },this);
42658         
42659     },
42660     
42661     clearEvent : function(ev)
42662     {
42663         if (ev.els) {
42664             Roo.each(ev.els, function(el) {
42665                 el.un('mouseenter' ,this.onEventEnter, this);
42666                 el.un('mouseleave' ,this.onEventLeave, this);
42667                 el.remove();
42668             },this);
42669             ev.els = [];
42670         }
42671     },
42672     
42673     
42674     renderEvent : function(ev,ctr) {
42675         if (!ctr) {
42676              ctr = this.view.el.select('.fc-event-container',true).first();
42677         }
42678         
42679          
42680         this.clearEvent(ev);
42681             //code
42682        
42683         
42684         
42685         ev.els = [];
42686         var cells = ev.cells;
42687         var rows = ev.rows;
42688         this.fireEvent('eventrender', this, ev);
42689         
42690         for(var i =0; i < rows.length; i++) {
42691             
42692             cls = '';
42693             if (i == 0) {
42694                 cls += ' fc-event-start';
42695             }
42696             if ((i+1) == rows.length) {
42697                 cls += ' fc-event-end';
42698             }
42699             
42700             //Roo.log(ev.data);
42701             // how many rows should it span..
42702             var cg = this.eventTmpl.append(ctr,Roo.apply({
42703                 fccls : cls
42704                 
42705             }, ev.data) , true);
42706             
42707             
42708             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42709             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42710             cg.on('click', this.onEventClick, this, ev);
42711             
42712             ev.els.push(cg);
42713             
42714             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42715             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42716             //Roo.log(cg);
42717              
42718             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42719             cg.setWidth(ebox.right - sbox.x -2);
42720         }
42721     },
42722     
42723     renderEvents: function()
42724     {   
42725         // first make sure there is enough space..
42726         
42727         if (!this.eventTmpl) {
42728             this.eventTmpl = new Roo.Template(
42729                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42730                     '<div class="fc-event-inner">' +
42731                         '<span class="fc-event-time">{time}</span>' +
42732                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42733                     '</div>' +
42734                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42735                 '</div>'
42736             );
42737                 
42738         }
42739                
42740         
42741         
42742         this.cells.each(function(c) {
42743             //Roo.log(c.select('.fc-day-content div',true).first());
42744             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42745         });
42746         
42747         var ctr = this.view.el.select('.fc-event-container',true).first();
42748         
42749         var cls;
42750         this.eventStore.each(function(ev){
42751             
42752             this.renderEvent(ev);
42753              
42754              
42755         }, this);
42756         this.view.layout();
42757         
42758     },
42759     
42760     onEventEnter: function (e, el,event,d) {
42761         this.fireEvent('evententer', this, el, event);
42762     },
42763     
42764     onEventLeave: function (e, el,event,d) {
42765         this.fireEvent('eventleave', this, el, event);
42766     },
42767     
42768     onEventClick: function (e, el,event,d) {
42769         this.fireEvent('eventclick', this, el, event);
42770     },
42771     
42772     onMonthChange: function () {
42773         this.store.load();
42774     },
42775     
42776     onLoad: function () {
42777         
42778         //Roo.log('calendar onload');
42779 //         
42780         if(this.eventStore.getCount() > 0){
42781             
42782            
42783             
42784             this.eventStore.each(function(d){
42785                 
42786                 
42787                 // FIXME..
42788                 var add =   d.data;
42789                 if (typeof(add.end_dt) == 'undefined')  {
42790                     Roo.log("Missing End time in calendar data: ");
42791                     Roo.log(d);
42792                     return;
42793                 }
42794                 if (typeof(add.start_dt) == 'undefined')  {
42795                     Roo.log("Missing Start time in calendar data: ");
42796                     Roo.log(d);
42797                     return;
42798                 }
42799                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42800                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42801                 add.id = add.id || d.id;
42802                 add.title = add.title || '??';
42803                 
42804                 this.addItem(d);
42805                 
42806              
42807             },this);
42808         }
42809         
42810         this.renderEvents();
42811     }
42812     
42813
42814 });
42815 /*
42816  grid : {
42817                 xtype: 'Grid',
42818                 xns: Roo.grid,
42819                 listeners : {
42820                     render : function ()
42821                     {
42822                         _this.grid = this;
42823                         
42824                         if (!this.view.el.hasClass('course-timesheet')) {
42825                             this.view.el.addClass('course-timesheet');
42826                         }
42827                         if (this.tsStyle) {
42828                             this.ds.load({});
42829                             return; 
42830                         }
42831                         Roo.log('width');
42832                         Roo.log(_this.grid.view.el.getWidth());
42833                         
42834                         
42835                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42836                             '.course-timesheet .x-grid-row' : {
42837                                 height: '80px'
42838                             },
42839                             '.x-grid-row td' : {
42840                                 'vertical-align' : 0
42841                             },
42842                             '.course-edit-link' : {
42843                                 'color' : 'blue',
42844                                 'text-overflow' : 'ellipsis',
42845                                 'overflow' : 'hidden',
42846                                 'white-space' : 'nowrap',
42847                                 'cursor' : 'pointer'
42848                             },
42849                             '.sub-link' : {
42850                                 'color' : 'green'
42851                             },
42852                             '.de-act-sup-link' : {
42853                                 'color' : 'purple',
42854                                 'text-decoration' : 'line-through'
42855                             },
42856                             '.de-act-link' : {
42857                                 'color' : 'red',
42858                                 'text-decoration' : 'line-through'
42859                             },
42860                             '.course-timesheet .course-highlight' : {
42861                                 'border-top-style': 'dashed !important',
42862                                 'border-bottom-bottom': 'dashed !important'
42863                             },
42864                             '.course-timesheet .course-item' : {
42865                                 'font-family'   : 'tahoma, arial, helvetica',
42866                                 'font-size'     : '11px',
42867                                 'overflow'      : 'hidden',
42868                                 'padding-left'  : '10px',
42869                                 'padding-right' : '10px',
42870                                 'padding-top' : '10px' 
42871                             }
42872                             
42873                         }, Roo.id());
42874                                 this.ds.load({});
42875                     }
42876                 },
42877                 autoWidth : true,
42878                 monitorWindowResize : false,
42879                 cellrenderer : function(v,x,r)
42880                 {
42881                     return v;
42882                 },
42883                 sm : {
42884                     xtype: 'CellSelectionModel',
42885                     xns: Roo.grid
42886                 },
42887                 dataSource : {
42888                     xtype: 'Store',
42889                     xns: Roo.data,
42890                     listeners : {
42891                         beforeload : function (_self, options)
42892                         {
42893                             options.params = options.params || {};
42894                             options.params._month = _this.monthField.getValue();
42895                             options.params.limit = 9999;
42896                             options.params['sort'] = 'when_dt';    
42897                             options.params['dir'] = 'ASC';    
42898                             this.proxy.loadResponse = this.loadResponse;
42899                             Roo.log("load?");
42900                             //this.addColumns();
42901                         },
42902                         load : function (_self, records, options)
42903                         {
42904                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42905                                 // if you click on the translation.. you can edit it...
42906                                 var el = Roo.get(this);
42907                                 var id = el.dom.getAttribute('data-id');
42908                                 var d = el.dom.getAttribute('data-date');
42909                                 var t = el.dom.getAttribute('data-time');
42910                                 //var id = this.child('span').dom.textContent;
42911                                 
42912                                 //Roo.log(this);
42913                                 Pman.Dialog.CourseCalendar.show({
42914                                     id : id,
42915                                     when_d : d,
42916                                     when_t : t,
42917                                     productitem_active : id ? 1 : 0
42918                                 }, function() {
42919                                     _this.grid.ds.load({});
42920                                 });
42921                            
42922                            });
42923                            
42924                            _this.panel.fireEvent('resize', [ '', '' ]);
42925                         }
42926                     },
42927                     loadResponse : function(o, success, response){
42928                             // this is overridden on before load..
42929                             
42930                             Roo.log("our code?");       
42931                             //Roo.log(success);
42932                             //Roo.log(response)
42933                             delete this.activeRequest;
42934                             if(!success){
42935                                 this.fireEvent("loadexception", this, o, response);
42936                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42937                                 return;
42938                             }
42939                             var result;
42940                             try {
42941                                 result = o.reader.read(response);
42942                             }catch(e){
42943                                 Roo.log("load exception?");
42944                                 this.fireEvent("loadexception", this, o, response, e);
42945                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42946                                 return;
42947                             }
42948                             Roo.log("ready...");        
42949                             // loop through result.records;
42950                             // and set this.tdate[date] = [] << array of records..
42951                             _this.tdata  = {};
42952                             Roo.each(result.records, function(r){
42953                                 //Roo.log(r.data);
42954                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42955                                     _this.tdata[r.data.when_dt.format('j')] = [];
42956                                 }
42957                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42958                             });
42959                             
42960                             //Roo.log(_this.tdata);
42961                             
42962                             result.records = [];
42963                             result.totalRecords = 6;
42964                     
42965                             // let's generate some duumy records for the rows.
42966                             //var st = _this.dateField.getValue();
42967                             
42968                             // work out monday..
42969                             //st = st.add(Date.DAY, -1 * st.format('w'));
42970                             
42971                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42972                             
42973                             var firstOfMonth = date.getFirstDayOfMonth();
42974                             var days = date.getDaysInMonth();
42975                             var d = 1;
42976                             var firstAdded = false;
42977                             for (var i = 0; i < result.totalRecords ; i++) {
42978                                 //var d= st.add(Date.DAY, i);
42979                                 var row = {};
42980                                 var added = 0;
42981                                 for(var w = 0 ; w < 7 ; w++){
42982                                     if(!firstAdded && firstOfMonth != w){
42983                                         continue;
42984                                     }
42985                                     if(d > days){
42986                                         continue;
42987                                     }
42988                                     firstAdded = true;
42989                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42990                                     row['weekday'+w] = String.format(
42991                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42992                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42993                                                     d,
42994                                                     date.format('Y-m-')+dd
42995                                                 );
42996                                     added++;
42997                                     if(typeof(_this.tdata[d]) != 'undefined'){
42998                                         Roo.each(_this.tdata[d], function(r){
42999                                             var is_sub = '';
43000                                             var deactive = '';
43001                                             var id = r.id;
43002                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
43003                                             if(r.parent_id*1>0){
43004                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
43005                                                 id = r.parent_id;
43006                                             }
43007                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
43008                                                 deactive = 'de-act-link';
43009                                             }
43010                                             
43011                                             row['weekday'+w] += String.format(
43012                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
43013                                                     id, //0
43014                                                     r.product_id_name, //1
43015                                                     r.when_dt.format('h:ia'), //2
43016                                                     is_sub, //3
43017                                                     deactive, //4
43018                                                     desc // 5
43019                                             );
43020                                         });
43021                                     }
43022                                     d++;
43023                                 }
43024                                 
43025                                 // only do this if something added..
43026                                 if(added > 0){ 
43027                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
43028                                 }
43029                                 
43030                                 
43031                                 // push it twice. (second one with an hour..
43032                                 
43033                             }
43034                             //Roo.log(result);
43035                             this.fireEvent("load", this, o, o.request.arg);
43036                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
43037                         },
43038                     sortInfo : {field: 'when_dt', direction : 'ASC' },
43039                     proxy : {
43040                         xtype: 'HttpProxy',
43041                         xns: Roo.data,
43042                         method : 'GET',
43043                         url : baseURL + '/Roo/Shop_course.php'
43044                     },
43045                     reader : {
43046                         xtype: 'JsonReader',
43047                         xns: Roo.data,
43048                         id : 'id',
43049                         fields : [
43050                             {
43051                                 'name': 'id',
43052                                 'type': 'int'
43053                             },
43054                             {
43055                                 'name': 'when_dt',
43056                                 'type': 'string'
43057                             },
43058                             {
43059                                 'name': 'end_dt',
43060                                 'type': 'string'
43061                             },
43062                             {
43063                                 'name': 'parent_id',
43064                                 'type': 'int'
43065                             },
43066                             {
43067                                 'name': 'product_id',
43068                                 'type': 'int'
43069                             },
43070                             {
43071                                 'name': 'productitem_id',
43072                                 'type': 'int'
43073                             },
43074                             {
43075                                 'name': 'guid',
43076                                 'type': 'int'
43077                             }
43078                         ]
43079                     }
43080                 },
43081                 toolbar : {
43082                     xtype: 'Toolbar',
43083                     xns: Roo,
43084                     items : [
43085                         {
43086                             xtype: 'Button',
43087                             xns: Roo.Toolbar,
43088                             listeners : {
43089                                 click : function (_self, e)
43090                                 {
43091                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43092                                     sd.setMonth(sd.getMonth()-1);
43093                                     _this.monthField.setValue(sd.format('Y-m-d'));
43094                                     _this.grid.ds.load({});
43095                                 }
43096                             },
43097                             text : "Back"
43098                         },
43099                         {
43100                             xtype: 'Separator',
43101                             xns: Roo.Toolbar
43102                         },
43103                         {
43104                             xtype: 'MonthField',
43105                             xns: Roo.form,
43106                             listeners : {
43107                                 render : function (_self)
43108                                 {
43109                                     _this.monthField = _self;
43110                                    // _this.monthField.set  today
43111                                 },
43112                                 select : function (combo, date)
43113                                 {
43114                                     _this.grid.ds.load({});
43115                                 }
43116                             },
43117                             value : (function() { return new Date(); })()
43118                         },
43119                         {
43120                             xtype: 'Separator',
43121                             xns: Roo.Toolbar
43122                         },
43123                         {
43124                             xtype: 'TextItem',
43125                             xns: Roo.Toolbar,
43126                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
43127                         },
43128                         {
43129                             xtype: 'Fill',
43130                             xns: Roo.Toolbar
43131                         },
43132                         {
43133                             xtype: 'Button',
43134                             xns: Roo.Toolbar,
43135                             listeners : {
43136                                 click : function (_self, e)
43137                                 {
43138                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43139                                     sd.setMonth(sd.getMonth()+1);
43140                                     _this.monthField.setValue(sd.format('Y-m-d'));
43141                                     _this.grid.ds.load({});
43142                                 }
43143                             },
43144                             text : "Next"
43145                         }
43146                     ]
43147                 },
43148                  
43149             }
43150         };
43151         
43152         *//*
43153  * Based on:
43154  * Ext JS Library 1.1.1
43155  * Copyright(c) 2006-2007, Ext JS, LLC.
43156  *
43157  * Originally Released Under LGPL - original licence link has changed is not relivant.
43158  *
43159  * Fork - LGPL
43160  * <script type="text/javascript">
43161  */
43162  
43163 /**
43164  * @class Roo.LoadMask
43165  * A simple utility class for generically masking elements while loading data.  If the element being masked has
43166  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
43167  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
43168  * element's UpdateManager load indicator and will be destroyed after the initial load.
43169  * @constructor
43170  * Create a new LoadMask
43171  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
43172  * @param {Object} config The config object
43173  */
43174 Roo.LoadMask = function(el, config){
43175     this.el = Roo.get(el);
43176     Roo.apply(this, config);
43177     if(this.store){
43178         this.store.on('beforeload', this.onBeforeLoad, this);
43179         this.store.on('load', this.onLoad, this);
43180         this.store.on('loadexception', this.onLoadException, this);
43181         this.removeMask = false;
43182     }else{
43183         var um = this.el.getUpdateManager();
43184         um.showLoadIndicator = false; // disable the default indicator
43185         um.on('beforeupdate', this.onBeforeLoad, this);
43186         um.on('update', this.onLoad, this);
43187         um.on('failure', this.onLoad, this);
43188         this.removeMask = true;
43189     }
43190 };
43191
43192 Roo.LoadMask.prototype = {
43193     /**
43194      * @cfg {Boolean} removeMask
43195      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
43196      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
43197      */
43198     removeMask : false,
43199     /**
43200      * @cfg {String} msg
43201      * The text to display in a centered loading message box (defaults to 'Loading...')
43202      */
43203     msg : 'Loading...',
43204     /**
43205      * @cfg {String} msgCls
43206      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43207      */
43208     msgCls : 'x-mask-loading',
43209
43210     /**
43211      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43212      * @type Boolean
43213      */
43214     disabled: false,
43215
43216     /**
43217      * Disables the mask to prevent it from being displayed
43218      */
43219     disable : function(){
43220        this.disabled = true;
43221     },
43222
43223     /**
43224      * Enables the mask so that it can be displayed
43225      */
43226     enable : function(){
43227         this.disabled = false;
43228     },
43229     
43230     onLoadException : function()
43231     {
43232         Roo.log(arguments);
43233         
43234         if (typeof(arguments[3]) != 'undefined') {
43235             Roo.MessageBox.alert("Error loading",arguments[3]);
43236         } 
43237         /*
43238         try {
43239             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43240                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43241             }   
43242         } catch(e) {
43243             
43244         }
43245         */
43246     
43247         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43248     },
43249     // private
43250     onLoad : function()
43251     {
43252         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43253     },
43254
43255     // private
43256     onBeforeLoad : function(){
43257         if(!this.disabled){
43258             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43259         }
43260     },
43261
43262     // private
43263     destroy : function(){
43264         if(this.store){
43265             this.store.un('beforeload', this.onBeforeLoad, this);
43266             this.store.un('load', this.onLoad, this);
43267             this.store.un('loadexception', this.onLoadException, this);
43268         }else{
43269             var um = this.el.getUpdateManager();
43270             um.un('beforeupdate', this.onBeforeLoad, this);
43271             um.un('update', this.onLoad, this);
43272             um.un('failure', this.onLoad, this);
43273         }
43274     }
43275 };/*
43276  * Based on:
43277  * Ext JS Library 1.1.1
43278  * Copyright(c) 2006-2007, Ext JS, LLC.
43279  *
43280  * Originally Released Under LGPL - original licence link has changed is not relivant.
43281  *
43282  * Fork - LGPL
43283  * <script type="text/javascript">
43284  */
43285
43286
43287 /**
43288  * @class Roo.XTemplate
43289  * @extends Roo.Template
43290  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43291 <pre><code>
43292 var t = new Roo.XTemplate(
43293         '&lt;select name="{name}"&gt;',
43294                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43295         '&lt;/select&gt;'
43296 );
43297  
43298 // then append, applying the master template values
43299  </code></pre>
43300  *
43301  * Supported features:
43302  *
43303  *  Tags:
43304
43305 <pre><code>
43306       {a_variable} - output encoded.
43307       {a_variable.format:("Y-m-d")} - call a method on the variable
43308       {a_variable:raw} - unencoded output
43309       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43310       {a_variable:this.method_on_template(...)} - call a method on the template object.
43311  
43312 </code></pre>
43313  *  The tpl tag:
43314 <pre><code>
43315         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43316         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43317         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43318         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43319   
43320         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43321         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43322 </code></pre>
43323  *      
43324  */
43325 Roo.XTemplate = function()
43326 {
43327     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43328     if (this.html) {
43329         this.compile();
43330     }
43331 };
43332
43333
43334 Roo.extend(Roo.XTemplate, Roo.Template, {
43335
43336     /**
43337      * The various sub templates
43338      */
43339     tpls : false,
43340     /**
43341      *
43342      * basic tag replacing syntax
43343      * WORD:WORD()
43344      *
43345      * // you can fake an object call by doing this
43346      *  x.t:(test,tesT) 
43347      * 
43348      */
43349     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43350
43351     /**
43352      * compile the template
43353      *
43354      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43355      *
43356      */
43357     compile: function()
43358     {
43359         var s = this.html;
43360      
43361         s = ['<tpl>', s, '</tpl>'].join('');
43362     
43363         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43364             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43365             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43366             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43367             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43368             m,
43369             id     = 0,
43370             tpls   = [];
43371     
43372         while(true == !!(m = s.match(re))){
43373             var forMatch   = m[0].match(nameRe),
43374                 ifMatch   = m[0].match(ifRe),
43375                 execMatch   = m[0].match(execRe),
43376                 namedMatch   = m[0].match(namedRe),
43377                 
43378                 exp  = null, 
43379                 fn   = null,
43380                 exec = null,
43381                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43382                 
43383             if (ifMatch) {
43384                 // if - puts fn into test..
43385                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43386                 if(exp){
43387                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43388                 }
43389             }
43390             
43391             if (execMatch) {
43392                 // exec - calls a function... returns empty if true is  returned.
43393                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43394                 if(exp){
43395                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43396                 }
43397             }
43398             
43399             
43400             if (name) {
43401                 // for = 
43402                 switch(name){
43403                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43404                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43405                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43406                 }
43407             }
43408             var uid = namedMatch ? namedMatch[1] : id;
43409             
43410             
43411             tpls.push({
43412                 id:     namedMatch ? namedMatch[1] : id,
43413                 target: name,
43414                 exec:   exec,
43415                 test:   fn,
43416                 body:   m[1] || ''
43417             });
43418             if (namedMatch) {
43419                 s = s.replace(m[0], '');
43420             } else { 
43421                 s = s.replace(m[0], '{xtpl'+ id + '}');
43422             }
43423             ++id;
43424         }
43425         this.tpls = [];
43426         for(var i = tpls.length-1; i >= 0; --i){
43427             this.compileTpl(tpls[i]);
43428             this.tpls[tpls[i].id] = tpls[i];
43429         }
43430         this.master = tpls[tpls.length-1];
43431         return this;
43432     },
43433     /**
43434      * same as applyTemplate, except it's done to one of the subTemplates
43435      * when using named templates, you can do:
43436      *
43437      * var str = pl.applySubTemplate('your-name', values);
43438      *
43439      * 
43440      * @param {Number} id of the template
43441      * @param {Object} values to apply to template
43442      * @param {Object} parent (normaly the instance of this object)
43443      */
43444     applySubTemplate : function(id, values, parent)
43445     {
43446         
43447         
43448         var t = this.tpls[id];
43449         
43450         
43451         try { 
43452             if(t.test && !t.test.call(this, values, parent)){
43453                 return '';
43454             }
43455         } catch(e) {
43456             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43457             Roo.log(e.toString());
43458             Roo.log(t.test);
43459             return ''
43460         }
43461         try { 
43462             
43463             if(t.exec && t.exec.call(this, values, parent)){
43464                 return '';
43465             }
43466         } catch(e) {
43467             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43468             Roo.log(e.toString());
43469             Roo.log(t.exec);
43470             return ''
43471         }
43472         try {
43473             var vs = t.target ? t.target.call(this, values, parent) : values;
43474             parent = t.target ? values : parent;
43475             if(t.target && vs instanceof Array){
43476                 var buf = [];
43477                 for(var i = 0, len = vs.length; i < len; i++){
43478                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43479                 }
43480                 return buf.join('');
43481             }
43482             return t.compiled.call(this, vs, parent);
43483         } catch (e) {
43484             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43485             Roo.log(e.toString());
43486             Roo.log(t.compiled);
43487             return '';
43488         }
43489     },
43490
43491     compileTpl : function(tpl)
43492     {
43493         var fm = Roo.util.Format;
43494         var useF = this.disableFormats !== true;
43495         var sep = Roo.isGecko ? "+" : ",";
43496         var undef = function(str) {
43497             Roo.log("Property not found :"  + str);
43498             return '';
43499         };
43500         
43501         var fn = function(m, name, format, args)
43502         {
43503             //Roo.log(arguments);
43504             args = args ? args.replace(/\\'/g,"'") : args;
43505             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43506             if (typeof(format) == 'undefined') {
43507                 format= 'htmlEncode';
43508             }
43509             if (format == 'raw' ) {
43510                 format = false;
43511             }
43512             
43513             if(name.substr(0, 4) == 'xtpl'){
43514                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43515             }
43516             
43517             // build an array of options to determine if value is undefined..
43518             
43519             // basically get 'xxxx.yyyy' then do
43520             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43521             //    (function () { Roo.log("Property not found"); return ''; })() :
43522             //    ......
43523             
43524             var udef_ar = [];
43525             var lookfor = '';
43526             Roo.each(name.split('.'), function(st) {
43527                 lookfor += (lookfor.length ? '.': '') + st;
43528                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43529             });
43530             
43531             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43532             
43533             
43534             if(format && useF){
43535                 
43536                 args = args ? ',' + args : "";
43537                  
43538                 if(format.substr(0, 5) != "this."){
43539                     format = "fm." + format + '(';
43540                 }else{
43541                     format = 'this.call("'+ format.substr(5) + '", ';
43542                     args = ", values";
43543                 }
43544                 
43545                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43546             }
43547              
43548             if (args.length) {
43549                 // called with xxyx.yuu:(test,test)
43550                 // change to ()
43551                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43552             }
43553             // raw.. - :raw modifier..
43554             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43555             
43556         };
43557         var body;
43558         // branched to use + in gecko and [].join() in others
43559         if(Roo.isGecko){
43560             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43561                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43562                     "';};};";
43563         }else{
43564             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43565             body.push(tpl.body.replace(/(\r\n|\n)/g,
43566                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43567             body.push("'].join('');};};");
43568             body = body.join('');
43569         }
43570         
43571         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43572        
43573         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43574         eval(body);
43575         
43576         return this;
43577     },
43578
43579     applyTemplate : function(values){
43580         return this.master.compiled.call(this, values, {});
43581         //var s = this.subs;
43582     },
43583
43584     apply : function(){
43585         return this.applyTemplate.apply(this, arguments);
43586     }
43587
43588  });
43589
43590 Roo.XTemplate.from = function(el){
43591     el = Roo.getDom(el);
43592     return new Roo.XTemplate(el.value || el.innerHTML);
43593 };