fix image loading
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1398  * to the Reader when its load method is called.
1399  * @constructor
1400  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1401  */
1402 Roo.data.MemoryProxy = function(data){
1403     if (data.data) {
1404         data = data.data;
1405     }
1406     Roo.data.MemoryProxy.superclass.constructor.call(this);
1407     this.data = data;
1408 };
1409
1410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1411     
1412     /**
1413      * Load data from the requested source (in this case an in-memory
1414      * data object passed to the constructor), read the data object into
1415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1416      * process that block using the passed callback.
1417      * @param {Object} params This parameter is not used by the MemoryProxy class.
1418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1419      * object into a block of Roo.data.Records.
1420      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1421      * The function must be passed <ul>
1422      * <li>The Record block object</li>
1423      * <li>The "arg" argument from the load function</li>
1424      * <li>A boolean success indicator</li>
1425      * </ul>
1426      * @param {Object} scope The scope in which to call the callback
1427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1428      */
1429     load : function(params, reader, callback, scope, arg){
1430         params = params || {};
1431         var result;
1432         try {
1433             result = reader.readRecords(params.data ? params.data :this.data);
1434         }catch(e){
1435             this.fireEvent("loadexception", this, arg, null, e);
1436             callback.call(scope, null, arg, false);
1437             return;
1438         }
1439         callback.call(scope, result, arg, true);
1440     },
1441     
1442     // private
1443     update : function(params, records){
1444         
1445     }
1446 });/*
1447  * Based on:
1448  * Ext JS Library 1.1.1
1449  * Copyright(c) 2006-2007, Ext JS, LLC.
1450  *
1451  * Originally Released Under LGPL - original licence link has changed is not relivant.
1452  *
1453  * Fork - LGPL
1454  * <script type="text/javascript">
1455  */
1456 /**
1457  * @class Roo.data.HttpProxy
1458  * @extends Roo.data.DataProxy
1459  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1460  * configured to reference a certain URL.<br><br>
1461  * <p>
1462  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1463  * from which the running page was served.<br><br>
1464  * <p>
1465  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1466  * <p>
1467  * Be aware that to enable the browser to parse an XML document, the server must set
1468  * the Content-Type header in the HTTP response to "text/xml".
1469  * @constructor
1470  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1471  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1472  * will be used to make the request.
1473  */
1474 Roo.data.HttpProxy = function(conn){
1475     Roo.data.HttpProxy.superclass.constructor.call(this);
1476     // is conn a conn config or a real conn?
1477     this.conn = conn;
1478     this.useAjax = !conn || !conn.events;
1479   
1480 };
1481
1482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1483     // thse are take from connection...
1484     
1485     /**
1486      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1487      */
1488     /**
1489      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1490      * extra parameters to each request made by this object. (defaults to undefined)
1491      */
1492     /**
1493      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1494      *  to each request made by this object. (defaults to undefined)
1495      */
1496     /**
1497      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1498      */
1499     /**
1500      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1501      */
1502      /**
1503      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1504      * @type Boolean
1505      */
1506   
1507
1508     /**
1509      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1510      * @type Boolean
1511      */
1512     /**
1513      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1514      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1515      * a finer-grained basis than the DataProxy events.
1516      */
1517     getConnection : function(){
1518         return this.useAjax ? Roo.Ajax : this.conn;
1519     },
1520
1521     /**
1522      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1523      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1524      * process that block using the passed callback.
1525      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1526      * for the request to the remote server.
1527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1528      * object into a block of Roo.data.Records.
1529      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1530      * The function must be passed <ul>
1531      * <li>The Record block object</li>
1532      * <li>The "arg" argument from the load function</li>
1533      * <li>A boolean success indicator</li>
1534      * </ul>
1535      * @param {Object} scope The scope in which to call the callback
1536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1537      */
1538     load : function(params, reader, callback, scope, arg){
1539         if(this.fireEvent("beforeload", this, params) !== false){
1540             var  o = {
1541                 params : params || {},
1542                 request: {
1543                     callback : callback,
1544                     scope : scope,
1545                     arg : arg
1546                 },
1547                 reader: reader,
1548                 callback : this.loadResponse,
1549                 scope: this
1550             };
1551             if(this.useAjax){
1552                 Roo.applyIf(o, this.conn);
1553                 if(this.activeRequest){
1554                     Roo.Ajax.abort(this.activeRequest);
1555                 }
1556                 this.activeRequest = Roo.Ajax.request(o);
1557             }else{
1558                 this.conn.request(o);
1559             }
1560         }else{
1561             callback.call(scope||this, null, arg, false);
1562         }
1563     },
1564
1565     // private
1566     loadResponse : function(o, success, response){
1567         delete this.activeRequest;
1568         if(!success){
1569             this.fireEvent("loadexception", this, o, response);
1570             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1571             return;
1572         }
1573         var result;
1574         try {
1575             result = o.reader.read(response);
1576         }catch(e){
1577             o.success = false;
1578             o.raw = { errorMsg : response.responseText };
1579             this.fireEvent("loadexception", this, o, response, e);
1580             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1581             return;
1582         }
1583         
1584         this.fireEvent("load", this, o, o.request.arg);
1585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1586     },
1587
1588     // private
1589     update : function(dataSet){
1590
1591     },
1592
1593     // private
1594     updateResponse : function(dataSet){
1595
1596     }
1597 });/*
1598  * Based on:
1599  * Ext JS Library 1.1.1
1600  * Copyright(c) 2006-2007, Ext JS, LLC.
1601  *
1602  * Originally Released Under LGPL - original licence link has changed is not relivant.
1603  *
1604  * Fork - LGPL
1605  * <script type="text/javascript">
1606  */
1607
1608 /**
1609  * @class Roo.data.ScriptTagProxy
1610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1611  * other than the originating domain of the running page.<br><br>
1612  * <p>
1613  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1615  * <p>
1616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1617  * source code that is used as the source inside a &lt;script> tag.<br><br>
1618  * <p>
1619  * In order for the browser to process the returned data, the server must wrap the data object
1620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1622  * depending on whether the callback name was passed:
1623  * <p>
1624  * <pre><code>
1625 boolean scriptTag = false;
1626 String cb = request.getParameter("callback");
1627 if (cb != null) {
1628     scriptTag = true;
1629     response.setContentType("text/javascript");
1630 } else {
1631     response.setContentType("application/x-json");
1632 }
1633 Writer out = response.getWriter();
1634 if (scriptTag) {
1635     out.write(cb + "(");
1636 }
1637 out.print(dataBlock.toJsonString());
1638 if (scriptTag) {
1639     out.write(");");
1640 }
1641 </pre></code>
1642  *
1643  * @constructor
1644  * @param {Object} config A configuration object.
1645  */
1646 Roo.data.ScriptTagProxy = function(config){
1647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1648     Roo.apply(this, config);
1649     this.head = document.getElementsByTagName("head")[0];
1650 };
1651
1652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1653
1654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1655     /**
1656      * @cfg {String} url The URL from which to request the data object.
1657      */
1658     /**
1659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1660      */
1661     timeout : 30000,
1662     /**
1663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1664      * the server the name of the callback function set up by the load call to process the returned data object.
1665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1666      * javascript output which calls this named function passing the data object as its only parameter.
1667      */
1668     callbackParam : "callback",
1669     /**
1670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1671      * name to the request.
1672      */
1673     nocache : true,
1674
1675     /**
1676      * Load data from the configured URL, read the data object into
1677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1678      * process that block using the passed callback.
1679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1680      * for the request to the remote server.
1681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1682      * object into a block of Roo.data.Records.
1683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1684      * The function must be passed <ul>
1685      * <li>The Record block object</li>
1686      * <li>The "arg" argument from the load function</li>
1687      * <li>A boolean success indicator</li>
1688      * </ul>
1689      * @param {Object} scope The scope in which to call the callback
1690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1691      */
1692     load : function(params, reader, callback, scope, arg){
1693         if(this.fireEvent("beforeload", this, params) !== false){
1694
1695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1696
1697             var url = this.url;
1698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1699             if(this.nocache){
1700                 url += "&_dc=" + (new Date().getTime());
1701             }
1702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1703             var trans = {
1704                 id : transId,
1705                 cb : "stcCallback"+transId,
1706                 scriptId : "stcScript"+transId,
1707                 params : params,
1708                 arg : arg,
1709                 url : url,
1710                 callback : callback,
1711                 scope : scope,
1712                 reader : reader
1713             };
1714             var conn = this;
1715
1716             window[trans.cb] = function(o){
1717                 conn.handleResponse(o, trans);
1718             };
1719
1720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1721
1722             if(this.autoAbort !== false){
1723                 this.abort();
1724             }
1725
1726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1727
1728             var script = document.createElement("script");
1729             script.setAttribute("src", url);
1730             script.setAttribute("type", "text/javascript");
1731             script.setAttribute("id", trans.scriptId);
1732             this.head.appendChild(script);
1733
1734             this.trans = trans;
1735         }else{
1736             callback.call(scope||this, null, arg, false);
1737         }
1738     },
1739
1740     // private
1741     isLoading : function(){
1742         return this.trans ? true : false;
1743     },
1744
1745     /**
1746      * Abort the current server request.
1747      */
1748     abort : function(){
1749         if(this.isLoading()){
1750             this.destroyTrans(this.trans);
1751         }
1752     },
1753
1754     // private
1755     destroyTrans : function(trans, isLoaded){
1756         this.head.removeChild(document.getElementById(trans.scriptId));
1757         clearTimeout(trans.timeoutId);
1758         if(isLoaded){
1759             window[trans.cb] = undefined;
1760             try{
1761                 delete window[trans.cb];
1762             }catch(e){}
1763         }else{
1764             // if hasn't been loaded, wait for load to remove it to prevent script error
1765             window[trans.cb] = function(){
1766                 window[trans.cb] = undefined;
1767                 try{
1768                     delete window[trans.cb];
1769                 }catch(e){}
1770             };
1771         }
1772     },
1773
1774     // private
1775     handleResponse : function(o, trans){
1776         this.trans = false;
1777         this.destroyTrans(trans, true);
1778         var result;
1779         try {
1780             result = trans.reader.readRecords(o);
1781         }catch(e){
1782             this.fireEvent("loadexception", this, o, trans.arg, e);
1783             trans.callback.call(trans.scope||window, null, trans.arg, false);
1784             return;
1785         }
1786         this.fireEvent("load", this, o, trans.arg);
1787         trans.callback.call(trans.scope||window, result, trans.arg, true);
1788     },
1789
1790     // private
1791     handleFailure : function(trans){
1792         this.trans = false;
1793         this.destroyTrans(trans, false);
1794         this.fireEvent("loadexception", this, null, trans.arg);
1795         trans.callback.call(trans.scope||window, null, trans.arg, false);
1796     }
1797 });/*
1798  * Based on:
1799  * Ext JS Library 1.1.1
1800  * Copyright(c) 2006-2007, Ext JS, LLC.
1801  *
1802  * Originally Released Under LGPL - original licence link has changed is not relivant.
1803  *
1804  * Fork - LGPL
1805  * <script type="text/javascript">
1806  */
1807
1808 /**
1809  * @class Roo.data.JsonReader
1810  * @extends Roo.data.DataReader
1811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1812  * based on mappings in a provided Roo.data.Record constructor.
1813  * 
1814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1815  * in the reply previously. 
1816  * 
1817  * <p>
1818  * Example code:
1819  * <pre><code>
1820 var RecordDef = Roo.data.Record.create([
1821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1823 ]);
1824 var myReader = new Roo.data.JsonReader({
1825     totalProperty: "results",    // The property which contains the total dataset size (optional)
1826     root: "rows",                // The property which contains an Array of row objects
1827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1828 }, RecordDef);
1829 </code></pre>
1830  * <p>
1831  * This would consume a JSON file like this:
1832  * <pre><code>
1833 { 'results': 2, 'rows': [
1834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1836 }
1837 </code></pre>
1838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1840  * paged from the remote server.
1841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1842  * @cfg {String} root name of the property which contains the Array of row objects.
1843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1844  * @cfg {Array} fields Array of field definition objects
1845  * @constructor
1846  * Create a new JsonReader
1847  * @param {Object} meta Metadata configuration options
1848  * @param {Object} recordType Either an Array of field definition objects,
1849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1850  */
1851 Roo.data.JsonReader = function(meta, recordType){
1852     
1853     meta = meta || {};
1854     // set some defaults:
1855     Roo.applyIf(meta, {
1856         totalProperty: 'total',
1857         successProperty : 'success',
1858         root : 'data',
1859         id : 'id'
1860     });
1861     
1862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1863 };
1864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1865     
1866     readerType : 'Json',
1867     
1868     /**
1869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1870      * Used by Store query builder to append _requestMeta to params.
1871      * 
1872      */
1873     metaFromRemote : false,
1874     /**
1875      * This method is only used by a DataProxy which has retrieved data from a remote server.
1876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1877      * @return {Object} data A data block which is used by an Roo.data.Store object as
1878      * a cache of Roo.data.Records.
1879      */
1880     read : function(response){
1881         var json = response.responseText;
1882        
1883         var o = /* eval:var:o */ eval("("+json+")");
1884         if(!o) {
1885             throw {message: "JsonReader.read: Json object not found"};
1886         }
1887         
1888         if(o.metaData){
1889             
1890             delete this.ef;
1891             this.metaFromRemote = true;
1892             this.meta = o.metaData;
1893             this.recordType = Roo.data.Record.create(o.metaData.fields);
1894             this.onMetaChange(this.meta, this.recordType, o);
1895         }
1896         return this.readRecords(o);
1897     },
1898
1899     // private function a store will implement
1900     onMetaChange : function(meta, recordType, o){
1901
1902     },
1903
1904     /**
1905          * @ignore
1906          */
1907     simpleAccess: function(obj, subsc) {
1908         return obj[subsc];
1909     },
1910
1911         /**
1912          * @ignore
1913          */
1914     getJsonAccessor: function(){
1915         var re = /[\[\.]/;
1916         return function(expr) {
1917             try {
1918                 return(re.test(expr))
1919                     ? new Function("obj", "return obj." + expr)
1920                     : function(obj){
1921                         return obj[expr];
1922                     };
1923             } catch(e){}
1924             return Roo.emptyFn;
1925         };
1926     }(),
1927
1928     /**
1929      * Create a data block containing Roo.data.Records from an XML document.
1930      * @param {Object} o An object which contains an Array of row objects in the property specified
1931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1932      * which contains the total size of the dataset.
1933      * @return {Object} data A data block which is used by an Roo.data.Store object as
1934      * a cache of Roo.data.Records.
1935      */
1936     readRecords : function(o){
1937         /**
1938          * After any data loads, the raw JSON data is available for further custom processing.
1939          * @type Object
1940          */
1941         this.o = o;
1942         var s = this.meta, Record = this.recordType,
1943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1944
1945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1946         if (!this.ef) {
1947             if(s.totalProperty) {
1948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1949                 }
1950                 if(s.successProperty) {
1951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1952                 }
1953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1954                 if (s.id) {
1955                         var g = this.getJsonAccessor(s.id);
1956                         this.getId = function(rec) {
1957                                 var r = g(rec);  
1958                                 return (r === undefined || r === "") ? null : r;
1959                         };
1960                 } else {
1961                         this.getId = function(){return null;};
1962                 }
1963             this.ef = [];
1964             for(var jj = 0; jj < fl; jj++){
1965                 f = fi[jj];
1966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1967                 this.ef[jj] = this.getJsonAccessor(map);
1968             }
1969         }
1970
1971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1972         if(s.totalProperty){
1973             var vt = parseInt(this.getTotal(o), 10);
1974             if(!isNaN(vt)){
1975                 totalRecords = vt;
1976             }
1977         }
1978         if(s.successProperty){
1979             var vs = this.getSuccess(o);
1980             if(vs === false || vs === 'false'){
1981                 success = false;
1982             }
1983         }
1984         var records = [];
1985         for(var i = 0; i < c; i++){
1986             var n = root[i];
1987             var values = {};
1988             var id = this.getId(n);
1989             for(var j = 0; j < fl; j++){
1990                 f = fi[j];
1991                                 var v = this.ef[j](n);
1992                                 if (!f.convert) {
1993                                         Roo.log('missing convert for ' + f.name);
1994                                         Roo.log(f);
1995                                         continue;
1996                                 }
1997                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1998             }
1999                         if (!Record) {
2000                                 return {
2001                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2002                                         success : false,
2003                                         records : [],
2004                                         totalRecords : 0
2005                                 };
2006                         }
2007             var record = new Record(values, id);
2008             record.json = n;
2009             records[i] = record;
2010         }
2011         return {
2012             raw : o,
2013             success : success,
2014             records : records,
2015             totalRecords : totalRecords
2016         };
2017     },
2018     // used when loading children.. @see loadDataFromChildren
2019     toLoadData: function(rec)
2020     {
2021         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2022         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2023         return { data : data, total : data.length };
2024         
2025     }
2026 });/*
2027  * Based on:
2028  * Ext JS Library 1.1.1
2029  * Copyright(c) 2006-2007, Ext JS, LLC.
2030  *
2031  * Originally Released Under LGPL - original licence link has changed is not relivant.
2032  *
2033  * Fork - LGPL
2034  * <script type="text/javascript">
2035  */
2036
2037 /**
2038  * @class Roo.data.XmlReader
2039  * @extends Roo.data.DataReader
2040  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2041  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2042  * <p>
2043  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2044  * header in the HTTP response must be set to "text/xml".</em>
2045  * <p>
2046  * Example code:
2047  * <pre><code>
2048 var RecordDef = Roo.data.Record.create([
2049    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2050    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2051 ]);
2052 var myReader = new Roo.data.XmlReader({
2053    totalRecords: "results", // The element which contains the total dataset size (optional)
2054    record: "row",           // The repeated element which contains row information
2055    id: "id"                 // The element within the row that provides an ID for the record (optional)
2056 }, RecordDef);
2057 </code></pre>
2058  * <p>
2059  * This would consume an XML file like this:
2060  * <pre><code>
2061 &lt;?xml?>
2062 &lt;dataset>
2063  &lt;results>2&lt;/results>
2064  &lt;row>
2065    &lt;id>1&lt;/id>
2066    &lt;name>Bill&lt;/name>
2067    &lt;occupation>Gardener&lt;/occupation>
2068  &lt;/row>
2069  &lt;row>
2070    &lt;id>2&lt;/id>
2071    &lt;name>Ben&lt;/name>
2072    &lt;occupation>Horticulturalist&lt;/occupation>
2073  &lt;/row>
2074 &lt;/dataset>
2075 </code></pre>
2076  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2077  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2078  * paged from the remote server.
2079  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2080  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2081  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2082  * a record identifier value.
2083  * @constructor
2084  * Create a new XmlReader
2085  * @param {Object} meta Metadata configuration options
2086  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2087  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2088  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2089  */
2090 Roo.data.XmlReader = function(meta, recordType){
2091     meta = meta || {};
2092     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2093 };
2094 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2095     
2096     readerType : 'Xml',
2097     
2098     /**
2099      * This method is only used by a DataProxy which has retrieved data from a remote server.
2100          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2101          * to contain a method called 'responseXML' that returns an XML document object.
2102      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103      * a cache of Roo.data.Records.
2104      */
2105     read : function(response){
2106         var doc = response.responseXML;
2107         if(!doc) {
2108             throw {message: "XmlReader.read: XML Document not available"};
2109         }
2110         return this.readRecords(doc);
2111     },
2112
2113     /**
2114      * Create a data block containing Roo.data.Records from an XML document.
2115          * @param {Object} doc A parsed XML document.
2116      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2117      * a cache of Roo.data.Records.
2118      */
2119     readRecords : function(doc){
2120         /**
2121          * After any data loads/reads, the raw XML Document is available for further custom processing.
2122          * @type XMLDocument
2123          */
2124         this.xmlData = doc;
2125         var root = doc.documentElement || doc;
2126         var q = Roo.DomQuery;
2127         var recordType = this.recordType, fields = recordType.prototype.fields;
2128         var sid = this.meta.id;
2129         var totalRecords = 0, success = true;
2130         if(this.meta.totalRecords){
2131             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2132         }
2133         
2134         if(this.meta.success){
2135             var sv = q.selectValue(this.meta.success, root, true);
2136             success = sv !== false && sv !== 'false';
2137         }
2138         var records = [];
2139         var ns = q.select(this.meta.record, root);
2140         for(var i = 0, len = ns.length; i < len; i++) {
2141                 var n = ns[i];
2142                 var values = {};
2143                 var id = sid ? q.selectValue(sid, n) : undefined;
2144                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2145                     var f = fields.items[j];
2146                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2147                     v = f.convert(v);
2148                     values[f.name] = v;
2149                 }
2150                 var record = new recordType(values, id);
2151                 record.node = n;
2152                 records[records.length] = record;
2153             }
2154
2155             return {
2156                 success : success,
2157                 records : records,
2158                 totalRecords : totalRecords || records.length
2159             };
2160     }
2161 });/*
2162  * Based on:
2163  * Ext JS Library 1.1.1
2164  * Copyright(c) 2006-2007, Ext JS, LLC.
2165  *
2166  * Originally Released Under LGPL - original licence link has changed is not relivant.
2167  *
2168  * Fork - LGPL
2169  * <script type="text/javascript">
2170  */
2171
2172 /**
2173  * @class Roo.data.ArrayReader
2174  * @extends Roo.data.DataReader
2175  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2176  * Each element of that Array represents a row of data fields. The
2177  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2178  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2179  * <p>
2180  * Example code:.
2181  * <pre><code>
2182 var RecordDef = Roo.data.Record.create([
2183     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2184     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2185 ]);
2186 var myReader = new Roo.data.ArrayReader({
2187     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2188 }, RecordDef);
2189 </code></pre>
2190  * <p>
2191  * This would consume an Array like this:
2192  * <pre><code>
2193 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2194   </code></pre>
2195  
2196  * @constructor
2197  * Create a new JsonReader
2198  * @param {Object} meta Metadata configuration options.
2199  * @param {Object|Array} recordType Either an Array of field definition objects
2200  * 
2201  * @cfg {Array} fields Array of field definition objects
2202  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2203  * as specified to {@link Roo.data.Record#create},
2204  * or an {@link Roo.data.Record} object
2205  *
2206  * 
2207  * created using {@link Roo.data.Record#create}.
2208  */
2209 Roo.data.ArrayReader = function(meta, recordType)
2210 {    
2211     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2212 };
2213
2214 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2215     
2216       /**
2217      * Create a data block containing Roo.data.Records from an XML document.
2218      * @param {Object} o An Array of row objects which represents the dataset.
2219      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2220      * a cache of Roo.data.Records.
2221      */
2222     readRecords : function(o)
2223     {
2224         var sid = this.meta ? this.meta.id : null;
2225         var recordType = this.recordType, fields = recordType.prototype.fields;
2226         var records = [];
2227         var root = o;
2228         for(var i = 0; i < root.length; i++){
2229             var n = root[i];
2230             var values = {};
2231             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2232             for(var j = 0, jlen = fields.length; j < jlen; j++){
2233                 var f = fields.items[j];
2234                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2235                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2236                 v = f.convert(v);
2237                 values[f.name] = v;
2238             }
2239             var record = new recordType(values, id);
2240             record.json = n;
2241             records[records.length] = record;
2242         }
2243         return {
2244             records : records,
2245             totalRecords : records.length
2246         };
2247     },
2248     // used when loading children.. @see loadDataFromChildren
2249     toLoadData: function(rec)
2250     {
2251         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2252         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2253         
2254     }
2255     
2256     
2257 });/*
2258  * Based on:
2259  * Ext JS Library 1.1.1
2260  * Copyright(c) 2006-2007, Ext JS, LLC.
2261  *
2262  * Originally Released Under LGPL - original licence link has changed is not relivant.
2263  *
2264  * Fork - LGPL
2265  * <script type="text/javascript">
2266  */
2267
2268
2269 /**
2270  * @class Roo.data.Tree
2271  * @extends Roo.util.Observable
2272  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2273  * in the tree have most standard DOM functionality.
2274  * @constructor
2275  * @param {Node} root (optional) The root node
2276  */
2277 Roo.data.Tree = function(root){
2278    this.nodeHash = {};
2279    /**
2280     * The root node for this tree
2281     * @type Node
2282     */
2283    this.root = null;
2284    if(root){
2285        this.setRootNode(root);
2286    }
2287    this.addEvents({
2288        /**
2289         * @event append
2290         * Fires when a new child node is appended to a node in this tree.
2291         * @param {Tree} tree The owner tree
2292         * @param {Node} parent The parent node
2293         * @param {Node} node The newly appended node
2294         * @param {Number} index The index of the newly appended node
2295         */
2296        "append" : true,
2297        /**
2298         * @event remove
2299         * Fires when a child node is removed from a node in this tree.
2300         * @param {Tree} tree The owner tree
2301         * @param {Node} parent The parent node
2302         * @param {Node} node The child node removed
2303         */
2304        "remove" : true,
2305        /**
2306         * @event move
2307         * Fires when a node is moved to a new location in the tree
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} node The node moved
2310         * @param {Node} oldParent The old parent of this node
2311         * @param {Node} newParent The new parent of this node
2312         * @param {Number} index The index it was moved to
2313         */
2314        "move" : true,
2315        /**
2316         * @event insert
2317         * Fires when a new child node is inserted in a node in this tree.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} parent The parent node
2320         * @param {Node} node The child node inserted
2321         * @param {Node} refNode The child node the node was inserted before
2322         */
2323        "insert" : true,
2324        /**
2325         * @event beforeappend
2326         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2327         * @param {Tree} tree The owner tree
2328         * @param {Node} parent The parent node
2329         * @param {Node} node The child node to be appended
2330         */
2331        "beforeappend" : true,
2332        /**
2333         * @event beforeremove
2334         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2335         * @param {Tree} tree The owner tree
2336         * @param {Node} parent The parent node
2337         * @param {Node} node The child node to be removed
2338         */
2339        "beforeremove" : true,
2340        /**
2341         * @event beforemove
2342         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} node The node being moved
2345         * @param {Node} oldParent The parent of the node
2346         * @param {Node} newParent The new parent the node is moving to
2347         * @param {Number} index The index it is being moved to
2348         */
2349        "beforemove" : true,
2350        /**
2351         * @event beforeinsert
2352         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2353         * @param {Tree} tree The owner tree
2354         * @param {Node} parent The parent node
2355         * @param {Node} node The child node to be inserted
2356         * @param {Node} refNode The child node the node is being inserted before
2357         */
2358        "beforeinsert" : true
2359    });
2360
2361     Roo.data.Tree.superclass.constructor.call(this);
2362 };
2363
2364 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2365     pathSeparator: "/",
2366
2367     proxyNodeEvent : function(){
2368         return this.fireEvent.apply(this, arguments);
2369     },
2370
2371     /**
2372      * Returns the root node for this tree.
2373      * @return {Node}
2374      */
2375     getRootNode : function(){
2376         return this.root;
2377     },
2378
2379     /**
2380      * Sets the root node for this tree.
2381      * @param {Node} node
2382      * @return {Node}
2383      */
2384     setRootNode : function(node){
2385         this.root = node;
2386         node.ownerTree = this;
2387         node.isRoot = true;
2388         this.registerNode(node);
2389         return node;
2390     },
2391
2392     /**
2393      * Gets a node in this tree by its id.
2394      * @param {String} id
2395      * @return {Node}
2396      */
2397     getNodeById : function(id){
2398         return this.nodeHash[id];
2399     },
2400
2401     registerNode : function(node){
2402         this.nodeHash[node.id] = node;
2403     },
2404
2405     unregisterNode : function(node){
2406         delete this.nodeHash[node.id];
2407     },
2408
2409     toString : function(){
2410         return "[Tree"+(this.id?" "+this.id:"")+"]";
2411     }
2412 });
2413
2414 /**
2415  * @class Roo.data.Node
2416  * @extends Roo.util.Observable
2417  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2418  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2419  * @constructor
2420  * @param {Object} attributes The attributes/config for the node
2421  */
2422 Roo.data.Node = function(attributes){
2423     /**
2424      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2425      * @type {Object}
2426      */
2427     this.attributes = attributes || {};
2428     this.leaf = this.attributes.leaf;
2429     /**
2430      * The node id. @type String
2431      */
2432     this.id = this.attributes.id;
2433     if(!this.id){
2434         this.id = Roo.id(null, "ynode-");
2435         this.attributes.id = this.id;
2436     }
2437      
2438     
2439     /**
2440      * All child nodes of this node. @type Array
2441      */
2442     this.childNodes = [];
2443     if(!this.childNodes.indexOf){ // indexOf is a must
2444         this.childNodes.indexOf = function(o){
2445             for(var i = 0, len = this.length; i < len; i++){
2446                 if(this[i] == o) {
2447                     return i;
2448                 }
2449             }
2450             return -1;
2451         };
2452     }
2453     /**
2454      * The parent node for this node. @type Node
2455      */
2456     this.parentNode = null;
2457     /**
2458      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2459      */
2460     this.firstChild = null;
2461     /**
2462      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2463      */
2464     this.lastChild = null;
2465     /**
2466      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2467      */
2468     this.previousSibling = null;
2469     /**
2470      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2471      */
2472     this.nextSibling = null;
2473
2474     this.addEvents({
2475        /**
2476         * @event append
2477         * Fires when a new child node is appended
2478         * @param {Tree} tree The owner tree
2479         * @param {Node} this This node
2480         * @param {Node} node The newly appended node
2481         * @param {Number} index The index of the newly appended node
2482         */
2483        "append" : true,
2484        /**
2485         * @event remove
2486         * Fires when a child node is removed
2487         * @param {Tree} tree The owner tree
2488         * @param {Node} this This node
2489         * @param {Node} node The removed node
2490         */
2491        "remove" : true,
2492        /**
2493         * @event move
2494         * Fires when this node is moved to a new location in the tree
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} oldParent The old parent of this node
2498         * @param {Node} newParent The new parent of this node
2499         * @param {Number} index The index it was moved to
2500         */
2501        "move" : true,
2502        /**
2503         * @event insert
2504         * Fires when a new child node is inserted.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} node The child node inserted
2508         * @param {Node} refNode The child node the node was inserted before
2509         */
2510        "insert" : true,
2511        /**
2512         * @event beforeappend
2513         * Fires before a new child is appended, return false to cancel the append.
2514         * @param {Tree} tree The owner tree
2515         * @param {Node} this This node
2516         * @param {Node} node The child node to be appended
2517         */
2518        "beforeappend" : true,
2519        /**
2520         * @event beforeremove
2521         * Fires before a child is removed, return false to cancel the remove.
2522         * @param {Tree} tree The owner tree
2523         * @param {Node} this This node
2524         * @param {Node} node The child node to be removed
2525         */
2526        "beforeremove" : true,
2527        /**
2528         * @event beforemove
2529         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} oldParent The parent of this node
2533         * @param {Node} newParent The new parent this node is moving to
2534         * @param {Number} index The index it is being moved to
2535         */
2536        "beforemove" : true,
2537        /**
2538         * @event beforeinsert
2539         * Fires before a new child is inserted, return false to cancel the insert.
2540         * @param {Tree} tree The owner tree
2541         * @param {Node} this This node
2542         * @param {Node} node The child node to be inserted
2543         * @param {Node} refNode The child node the node is being inserted before
2544         */
2545        "beforeinsert" : true
2546    });
2547     this.listeners = this.attributes.listeners;
2548     Roo.data.Node.superclass.constructor.call(this);
2549 };
2550
2551 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2552     fireEvent : function(evtName){
2553         // first do standard event for this node
2554         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2555             return false;
2556         }
2557         // then bubble it up to the tree if the event wasn't cancelled
2558         var ot = this.getOwnerTree();
2559         if(ot){
2560             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2561                 return false;
2562             }
2563         }
2564         return true;
2565     },
2566
2567     /**
2568      * Returns true if this node is a leaf
2569      * @return {Boolean}
2570      */
2571     isLeaf : function(){
2572         return this.leaf === true;
2573     },
2574
2575     // private
2576     setFirstChild : function(node){
2577         this.firstChild = node;
2578     },
2579
2580     //private
2581     setLastChild : function(node){
2582         this.lastChild = node;
2583     },
2584
2585
2586     /**
2587      * Returns true if this node is the last child of its parent
2588      * @return {Boolean}
2589      */
2590     isLast : function(){
2591        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2592     },
2593
2594     /**
2595      * Returns true if this node is the first child of its parent
2596      * @return {Boolean}
2597      */
2598     isFirst : function(){
2599        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2600     },
2601
2602     hasChildNodes : function(){
2603         return !this.isLeaf() && this.childNodes.length > 0;
2604     },
2605
2606     /**
2607      * Insert node(s) as the last child node of this node.
2608      * @param {Node/Array} node The node or Array of nodes to append
2609      * @return {Node} The appended node if single append, or null if an array was passed
2610      */
2611     appendChild : function(node){
2612         var multi = false;
2613         if(node instanceof Array){
2614             multi = node;
2615         }else if(arguments.length > 1){
2616             multi = arguments;
2617         }
2618         
2619         // if passed an array or multiple args do them one by one
2620         if(multi){
2621             for(var i = 0, len = multi.length; i < len; i++) {
2622                 this.appendChild(multi[i]);
2623             }
2624         }else{
2625             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2626                 return false;
2627             }
2628             var index = this.childNodes.length;
2629             var oldParent = node.parentNode;
2630             // it's a move, make sure we move it cleanly
2631             if(oldParent){
2632                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2633                     return false;
2634                 }
2635                 oldParent.removeChild(node);
2636             }
2637             
2638             index = this.childNodes.length;
2639             if(index == 0){
2640                 this.setFirstChild(node);
2641             }
2642             this.childNodes.push(node);
2643             node.parentNode = this;
2644             var ps = this.childNodes[index-1];
2645             if(ps){
2646                 node.previousSibling = ps;
2647                 ps.nextSibling = node;
2648             }else{
2649                 node.previousSibling = null;
2650             }
2651             node.nextSibling = null;
2652             this.setLastChild(node);
2653             node.setOwnerTree(this.getOwnerTree());
2654             this.fireEvent("append", this.ownerTree, this, node, index);
2655             if(this.ownerTree) {
2656                 this.ownerTree.fireEvent("appendnode", this, node, index);
2657             }
2658             if(oldParent){
2659                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2660             }
2661             return node;
2662         }
2663     },
2664
2665     /**
2666      * Removes a child node from this node.
2667      * @param {Node} node The node to remove
2668      * @return {Node} The removed node
2669      */
2670     removeChild : function(node){
2671         var index = this.childNodes.indexOf(node);
2672         if(index == -1){
2673             return false;
2674         }
2675         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2676             return false;
2677         }
2678
2679         // remove it from childNodes collection
2680         this.childNodes.splice(index, 1);
2681
2682         // update siblings
2683         if(node.previousSibling){
2684             node.previousSibling.nextSibling = node.nextSibling;
2685         }
2686         if(node.nextSibling){
2687             node.nextSibling.previousSibling = node.previousSibling;
2688         }
2689
2690         // update child refs
2691         if(this.firstChild == node){
2692             this.setFirstChild(node.nextSibling);
2693         }
2694         if(this.lastChild == node){
2695             this.setLastChild(node.previousSibling);
2696         }
2697
2698         node.setOwnerTree(null);
2699         // clear any references from the node
2700         node.parentNode = null;
2701         node.previousSibling = null;
2702         node.nextSibling = null;
2703         this.fireEvent("remove", this.ownerTree, this, node);
2704         return node;
2705     },
2706
2707     /**
2708      * Inserts the first node before the second node in this nodes childNodes collection.
2709      * @param {Node} node The node to insert
2710      * @param {Node} refNode The node to insert before (if null the node is appended)
2711      * @return {Node} The inserted node
2712      */
2713     insertBefore : function(node, refNode){
2714         if(!refNode){ // like standard Dom, refNode can be null for append
2715             return this.appendChild(node);
2716         }
2717         // nothing to do
2718         if(node == refNode){
2719             return false;
2720         }
2721
2722         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2723             return false;
2724         }
2725         var index = this.childNodes.indexOf(refNode);
2726         var oldParent = node.parentNode;
2727         var refIndex = index;
2728
2729         // when moving internally, indexes will change after remove
2730         if(oldParent == this && this.childNodes.indexOf(node) < index){
2731             refIndex--;
2732         }
2733
2734         // it's a move, make sure we move it cleanly
2735         if(oldParent){
2736             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2737                 return false;
2738             }
2739             oldParent.removeChild(node);
2740         }
2741         if(refIndex == 0){
2742             this.setFirstChild(node);
2743         }
2744         this.childNodes.splice(refIndex, 0, node);
2745         node.parentNode = this;
2746         var ps = this.childNodes[refIndex-1];
2747         if(ps){
2748             node.previousSibling = ps;
2749             ps.nextSibling = node;
2750         }else{
2751             node.previousSibling = null;
2752         }
2753         node.nextSibling = refNode;
2754         refNode.previousSibling = node;
2755         node.setOwnerTree(this.getOwnerTree());
2756         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2757         if(oldParent){
2758             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2759         }
2760         return node;
2761     },
2762
2763     /**
2764      * Returns the child node at the specified index.
2765      * @param {Number} index
2766      * @return {Node}
2767      */
2768     item : function(index){
2769         return this.childNodes[index];
2770     },
2771
2772     /**
2773      * Replaces one child node in this node with another.
2774      * @param {Node} newChild The replacement node
2775      * @param {Node} oldChild The node to replace
2776      * @return {Node} The replaced node
2777      */
2778     replaceChild : function(newChild, oldChild){
2779         this.insertBefore(newChild, oldChild);
2780         this.removeChild(oldChild);
2781         return oldChild;
2782     },
2783
2784     /**
2785      * Returns the index of a child node
2786      * @param {Node} node
2787      * @return {Number} The index of the node or -1 if it was not found
2788      */
2789     indexOf : function(child){
2790         return this.childNodes.indexOf(child);
2791     },
2792
2793     /**
2794      * Returns the tree this node is in.
2795      * @return {Tree}
2796      */
2797     getOwnerTree : function(){
2798         // if it doesn't have one, look for one
2799         if(!this.ownerTree){
2800             var p = this;
2801             while(p){
2802                 if(p.ownerTree){
2803                     this.ownerTree = p.ownerTree;
2804                     break;
2805                 }
2806                 p = p.parentNode;
2807             }
2808         }
2809         return this.ownerTree;
2810     },
2811
2812     /**
2813      * Returns depth of this node (the root node has a depth of 0)
2814      * @return {Number}
2815      */
2816     getDepth : function(){
2817         var depth = 0;
2818         var p = this;
2819         while(p.parentNode){
2820             ++depth;
2821             p = p.parentNode;
2822         }
2823         return depth;
2824     },
2825
2826     // private
2827     setOwnerTree : function(tree){
2828         // if it's move, we need to update everyone
2829         if(tree != this.ownerTree){
2830             if(this.ownerTree){
2831                 this.ownerTree.unregisterNode(this);
2832             }
2833             this.ownerTree = tree;
2834             var cs = this.childNodes;
2835             for(var i = 0, len = cs.length; i < len; i++) {
2836                 cs[i].setOwnerTree(tree);
2837             }
2838             if(tree){
2839                 tree.registerNode(this);
2840             }
2841         }
2842     },
2843
2844     /**
2845      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2846      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2847      * @return {String} The path
2848      */
2849     getPath : function(attr){
2850         attr = attr || "id";
2851         var p = this.parentNode;
2852         var b = [this.attributes[attr]];
2853         while(p){
2854             b.unshift(p.attributes[attr]);
2855             p = p.parentNode;
2856         }
2857         var sep = this.getOwnerTree().pathSeparator;
2858         return sep + b.join(sep);
2859     },
2860
2861     /**
2862      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863      * function call will be the scope provided or the current node. The arguments to the function
2864      * will be the args provided or the current node. If the function returns false at any point,
2865      * the bubble is stopped.
2866      * @param {Function} fn The function to call
2867      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2869      */
2870     bubble : function(fn, scope, args){
2871         var p = this;
2872         while(p){
2873             if(fn.call(scope || p, args || p) === false){
2874                 break;
2875             }
2876             p = p.parentNode;
2877         }
2878     },
2879
2880     /**
2881      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2882      * function call will be the scope provided or the current node. The arguments to the function
2883      * will be the args provided or the current node. If the function returns false at any point,
2884      * the cascade is stopped on that branch.
2885      * @param {Function} fn The function to call
2886      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2887      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888      */
2889     cascade : function(fn, scope, args){
2890         if(fn.call(scope || this, args || this) !== false){
2891             var cs = this.childNodes;
2892             for(var i = 0, len = cs.length; i < len; i++) {
2893                 cs[i].cascade(fn, scope, args);
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2900      * function call will be the scope provided or the current node. The arguments to the function
2901      * will be the args provided or the current node. If the function returns false at any point,
2902      * the iteration stops.
2903      * @param {Function} fn The function to call
2904      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2905      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2906      */
2907     eachChild : function(fn, scope, args){
2908         var cs = this.childNodes;
2909         for(var i = 0, len = cs.length; i < len; i++) {
2910                 if(fn.call(scope || this, args || cs[i]) === false){
2911                     break;
2912                 }
2913         }
2914     },
2915
2916     /**
2917      * Finds the first child that has the attribute with the specified value.
2918      * @param {String} attribute The attribute name
2919      * @param {Mixed} value The value to search for
2920      * @return {Node} The found child or null if none was found
2921      */
2922     findChild : function(attribute, value){
2923         var cs = this.childNodes;
2924         for(var i = 0, len = cs.length; i < len; i++) {
2925                 if(cs[i].attributes[attribute] == value){
2926                     return cs[i];
2927                 }
2928         }
2929         return null;
2930     },
2931
2932     /**
2933      * Finds the first child by a custom function. The child matches if the function passed
2934      * returns true.
2935      * @param {Function} fn
2936      * @param {Object} scope (optional)
2937      * @return {Node} The found child or null if none was found
2938      */
2939     findChildBy : function(fn, scope){
2940         var cs = this.childNodes;
2941         for(var i = 0, len = cs.length; i < len; i++) {
2942                 if(fn.call(scope||cs[i], cs[i]) === true){
2943                     return cs[i];
2944                 }
2945         }
2946         return null;
2947     },
2948
2949     /**
2950      * Sorts this nodes children using the supplied sort function
2951      * @param {Function} fn
2952      * @param {Object} scope (optional)
2953      */
2954     sort : function(fn, scope){
2955         var cs = this.childNodes;
2956         var len = cs.length;
2957         if(len > 0){
2958             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2959             cs.sort(sortFn);
2960             for(var i = 0; i < len; i++){
2961                 var n = cs[i];
2962                 n.previousSibling = cs[i-1];
2963                 n.nextSibling = cs[i+1];
2964                 if(i == 0){
2965                     this.setFirstChild(n);
2966                 }
2967                 if(i == len-1){
2968                     this.setLastChild(n);
2969                 }
2970             }
2971         }
2972     },
2973
2974     /**
2975      * Returns true if this node is an ancestor (at any point) of the passed node.
2976      * @param {Node} node
2977      * @return {Boolean}
2978      */
2979     contains : function(node){
2980         return node.isAncestor(this);
2981     },
2982
2983     /**
2984      * Returns true if the passed node is an ancestor (at any point) of this node.
2985      * @param {Node} node
2986      * @return {Boolean}
2987      */
2988     isAncestor : function(node){
2989         var p = this.parentNode;
2990         while(p){
2991             if(p == node){
2992                 return true;
2993             }
2994             p = p.parentNode;
2995         }
2996         return false;
2997     },
2998
2999     toString : function(){
3000         return "[Node"+(this.id?" "+this.id:"")+"]";
3001     }
3002 });/*
3003  * Based on:
3004  * Ext JS Library 1.1.1
3005  * Copyright(c) 2006-2007, Ext JS, LLC.
3006  *
3007  * Originally Released Under LGPL - original licence link has changed is not relivant.
3008  *
3009  * Fork - LGPL
3010  * <script type="text/javascript">
3011  */
3012
3013
3014 /**
3015  * @class Roo.Shadow
3016  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3017  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3018  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3019  * @constructor
3020  * Create a new Shadow
3021  * @param {Object} config The config object
3022  */
3023 Roo.Shadow = function(config){
3024     Roo.apply(this, config);
3025     if(typeof this.mode != "string"){
3026         this.mode = this.defaultMode;
3027     }
3028     var o = this.offset, a = {h: 0};
3029     var rad = Math.floor(this.offset/2);
3030     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3031         case "drop":
3032             a.w = 0;
3033             a.l = a.t = o;
3034             a.t -= 1;
3035             if(Roo.isIE){
3036                 a.l -= this.offset + rad;
3037                 a.t -= this.offset + rad;
3038                 a.w -= rad;
3039                 a.h -= rad;
3040                 a.t += 1;
3041             }
3042         break;
3043         case "sides":
3044             a.w = (o*2);
3045             a.l = -o;
3046             a.t = o-1;
3047             if(Roo.isIE){
3048                 a.l -= (this.offset - rad);
3049                 a.t -= this.offset + rad;
3050                 a.l += 1;
3051                 a.w -= (this.offset - rad)*2;
3052                 a.w -= rad + 1;
3053                 a.h -= 1;
3054             }
3055         break;
3056         case "frame":
3057             a.w = a.h = (o*2);
3058             a.l = a.t = -o;
3059             a.t += 1;
3060             a.h -= 2;
3061             if(Roo.isIE){
3062                 a.l -= (this.offset - rad);
3063                 a.t -= (this.offset - rad);
3064                 a.l += 1;
3065                 a.w -= (this.offset + rad + 1);
3066                 a.h -= (this.offset + rad);
3067                 a.h += 1;
3068             }
3069         break;
3070     };
3071
3072     this.adjusts = a;
3073 };
3074
3075 Roo.Shadow.prototype = {
3076     /**
3077      * @cfg {String} mode
3078      * The shadow display mode.  Supports the following options:<br />
3079      * sides: Shadow displays on both sides and bottom only<br />
3080      * frame: Shadow displays equally on all four sides<br />
3081      * drop: Traditional bottom-right drop shadow (default)
3082      */
3083     mode: false,
3084     /**
3085      * @cfg {String} offset
3086      * The number of pixels to offset the shadow from the element (defaults to 4)
3087      */
3088     offset: 4,
3089
3090     // private
3091     defaultMode: "drop",
3092
3093     /**
3094      * Displays the shadow under the target element
3095      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3096      */
3097     show : function(target){
3098         target = Roo.get(target);
3099         if(!this.el){
3100             this.el = Roo.Shadow.Pool.pull();
3101             if(this.el.dom.nextSibling != target.dom){
3102                 this.el.insertBefore(target);
3103             }
3104         }
3105         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3106         if(Roo.isIE){
3107             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3108         }
3109         this.realign(
3110             target.getLeft(true),
3111             target.getTop(true),
3112             target.getWidth(),
3113             target.getHeight()
3114         );
3115         this.el.dom.style.display = "block";
3116     },
3117
3118     /**
3119      * Returns true if the shadow is visible, else false
3120      */
3121     isVisible : function(){
3122         return this.el ? true : false;  
3123     },
3124
3125     /**
3126      * Direct alignment when values are already available. Show must be called at least once before
3127      * calling this method to ensure it is initialized.
3128      * @param {Number} left The target element left position
3129      * @param {Number} top The target element top position
3130      * @param {Number} width The target element width
3131      * @param {Number} height The target element height
3132      */
3133     realign : function(l, t, w, h){
3134         if(!this.el){
3135             return;
3136         }
3137         var a = this.adjusts, d = this.el.dom, s = d.style;
3138         var iea = 0;
3139         s.left = (l+a.l)+"px";
3140         s.top = (t+a.t)+"px";
3141         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3142  
3143         if(s.width != sws || s.height != shs){
3144             s.width = sws;
3145             s.height = shs;
3146             if(!Roo.isIE){
3147                 var cn = d.childNodes;
3148                 var sww = Math.max(0, (sw-12))+"px";
3149                 cn[0].childNodes[1].style.width = sww;
3150                 cn[1].childNodes[1].style.width = sww;
3151                 cn[2].childNodes[1].style.width = sww;
3152                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3153             }
3154         }
3155     },
3156
3157     /**
3158      * Hides this shadow
3159      */
3160     hide : function(){
3161         if(this.el){
3162             this.el.dom.style.display = "none";
3163             Roo.Shadow.Pool.push(this.el);
3164             delete this.el;
3165         }
3166     },
3167
3168     /**
3169      * Adjust the z-index of this shadow
3170      * @param {Number} zindex The new z-index
3171      */
3172     setZIndex : function(z){
3173         this.zIndex = z;
3174         if(this.el){
3175             this.el.setStyle("z-index", z);
3176         }
3177     }
3178 };
3179
3180 // Private utility class that manages the internal Shadow cache
3181 Roo.Shadow.Pool = function(){
3182     var p = [];
3183     var markup = Roo.isIE ?
3184                  '<div class="x-ie-shadow"></div>' :
3185                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3186     return {
3187         pull : function(){
3188             var sh = p.shift();
3189             if(!sh){
3190                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3191                 sh.autoBoxAdjust = false;
3192             }
3193             return sh;
3194         },
3195
3196         push : function(sh){
3197             p.push(sh);
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210
3211
3212 /**
3213  * @class Roo.SplitBar
3214  * @extends Roo.util.Observable
3215  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3216  * <br><br>
3217  * Usage:
3218  * <pre><code>
3219 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3220                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3221 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3222 split.minSize = 100;
3223 split.maxSize = 600;
3224 split.animate = true;
3225 split.on('moved', splitterMoved);
3226 </code></pre>
3227  * @constructor
3228  * Create a new SplitBar
3229  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3230  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3231  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3232  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3233                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3234                         position of the SplitBar).
3235  */
3236 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3237     
3238     /** @private */
3239     this.el = Roo.get(dragElement, true);
3240     this.el.dom.unselectable = "on";
3241     /** @private */
3242     this.resizingEl = Roo.get(resizingElement, true);
3243
3244     /**
3245      * @private
3246      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3247      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3248      * @type Number
3249      */
3250     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3251     
3252     /**
3253      * The minimum size of the resizing element. (Defaults to 0)
3254      * @type Number
3255      */
3256     this.minSize = 0;
3257     
3258     /**
3259      * The maximum size of the resizing element. (Defaults to 2000)
3260      * @type Number
3261      */
3262     this.maxSize = 2000;
3263     
3264     /**
3265      * Whether to animate the transition to the new size
3266      * @type Boolean
3267      */
3268     this.animate = false;
3269     
3270     /**
3271      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3272      * @type Boolean
3273      */
3274     this.useShim = false;
3275     
3276     /** @private */
3277     this.shim = null;
3278     
3279     if(!existingProxy){
3280         /** @private */
3281         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3282     }else{
3283         this.proxy = Roo.get(existingProxy).dom;
3284     }
3285     /** @private */
3286     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3287     
3288     /** @private */
3289     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3290     
3291     /** @private */
3292     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3293     
3294     /** @private */
3295     this.dragSpecs = {};
3296     
3297     /**
3298      * @private The adapter to use to positon and resize elements
3299      */
3300     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3301     this.adapter.init(this);
3302     
3303     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3304         /** @private */
3305         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3306         this.el.addClass("x-splitbar-h");
3307     }else{
3308         /** @private */
3309         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3310         this.el.addClass("x-splitbar-v");
3311     }
3312     
3313     this.addEvents({
3314         /**
3315          * @event resize
3316          * Fires when the splitter is moved (alias for {@link #event-moved})
3317          * @param {Roo.SplitBar} this
3318          * @param {Number} newSize the new width or height
3319          */
3320         "resize" : true,
3321         /**
3322          * @event moved
3323          * Fires when the splitter is moved
3324          * @param {Roo.SplitBar} this
3325          * @param {Number} newSize the new width or height
3326          */
3327         "moved" : true,
3328         /**
3329          * @event beforeresize
3330          * Fires before the splitter is dragged
3331          * @param {Roo.SplitBar} this
3332          */
3333         "beforeresize" : true,
3334
3335         "beforeapply" : true
3336     });
3337
3338     Roo.util.Observable.call(this);
3339 };
3340
3341 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3342     onStartProxyDrag : function(x, y){
3343         this.fireEvent("beforeresize", this);
3344         if(!this.overlay){
3345             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3346             o.unselectable();
3347             o.enableDisplayMode("block");
3348             // all splitbars share the same overlay
3349             Roo.SplitBar.prototype.overlay = o;
3350         }
3351         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3352         this.overlay.show();
3353         Roo.get(this.proxy).setDisplayed("block");
3354         var size = this.adapter.getElementSize(this);
3355         this.activeMinSize = this.getMinimumSize();;
3356         this.activeMaxSize = this.getMaximumSize();;
3357         var c1 = size - this.activeMinSize;
3358         var c2 = Math.max(this.activeMaxSize - size, 0);
3359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3360             this.dd.resetConstraints();
3361             this.dd.setXConstraint(
3362                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3363                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3364             );
3365             this.dd.setYConstraint(0, 0);
3366         }else{
3367             this.dd.resetConstraints();
3368             this.dd.setXConstraint(0, 0);
3369             this.dd.setYConstraint(
3370                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3371                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3372             );
3373          }
3374         this.dragSpecs.startSize = size;
3375         this.dragSpecs.startPoint = [x, y];
3376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3377     },
3378     
3379     /** 
3380      * @private Called after the drag operation by the DDProxy
3381      */
3382     onEndProxyDrag : function(e){
3383         Roo.get(this.proxy).setDisplayed(false);
3384         var endPoint = Roo.lib.Event.getXY(e);
3385         if(this.overlay){
3386             this.overlay.hide();
3387         }
3388         var newSize;
3389         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3390             newSize = this.dragSpecs.startSize + 
3391                 (this.placement == Roo.SplitBar.LEFT ?
3392                     endPoint[0] - this.dragSpecs.startPoint[0] :
3393                     this.dragSpecs.startPoint[0] - endPoint[0]
3394                 );
3395         }else{
3396             newSize = this.dragSpecs.startSize + 
3397                 (this.placement == Roo.SplitBar.TOP ?
3398                     endPoint[1] - this.dragSpecs.startPoint[1] :
3399                     this.dragSpecs.startPoint[1] - endPoint[1]
3400                 );
3401         }
3402         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3403         if(newSize != this.dragSpecs.startSize){
3404             if(this.fireEvent('beforeapply', this, newSize) !== false){
3405                 this.adapter.setElementSize(this, newSize);
3406                 this.fireEvent("moved", this, newSize);
3407                 this.fireEvent("resize", this, newSize);
3408             }
3409         }
3410     },
3411     
3412     /**
3413      * Get the adapter this SplitBar uses
3414      * @return The adapter object
3415      */
3416     getAdapter : function(){
3417         return this.adapter;
3418     },
3419     
3420     /**
3421      * Set the adapter this SplitBar uses
3422      * @param {Object} adapter A SplitBar adapter object
3423      */
3424     setAdapter : function(adapter){
3425         this.adapter = adapter;
3426         this.adapter.init(this);
3427     },
3428     
3429     /**
3430      * Gets the minimum size for the resizing element
3431      * @return {Number} The minimum size
3432      */
3433     getMinimumSize : function(){
3434         return this.minSize;
3435     },
3436     
3437     /**
3438      * Sets the minimum size for the resizing element
3439      * @param {Number} minSize The minimum size
3440      */
3441     setMinimumSize : function(minSize){
3442         this.minSize = minSize;
3443     },
3444     
3445     /**
3446      * Gets the maximum size for the resizing element
3447      * @return {Number} The maximum size
3448      */
3449     getMaximumSize : function(){
3450         return this.maxSize;
3451     },
3452     
3453     /**
3454      * Sets the maximum size for the resizing element
3455      * @param {Number} maxSize The maximum size
3456      */
3457     setMaximumSize : function(maxSize){
3458         this.maxSize = maxSize;
3459     },
3460     
3461     /**
3462      * Sets the initialize size for the resizing element
3463      * @param {Number} size The initial size
3464      */
3465     setCurrentSize : function(size){
3466         var oldAnimate = this.animate;
3467         this.animate = false;
3468         this.adapter.setElementSize(this, size);
3469         this.animate = oldAnimate;
3470     },
3471     
3472     /**
3473      * Destroy this splitbar. 
3474      * @param {Boolean} removeEl True to remove the element
3475      */
3476     destroy : function(removeEl){
3477         if(this.shim){
3478             this.shim.remove();
3479         }
3480         this.dd.unreg();
3481         this.proxy.parentNode.removeChild(this.proxy);
3482         if(removeEl){
3483             this.el.remove();
3484         }
3485     }
3486 });
3487
3488 /**
3489  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3490  */
3491 Roo.SplitBar.createProxy = function(dir){
3492     var proxy = new Roo.Element(document.createElement("div"));
3493     proxy.unselectable();
3494     var cls = 'x-splitbar-proxy';
3495     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3496     document.body.appendChild(proxy.dom);
3497     return proxy.dom;
3498 };
3499
3500 /** 
3501  * @class Roo.SplitBar.BasicLayoutAdapter
3502  * Default Adapter. It assumes the splitter and resizing element are not positioned
3503  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3504  */
3505 Roo.SplitBar.BasicLayoutAdapter = function(){
3506 };
3507
3508 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3509     // do nothing for now
3510     init : function(s){
3511     
3512     },
3513     /**
3514      * Called before drag operations to get the current size of the resizing element. 
3515      * @param {Roo.SplitBar} s The SplitBar using this adapter
3516      */
3517      getElementSize : function(s){
3518         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3519             return s.resizingEl.getWidth();
3520         }else{
3521             return s.resizingEl.getHeight();
3522         }
3523     },
3524     
3525     /**
3526      * Called after drag operations to set the size of the resizing element.
3527      * @param {Roo.SplitBar} s The SplitBar using this adapter
3528      * @param {Number} newSize The new size to set
3529      * @param {Function} onComplete A function to be invoked when resizing is complete
3530      */
3531     setElementSize : function(s, newSize, onComplete){
3532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3533             if(!s.animate){
3534                 s.resizingEl.setWidth(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }else{
3542             
3543             if(!s.animate){
3544                 s.resizingEl.setHeight(newSize);
3545                 if(onComplete){
3546                     onComplete(s, newSize);
3547                 }
3548             }else{
3549                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3550             }
3551         }
3552     }
3553 };
3554
3555 /** 
3556  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3557  * @extends Roo.SplitBar.BasicLayoutAdapter
3558  * Adapter that  moves the splitter element to align with the resized sizing element. 
3559  * Used with an absolute positioned SplitBar.
3560  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3561  * document.body, make sure you assign an id to the body element.
3562  */
3563 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3564     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3565     this.container = Roo.get(container);
3566 };
3567
3568 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3569     init : function(s){
3570         this.basic.init(s);
3571     },
3572     
3573     getElementSize : function(s){
3574         return this.basic.getElementSize(s);
3575     },
3576     
3577     setElementSize : function(s, newSize, onComplete){
3578         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3579     },
3580     
3581     moveSplitter : function(s){
3582         var yes = Roo.SplitBar;
3583         switch(s.placement){
3584             case yes.LEFT:
3585                 s.el.setX(s.resizingEl.getRight());
3586                 break;
3587             case yes.RIGHT:
3588                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3589                 break;
3590             case yes.TOP:
3591                 s.el.setY(s.resizingEl.getBottom());
3592                 break;
3593             case yes.BOTTOM:
3594                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3595                 break;
3596         }
3597     }
3598 };
3599
3600 /**
3601  * Orientation constant - Create a vertical SplitBar
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.VERTICAL = 1;
3606
3607 /**
3608  * Orientation constant - Create a horizontal SplitBar
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.HORIZONTAL = 2;
3613
3614 /**
3615  * Placement constant - The resizing element is to the left of the splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.LEFT = 1;
3620
3621 /**
3622  * Placement constant - The resizing element is to the right of the splitter element
3623  * @static
3624  * @type Number
3625  */
3626 Roo.SplitBar.RIGHT = 2;
3627
3628 /**
3629  * Placement constant - The resizing element is positioned above the splitter element
3630  * @static
3631  * @type Number
3632  */
3633 Roo.SplitBar.TOP = 3;
3634
3635 /**
3636  * Placement constant - The resizing element is positioned under splitter element
3637  * @static
3638  * @type Number
3639  */
3640 Roo.SplitBar.BOTTOM = 4;
3641 /*
3642  * Based on:
3643  * Ext JS Library 1.1.1
3644  * Copyright(c) 2006-2007, Ext JS, LLC.
3645  *
3646  * Originally Released Under LGPL - original licence link has changed is not relivant.
3647  *
3648  * Fork - LGPL
3649  * <script type="text/javascript">
3650  */
3651
3652 /**
3653  * @class Roo.View
3654  * @extends Roo.util.Observable
3655  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3656  * This class also supports single and multi selection modes. <br>
3657  * Create a data model bound view:
3658  <pre><code>
3659  var store = new Roo.data.Store(...);
3660
3661  var view = new Roo.View({
3662     el : "my-element",
3663     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3664  
3665     singleSelect: true,
3666     selectedClass: "ydataview-selected",
3667     store: store
3668  });
3669
3670  // listen for node click?
3671  view.on("click", function(vw, index, node, e){
3672  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3673  });
3674
3675  // load XML data
3676  dataModel.load("foobar.xml");
3677  </code></pre>
3678  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3679  * <br><br>
3680  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3681  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3682  * 
3683  * Note: old style constructor is still suported (container, template, config)
3684  * 
3685  * @constructor
3686  * Create a new View
3687  * @param {Object} config The config object
3688  * 
3689  */
3690 Roo.View = function(config, depreciated_tpl, depreciated_config){
3691     
3692     this.parent = false;
3693     
3694     if (typeof(depreciated_tpl) == 'undefined') {
3695         // new way.. - universal constructor.
3696         Roo.apply(this, config);
3697         this.el  = Roo.get(this.el);
3698     } else {
3699         // old format..
3700         this.el  = Roo.get(config);
3701         this.tpl = depreciated_tpl;
3702         Roo.apply(this, depreciated_config);
3703     }
3704     this.wrapEl  = this.el.wrap().wrap();
3705     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3706     
3707     
3708     if(typeof(this.tpl) == "string"){
3709         this.tpl = new Roo.Template(this.tpl);
3710     } else {
3711         // support xtype ctors..
3712         this.tpl = new Roo.factory(this.tpl, Roo);
3713     }
3714     
3715     
3716     this.tpl.compile();
3717     
3718     /** @private */
3719     this.addEvents({
3720         /**
3721          * @event beforeclick
3722          * Fires before a click is processed. Returns false to cancel the default action.
3723          * @param {Roo.View} this
3724          * @param {Number} index The index of the target node
3725          * @param {HTMLElement} node The target node
3726          * @param {Roo.EventObject} e The raw event object
3727          */
3728             "beforeclick" : true,
3729         /**
3730          * @event click
3731          * Fires when a template node is clicked.
3732          * @param {Roo.View} this
3733          * @param {Number} index The index of the target node
3734          * @param {HTMLElement} node The target node
3735          * @param {Roo.EventObject} e The raw event object
3736          */
3737             "click" : true,
3738         /**
3739          * @event dblclick
3740          * Fires when a template node is double clicked.
3741          * @param {Roo.View} this
3742          * @param {Number} index The index of the target node
3743          * @param {HTMLElement} node The target node
3744          * @param {Roo.EventObject} e The raw event object
3745          */
3746             "dblclick" : true,
3747         /**
3748          * @event contextmenu
3749          * Fires when a template node is right clicked.
3750          * @param {Roo.View} this
3751          * @param {Number} index The index of the target node
3752          * @param {HTMLElement} node The target node
3753          * @param {Roo.EventObject} e The raw event object
3754          */
3755             "contextmenu" : true,
3756         /**
3757          * @event selectionchange
3758          * Fires when the selected nodes change.
3759          * @param {Roo.View} this
3760          * @param {Array} selections Array of the selected nodes
3761          */
3762             "selectionchange" : true,
3763     
3764         /**
3765          * @event beforeselect
3766          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3767          * @param {Roo.View} this
3768          * @param {HTMLElement} node The node to be selected
3769          * @param {Array} selections Array of currently selected nodes
3770          */
3771             "beforeselect" : true,
3772         /**
3773          * @event preparedata
3774          * Fires on every row to render, to allow you to change the data.
3775          * @param {Roo.View} this
3776          * @param {Object} data to be rendered (change this)
3777          */
3778           "preparedata" : true
3779           
3780           
3781         });
3782
3783
3784
3785     this.el.on({
3786         "click": this.onClick,
3787         "dblclick": this.onDblClick,
3788         "contextmenu": this.onContextMenu,
3789         scope:this
3790     });
3791
3792     this.selections = [];
3793     this.nodes = [];
3794     this.cmp = new Roo.CompositeElementLite([]);
3795     if(this.store){
3796         this.store = Roo.factory(this.store, Roo.data);
3797         this.setStore(this.store, true);
3798     }
3799     
3800     if ( this.footer && this.footer.xtype) {
3801            
3802          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3803         
3804         this.footer.dataSource = this.store;
3805         this.footer.container = fctr;
3806         this.footer = Roo.factory(this.footer, Roo);
3807         fctr.insertFirst(this.el);
3808         
3809         // this is a bit insane - as the paging toolbar seems to detach the el..
3810 //        dom.parentNode.parentNode.parentNode
3811          // they get detached?
3812     }
3813     
3814     
3815     Roo.View.superclass.constructor.call(this);
3816     
3817     
3818 };
3819
3820 Roo.extend(Roo.View, Roo.util.Observable, {
3821     
3822      /**
3823      * @cfg {Roo.data.Store} store Data store to load data from.
3824      */
3825     store : false,
3826     
3827     /**
3828      * @cfg {String|Roo.Element} el The container element.
3829      */
3830     el : '',
3831     
3832     /**
3833      * @cfg {String|Roo.Template} tpl The template used by this View 
3834      */
3835     tpl : false,
3836     /**
3837      * @cfg {String} dataName the named area of the template to use as the data area
3838      *                          Works with domtemplates roo-name="name"
3839      */
3840     dataName: false,
3841     /**
3842      * @cfg {String} selectedClass The css class to add to selected nodes
3843      */
3844     selectedClass : "x-view-selected",
3845      /**
3846      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3847      */
3848     emptyText : "",
3849     
3850     /**
3851      * @cfg {String} text to display on mask (default Loading)
3852      */
3853     mask : false,
3854     /**
3855      * @cfg {Boolean} multiSelect Allow multiple selection
3856      */
3857     multiSelect : false,
3858     /**
3859      * @cfg {Boolean} singleSelect Allow single selection
3860      */
3861     singleSelect:  false,
3862     
3863     /**
3864      * @cfg {Boolean} toggleSelect - selecting 
3865      */
3866     toggleSelect : false,
3867     
3868     /**
3869      * @cfg {Boolean} tickable - selecting 
3870      */
3871     tickable : false,
3872     
3873     /**
3874      * Returns the element this view is bound to.
3875      * @return {Roo.Element}
3876      */
3877     getEl : function(){
3878         return this.wrapEl;
3879     },
3880     
3881     
3882
3883     /**
3884      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3885      */
3886     refresh : function(){
3887         //Roo.log('refresh');
3888         var t = this.tpl;
3889         
3890         // if we are using something like 'domtemplate', then
3891         // the what gets used is:
3892         // t.applySubtemplate(NAME, data, wrapping data..)
3893         // the outer template then get' applied with
3894         //     the store 'extra data'
3895         // and the body get's added to the
3896         //      roo-name="data" node?
3897         //      <span class='roo-tpl-{name}'></span> ?????
3898         
3899         
3900         
3901         this.clearSelections();
3902         this.el.update("");
3903         var html = [];
3904         var records = this.store.getRange();
3905         if(records.length < 1) {
3906             
3907             // is this valid??  = should it render a template??
3908             
3909             this.el.update(this.emptyText);
3910             return;
3911         }
3912         var el = this.el;
3913         if (this.dataName) {
3914             this.el.update(t.apply(this.store.meta)); //????
3915             el = this.el.child('.roo-tpl-' + this.dataName);
3916         }
3917         
3918         for(var i = 0, len = records.length; i < len; i++){
3919             var data = this.prepareData(records[i].data, i, records[i]);
3920             this.fireEvent("preparedata", this, data, i, records[i]);
3921             
3922             var d = Roo.apply({}, data);
3923             
3924             if(this.tickable){
3925                 Roo.apply(d, {'roo-id' : Roo.id()});
3926                 
3927                 var _this = this;
3928             
3929                 Roo.each(this.parent.item, function(item){
3930                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3931                         return;
3932                     }
3933                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3934                 });
3935             }
3936             
3937             html[html.length] = Roo.util.Format.trim(
3938                 this.dataName ?
3939                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3940                     t.apply(d)
3941             );
3942         }
3943         
3944         
3945         
3946         el.update(html.join(""));
3947         this.nodes = el.dom.childNodes;
3948         this.updateIndexes(0);
3949     },
3950     
3951
3952     /**
3953      * Function to override to reformat the data that is sent to
3954      * the template for each node.
3955      * DEPRICATED - use the preparedata event handler.
3956      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3957      * a JSON object for an UpdateManager bound view).
3958      */
3959     prepareData : function(data, index, record)
3960     {
3961         this.fireEvent("preparedata", this, data, index, record);
3962         return data;
3963     },
3964
3965     onUpdate : function(ds, record){
3966         // Roo.log('on update');   
3967         this.clearSelections();
3968         var index = this.store.indexOf(record);
3969         var n = this.nodes[index];
3970         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3971         n.parentNode.removeChild(n);
3972         this.updateIndexes(index, index);
3973     },
3974
3975     
3976     
3977 // --------- FIXME     
3978     onAdd : function(ds, records, index)
3979     {
3980         //Roo.log(['on Add', ds, records, index] );        
3981         this.clearSelections();
3982         if(this.nodes.length == 0){
3983             this.refresh();
3984             return;
3985         }
3986         var n = this.nodes[index];
3987         for(var i = 0, len = records.length; i < len; i++){
3988             var d = this.prepareData(records[i].data, i, records[i]);
3989             if(n){
3990                 this.tpl.insertBefore(n, d);
3991             }else{
3992                 
3993                 this.tpl.append(this.el, d);
3994             }
3995         }
3996         this.updateIndexes(index);
3997     },
3998
3999     onRemove : function(ds, record, index){
4000        // Roo.log('onRemove');
4001         this.clearSelections();
4002         var el = this.dataName  ?
4003             this.el.child('.roo-tpl-' + this.dataName) :
4004             this.el; 
4005         
4006         el.dom.removeChild(this.nodes[index]);
4007         this.updateIndexes(index);
4008     },
4009
4010     /**
4011      * Refresh an individual node.
4012      * @param {Number} index
4013      */
4014     refreshNode : function(index){
4015         this.onUpdate(this.store, this.store.getAt(index));
4016     },
4017
4018     updateIndexes : function(startIndex, endIndex){
4019         var ns = this.nodes;
4020         startIndex = startIndex || 0;
4021         endIndex = endIndex || ns.length - 1;
4022         for(var i = startIndex; i <= endIndex; i++){
4023             ns[i].nodeIndex = i;
4024         }
4025     },
4026
4027     /**
4028      * Changes the data store this view uses and refresh the view.
4029      * @param {Store} store
4030      */
4031     setStore : function(store, initial){
4032         if(!initial && this.store){
4033             this.store.un("datachanged", this.refresh);
4034             this.store.un("add", this.onAdd);
4035             this.store.un("remove", this.onRemove);
4036             this.store.un("update", this.onUpdate);
4037             this.store.un("clear", this.refresh);
4038             this.store.un("beforeload", this.onBeforeLoad);
4039             this.store.un("load", this.onLoad);
4040             this.store.un("loadexception", this.onLoad);
4041         }
4042         if(store){
4043           
4044             store.on("datachanged", this.refresh, this);
4045             store.on("add", this.onAdd, this);
4046             store.on("remove", this.onRemove, this);
4047             store.on("update", this.onUpdate, this);
4048             store.on("clear", this.refresh, this);
4049             store.on("beforeload", this.onBeforeLoad, this);
4050             store.on("load", this.onLoad, this);
4051             store.on("loadexception", this.onLoad, this);
4052         }
4053         
4054         if(store){
4055             this.refresh();
4056         }
4057     },
4058     /**
4059      * onbeforeLoad - masks the loading area.
4060      *
4061      */
4062     onBeforeLoad : function(store,opts)
4063     {
4064          //Roo.log('onBeforeLoad');   
4065         if (!opts.add) {
4066             this.el.update("");
4067         }
4068         this.el.mask(this.mask ? this.mask : "Loading" ); 
4069     },
4070     onLoad : function ()
4071     {
4072         this.el.unmask();
4073     },
4074     
4075
4076     /**
4077      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4078      * @param {HTMLElement} node
4079      * @return {HTMLElement} The template node
4080      */
4081     findItemFromChild : function(node){
4082         var el = this.dataName  ?
4083             this.el.child('.roo-tpl-' + this.dataName,true) :
4084             this.el.dom; 
4085         
4086         if(!node || node.parentNode == el){
4087                     return node;
4088             }
4089             var p = node.parentNode;
4090             while(p && p != el){
4091             if(p.parentNode == el){
4092                 return p;
4093             }
4094             p = p.parentNode;
4095         }
4096             return null;
4097     },
4098
4099     /** @ignore */
4100     onClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             var index = this.indexOf(item);
4104             if(this.onItemClick(item, index, e) !== false){
4105                 this.fireEvent("click", this, index, item, e);
4106             }
4107         }else{
4108             this.clearSelections();
4109         }
4110     },
4111
4112     /** @ignore */
4113     onContextMenu : function(e){
4114         var item = this.findItemFromChild(e.getTarget());
4115         if(item){
4116             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4117         }
4118     },
4119
4120     /** @ignore */
4121     onDblClick : function(e){
4122         var item = this.findItemFromChild(e.getTarget());
4123         if(item){
4124             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4125         }
4126     },
4127
4128     onItemClick : function(item, index, e)
4129     {
4130         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4131             return false;
4132         }
4133         if (this.toggleSelect) {
4134             var m = this.isSelected(item) ? 'unselect' : 'select';
4135             //Roo.log(m);
4136             var _t = this;
4137             _t[m](item, true, false);
4138             return true;
4139         }
4140         if(this.multiSelect || this.singleSelect){
4141             if(this.multiSelect && e.shiftKey && this.lastSelection){
4142                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4143             }else{
4144                 this.select(item, this.multiSelect && e.ctrlKey);
4145                 this.lastSelection = item;
4146             }
4147             
4148             if(!this.tickable){
4149                 e.preventDefault();
4150             }
4151             
4152         }
4153         return true;
4154     },
4155
4156     /**
4157      * Get the number of selected nodes.
4158      * @return {Number}
4159      */
4160     getSelectionCount : function(){
4161         return this.selections.length;
4162     },
4163
4164     /**
4165      * Get the currently selected nodes.
4166      * @return {Array} An array of HTMLElements
4167      */
4168     getSelectedNodes : function(){
4169         return this.selections;
4170     },
4171
4172     /**
4173      * Get the indexes of the selected nodes.
4174      * @return {Array}
4175      */
4176     getSelectedIndexes : function(){
4177         var indexes = [], s = this.selections;
4178         for(var i = 0, len = s.length; i < len; i++){
4179             indexes.push(s[i].nodeIndex);
4180         }
4181         return indexes;
4182     },
4183
4184     /**
4185      * Clear all selections
4186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4187      */
4188     clearSelections : function(suppressEvent){
4189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4190             this.cmp.elements = this.selections;
4191             this.cmp.removeClass(this.selectedClass);
4192             this.selections = [];
4193             if(!suppressEvent){
4194                 this.fireEvent("selectionchange", this, this.selections);
4195             }
4196         }
4197     },
4198
4199     /**
4200      * Returns true if the passed node is selected
4201      * @param {HTMLElement/Number} node The node or node index
4202      * @return {Boolean}
4203      */
4204     isSelected : function(node){
4205         var s = this.selections;
4206         if(s.length < 1){
4207             return false;
4208         }
4209         node = this.getNode(node);
4210         return s.indexOf(node) !== -1;
4211     },
4212
4213     /**
4214      * Selects nodes.
4215      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4216      * @param {Boolean} keepExisting (optional) true to keep existing selections
4217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4218      */
4219     select : function(nodeInfo, keepExisting, suppressEvent){
4220         if(nodeInfo instanceof Array){
4221             if(!keepExisting){
4222                 this.clearSelections(true);
4223             }
4224             for(var i = 0, len = nodeInfo.length; i < len; i++){
4225                 this.select(nodeInfo[i], true, true);
4226             }
4227             return;
4228         } 
4229         var node = this.getNode(nodeInfo);
4230         if(!node || this.isSelected(node)){
4231             return; // already selected.
4232         }
4233         if(!keepExisting){
4234             this.clearSelections(true);
4235         }
4236         
4237         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4238             Roo.fly(node).addClass(this.selectedClass);
4239             this.selections.push(node);
4240             if(!suppressEvent){
4241                 this.fireEvent("selectionchange", this, this.selections);
4242             }
4243         }
4244         
4245         
4246     },
4247       /**
4248      * Unselects nodes.
4249      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4250      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4251      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4252      */
4253     unselect : function(nodeInfo, keepExisting, suppressEvent)
4254     {
4255         if(nodeInfo instanceof Array){
4256             Roo.each(this.selections, function(s) {
4257                 this.unselect(s, nodeInfo);
4258             }, this);
4259             return;
4260         }
4261         var node = this.getNode(nodeInfo);
4262         if(!node || !this.isSelected(node)){
4263             //Roo.log("not selected");
4264             return; // not selected.
4265         }
4266         // fireevent???
4267         var ns = [];
4268         Roo.each(this.selections, function(s) {
4269             if (s == node ) {
4270                 Roo.fly(node).removeClass(this.selectedClass);
4271
4272                 return;
4273             }
4274             ns.push(s);
4275         },this);
4276         
4277         this.selections= ns;
4278         this.fireEvent("selectionchange", this, this.selections);
4279     },
4280
4281     /**
4282      * Gets a template node.
4283      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4284      * @return {HTMLElement} The node or null if it wasn't found
4285      */
4286     getNode : function(nodeInfo){
4287         if(typeof nodeInfo == "string"){
4288             return document.getElementById(nodeInfo);
4289         }else if(typeof nodeInfo == "number"){
4290             return this.nodes[nodeInfo];
4291         }
4292         return nodeInfo;
4293     },
4294
4295     /**
4296      * Gets a range template nodes.
4297      * @param {Number} startIndex
4298      * @param {Number} endIndex
4299      * @return {Array} An array of nodes
4300      */
4301     getNodes : function(start, end){
4302         var ns = this.nodes;
4303         start = start || 0;
4304         end = typeof end == "undefined" ? ns.length - 1 : end;
4305         var nodes = [];
4306         if(start <= end){
4307             for(var i = start; i <= end; i++){
4308                 nodes.push(ns[i]);
4309             }
4310         } else{
4311             for(var i = start; i >= end; i--){
4312                 nodes.push(ns[i]);
4313             }
4314         }
4315         return nodes;
4316     },
4317
4318     /**
4319      * Finds the index of the passed node
4320      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4321      * @return {Number} The index of the node or -1
4322      */
4323     indexOf : function(node){
4324         node = this.getNode(node);
4325         if(typeof node.nodeIndex == "number"){
4326             return node.nodeIndex;
4327         }
4328         var ns = this.nodes;
4329         for(var i = 0, len = ns.length; i < len; i++){
4330             if(ns[i] == node){
4331                 return i;
4332             }
4333         }
4334         return -1;
4335     }
4336 });
4337 /*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348 /**
4349  * @class Roo.JsonView
4350  * @extends Roo.View
4351  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4352 <pre><code>
4353 var view = new Roo.JsonView({
4354     container: "my-element",
4355     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4356     multiSelect: true, 
4357     jsonRoot: "data" 
4358 });
4359
4360 // listen for node click?
4361 view.on("click", function(vw, index, node, e){
4362     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4363 });
4364
4365 // direct load of JSON data
4366 view.load("foobar.php");
4367
4368 // Example from my blog list
4369 var tpl = new Roo.Template(
4370     '&lt;div class="entry"&gt;' +
4371     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4372     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4373     "&lt;/div&gt;&lt;hr /&gt;"
4374 );
4375
4376 var moreView = new Roo.JsonView({
4377     container :  "entry-list", 
4378     template : tpl,
4379     jsonRoot: "posts"
4380 });
4381 moreView.on("beforerender", this.sortEntries, this);
4382 moreView.load({
4383     url: "/blog/get-posts.php",
4384     params: "allposts=true",
4385     text: "Loading Blog Entries..."
4386 });
4387 </code></pre>
4388
4389 * Note: old code is supported with arguments : (container, template, config)
4390
4391
4392  * @constructor
4393  * Create a new JsonView
4394  * 
4395  * @param {Object} config The config object
4396  * 
4397  */
4398 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4399     
4400     
4401     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4402
4403     var um = this.el.getUpdateManager();
4404     um.setRenderer(this);
4405     um.on("update", this.onLoad, this);
4406     um.on("failure", this.onLoadException, this);
4407
4408     /**
4409      * @event beforerender
4410      * Fires before rendering of the downloaded JSON data.
4411      * @param {Roo.JsonView} this
4412      * @param {Object} data The JSON data loaded
4413      */
4414     /**
4415      * @event load
4416      * Fires when data is loaded.
4417      * @param {Roo.JsonView} this
4418      * @param {Object} data The JSON data loaded
4419      * @param {Object} response The raw Connect response object
4420      */
4421     /**
4422      * @event loadexception
4423      * Fires when loading fails.
4424      * @param {Roo.JsonView} this
4425      * @param {Object} response The raw Connect response object
4426      */
4427     this.addEvents({
4428         'beforerender' : true,
4429         'load' : true,
4430         'loadexception' : true
4431     });
4432 };
4433 Roo.extend(Roo.JsonView, Roo.View, {
4434     /**
4435      * @type {String} The root property in the loaded JSON object that contains the data
4436      */
4437     jsonRoot : "",
4438
4439     /**
4440      * Refreshes the view.
4441      */
4442     refresh : function(){
4443         this.clearSelections();
4444         this.el.update("");
4445         var html = [];
4446         var o = this.jsonData;
4447         if(o && o.length > 0){
4448             for(var i = 0, len = o.length; i < len; i++){
4449                 var data = this.prepareData(o[i], i, o);
4450                 html[html.length] = this.tpl.apply(data);
4451             }
4452         }else{
4453             html.push(this.emptyText);
4454         }
4455         this.el.update(html.join(""));
4456         this.nodes = this.el.dom.childNodes;
4457         this.updateIndexes(0);
4458     },
4459
4460     /**
4461      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4462      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4463      <pre><code>
4464      view.load({
4465          url: "your-url.php",
4466          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4467          callback: yourFunction,
4468          scope: yourObject, //(optional scope)
4469          discardUrl: false,
4470          nocache: false,
4471          text: "Loading...",
4472          timeout: 30,
4473          scripts: false
4474      });
4475      </code></pre>
4476      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4477      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4478      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4479      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4480      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4481      */
4482     load : function(){
4483         var um = this.el.getUpdateManager();
4484         um.update.apply(um, arguments);
4485     },
4486
4487     // note - render is a standard framework call...
4488     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4489     render : function(el, response){
4490         
4491         this.clearSelections();
4492         this.el.update("");
4493         var o;
4494         try{
4495             if (response != '') {
4496                 o = Roo.util.JSON.decode(response.responseText);
4497                 if(this.jsonRoot){
4498                     
4499                     o = o[this.jsonRoot];
4500                 }
4501             }
4502         } catch(e){
4503         }
4504         /**
4505          * The current JSON data or null
4506          */
4507         this.jsonData = o;
4508         this.beforeRender();
4509         this.refresh();
4510     },
4511
4512 /**
4513  * Get the number of records in the current JSON dataset
4514  * @return {Number}
4515  */
4516     getCount : function(){
4517         return this.jsonData ? this.jsonData.length : 0;
4518     },
4519
4520 /**
4521  * Returns the JSON object for the specified node(s)
4522  * @param {HTMLElement/Array} node The node or an array of nodes
4523  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4524  * you get the JSON object for the node
4525  */
4526     getNodeData : function(node){
4527         if(node instanceof Array){
4528             var data = [];
4529             for(var i = 0, len = node.length; i < len; i++){
4530                 data.push(this.getNodeData(node[i]));
4531             }
4532             return data;
4533         }
4534         return this.jsonData[this.indexOf(node)] || null;
4535     },
4536
4537     beforeRender : function(){
4538         this.snapshot = this.jsonData;
4539         if(this.sortInfo){
4540             this.sort.apply(this, this.sortInfo);
4541         }
4542         this.fireEvent("beforerender", this, this.jsonData);
4543     },
4544
4545     onLoad : function(el, o){
4546         this.fireEvent("load", this, this.jsonData, o);
4547     },
4548
4549     onLoadException : function(el, o){
4550         this.fireEvent("loadexception", this, o);
4551     },
4552
4553 /**
4554  * Filter the data by a specific property.
4555  * @param {String} property A property on your JSON objects
4556  * @param {String/RegExp} value Either string that the property values
4557  * should start with, or a RegExp to test against the property
4558  */
4559     filter : function(property, value){
4560         if(this.jsonData){
4561             var data = [];
4562             var ss = this.snapshot;
4563             if(typeof value == "string"){
4564                 var vlen = value.length;
4565                 if(vlen == 0){
4566                     this.clearFilter();
4567                     return;
4568                 }
4569                 value = value.toLowerCase();
4570                 for(var i = 0, len = ss.length; i < len; i++){
4571                     var o = ss[i];
4572                     if(o[property].substr(0, vlen).toLowerCase() == value){
4573                         data.push(o);
4574                     }
4575                 }
4576             } else if(value.exec){ // regex?
4577                 for(var i = 0, len = ss.length; i < len; i++){
4578                     var o = ss[i];
4579                     if(value.test(o[property])){
4580                         data.push(o);
4581                     }
4582                 }
4583             } else{
4584                 return;
4585             }
4586             this.jsonData = data;
4587             this.refresh();
4588         }
4589     },
4590
4591 /**
4592  * Filter by a function. The passed function will be called with each
4593  * object in the current dataset. If the function returns true the value is kept,
4594  * otherwise it is filtered.
4595  * @param {Function} fn
4596  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4597  */
4598     filterBy : function(fn, scope){
4599         if(this.jsonData){
4600             var data = [];
4601             var ss = this.snapshot;
4602             for(var i = 0, len = ss.length; i < len; i++){
4603                 var o = ss[i];
4604                 if(fn.call(scope || this, o)){
4605                     data.push(o);
4606                 }
4607             }
4608             this.jsonData = data;
4609             this.refresh();
4610         }
4611     },
4612
4613 /**
4614  * Clears the current filter.
4615  */
4616     clearFilter : function(){
4617         if(this.snapshot && this.jsonData != this.snapshot){
4618             this.jsonData = this.snapshot;
4619             this.refresh();
4620         }
4621     },
4622
4623
4624 /**
4625  * Sorts the data for this view and refreshes it.
4626  * @param {String} property A property on your JSON objects to sort on
4627  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4628  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4629  */
4630     sort : function(property, dir, sortType){
4631         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4632         if(this.jsonData){
4633             var p = property;
4634             var dsc = dir && dir.toLowerCase() == "desc";
4635             var f = function(o1, o2){
4636                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4637                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4638                 ;
4639                 if(v1 < v2){
4640                     return dsc ? +1 : -1;
4641                 } else if(v1 > v2){
4642                     return dsc ? -1 : +1;
4643                 } else{
4644                     return 0;
4645                 }
4646             };
4647             this.jsonData.sort(f);
4648             this.refresh();
4649             if(this.jsonData != this.snapshot){
4650                 this.snapshot.sort(f);
4651             }
4652         }
4653     }
4654 });/*
4655  * Based on:
4656  * Ext JS Library 1.1.1
4657  * Copyright(c) 2006-2007, Ext JS, LLC.
4658  *
4659  * Originally Released Under LGPL - original licence link has changed is not relivant.
4660  *
4661  * Fork - LGPL
4662  * <script type="text/javascript">
4663  */
4664  
4665
4666 /**
4667  * @class Roo.ColorPalette
4668  * @extends Roo.Component
4669  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4670  * Here's an example of typical usage:
4671  * <pre><code>
4672 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4673 cp.render('my-div');
4674
4675 cp.on('select', function(palette, selColor){
4676     // do something with selColor
4677 });
4678 </code></pre>
4679  * @constructor
4680  * Create a new ColorPalette
4681  * @param {Object} config The config object
4682  */
4683 Roo.ColorPalette = function(config){
4684     Roo.ColorPalette.superclass.constructor.call(this, config);
4685     this.addEvents({
4686         /**
4687              * @event select
4688              * Fires when a color is selected
4689              * @param {ColorPalette} this
4690              * @param {String} color The 6-digit color hex code (without the # symbol)
4691              */
4692         select: true
4693     });
4694
4695     if(this.handler){
4696         this.on("select", this.handler, this.scope, true);
4697     }
4698 };
4699 Roo.extend(Roo.ColorPalette, Roo.Component, {
4700     /**
4701      * @cfg {String} itemCls
4702      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4703      */
4704     itemCls : "x-color-palette",
4705     /**
4706      * @cfg {String} value
4707      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4708      * the hex codes are case-sensitive.
4709      */
4710     value : null,
4711     clickEvent:'click',
4712     // private
4713     ctype: "Roo.ColorPalette",
4714
4715     /**
4716      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4717      */
4718     allowReselect : false,
4719
4720     /**
4721      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4722      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4723      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4724      * of colors with the width setting until the box is symmetrical.</p>
4725      * <p>You can override individual colors if needed:</p>
4726      * <pre><code>
4727 var cp = new Roo.ColorPalette();
4728 cp.colors[0] = "FF0000";  // change the first box to red
4729 </code></pre>
4730
4731 Or you can provide a custom array of your own for complete control:
4732 <pre><code>
4733 var cp = new Roo.ColorPalette();
4734 cp.colors = ["000000", "993300", "333300"];
4735 </code></pre>
4736      * @type Array
4737      */
4738     colors : [
4739         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4740         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4741         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4742         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4743         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4744     ],
4745
4746     // private
4747     onRender : function(container, position){
4748         var t = new Roo.MasterTemplate(
4749             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4750         );
4751         var c = this.colors;
4752         for(var i = 0, len = c.length; i < len; i++){
4753             t.add([c[i]]);
4754         }
4755         var el = document.createElement("div");
4756         el.className = this.itemCls;
4757         t.overwrite(el);
4758         container.dom.insertBefore(el, position);
4759         this.el = Roo.get(el);
4760         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4761         if(this.clickEvent != 'click'){
4762             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4763         }
4764     },
4765
4766     // private
4767     afterRender : function(){
4768         Roo.ColorPalette.superclass.afterRender.call(this);
4769         if(this.value){
4770             var s = this.value;
4771             this.value = null;
4772             this.select(s);
4773         }
4774     },
4775
4776     // private
4777     handleClick : function(e, t){
4778         e.preventDefault();
4779         if(!this.disabled){
4780             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4781             this.select(c.toUpperCase());
4782         }
4783     },
4784
4785     /**
4786      * Selects the specified color in the palette (fires the select event)
4787      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4788      */
4789     select : function(color){
4790         color = color.replace("#", "");
4791         if(color != this.value || this.allowReselect){
4792             var el = this.el;
4793             if(this.value){
4794                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4795             }
4796             el.child("a.color-"+color).addClass("x-color-palette-sel");
4797             this.value = color;
4798             this.fireEvent("select", this, color);
4799         }
4800     }
4801 });/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812 /**
4813  * @class Roo.DatePicker
4814  * @extends Roo.Component
4815  * Simple date picker class.
4816  * @constructor
4817  * Create a new DatePicker
4818  * @param {Object} config The config object
4819  */
4820 Roo.DatePicker = function(config){
4821     Roo.DatePicker.superclass.constructor.call(this, config);
4822
4823     this.value = config && config.value ?
4824                  config.value.clearTime() : new Date().clearTime();
4825
4826     this.addEvents({
4827         /**
4828              * @event select
4829              * Fires when a date is selected
4830              * @param {DatePicker} this
4831              * @param {Date} date The selected date
4832              */
4833         'select': true,
4834         /**
4835              * @event monthchange
4836              * Fires when the displayed month changes 
4837              * @param {DatePicker} this
4838              * @param {Date} date The selected month
4839              */
4840         'monthchange': true
4841     });
4842
4843     if(this.handler){
4844         this.on("select", this.handler,  this.scope || this);
4845     }
4846     // build the disabledDatesRE
4847     if(!this.disabledDatesRE && this.disabledDates){
4848         var dd = this.disabledDates;
4849         var re = "(?:";
4850         for(var i = 0; i < dd.length; i++){
4851             re += dd[i];
4852             if(i != dd.length-1) {
4853                 re += "|";
4854             }
4855         }
4856         this.disabledDatesRE = new RegExp(re + ")");
4857     }
4858 };
4859
4860 Roo.extend(Roo.DatePicker, Roo.Component, {
4861     /**
4862      * @cfg {String} todayText
4863      * The text to display on the button that selects the current date (defaults to "Today")
4864      */
4865     todayText : "Today",
4866     /**
4867      * @cfg {String} okText
4868      * The text to display on the ok button
4869      */
4870     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4871     /**
4872      * @cfg {String} cancelText
4873      * The text to display on the cancel button
4874      */
4875     cancelText : "Cancel",
4876     /**
4877      * @cfg {String} todayTip
4878      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4879      */
4880     todayTip : "{0} (Spacebar)",
4881     /**
4882      * @cfg {Date} minDate
4883      * Minimum allowable date (JavaScript date object, defaults to null)
4884      */
4885     minDate : null,
4886     /**
4887      * @cfg {Date} maxDate
4888      * Maximum allowable date (JavaScript date object, defaults to null)
4889      */
4890     maxDate : null,
4891     /**
4892      * @cfg {String} minText
4893      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4894      */
4895     minText : "This date is before the minimum date",
4896     /**
4897      * @cfg {String} maxText
4898      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4899      */
4900     maxText : "This date is after the maximum date",
4901     /**
4902      * @cfg {String} format
4903      * The default date format string which can be overriden for localization support.  The format must be
4904      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4905      */
4906     format : "m/d/y",
4907     /**
4908      * @cfg {Array} disabledDays
4909      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4910      */
4911     disabledDays : null,
4912     /**
4913      * @cfg {String} disabledDaysText
4914      * The tooltip to display when the date falls on a disabled day (defaults to "")
4915      */
4916     disabledDaysText : "",
4917     /**
4918      * @cfg {RegExp} disabledDatesRE
4919      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4920      */
4921     disabledDatesRE : null,
4922     /**
4923      * @cfg {String} disabledDatesText
4924      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4925      */
4926     disabledDatesText : "",
4927     /**
4928      * @cfg {Boolean} constrainToViewport
4929      * True to constrain the date picker to the viewport (defaults to true)
4930      */
4931     constrainToViewport : true,
4932     /**
4933      * @cfg {Array} monthNames
4934      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4935      */
4936     monthNames : Date.monthNames,
4937     /**
4938      * @cfg {Array} dayNames
4939      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4940      */
4941     dayNames : Date.dayNames,
4942     /**
4943      * @cfg {String} nextText
4944      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4945      */
4946     nextText: 'Next Month (Control+Right)',
4947     /**
4948      * @cfg {String} prevText
4949      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4950      */
4951     prevText: 'Previous Month (Control+Left)',
4952     /**
4953      * @cfg {String} monthYearText
4954      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4955      */
4956     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4957     /**
4958      * @cfg {Number} startDay
4959      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4960      */
4961     startDay : 0,
4962     /**
4963      * @cfg {Bool} showClear
4964      * Show a clear button (usefull for date form elements that can be blank.)
4965      */
4966     
4967     showClear: false,
4968     
4969     /**
4970      * Sets the value of the date field
4971      * @param {Date} value The date to set
4972      */
4973     setValue : function(value){
4974         var old = this.value;
4975         
4976         if (typeof(value) == 'string') {
4977          
4978             value = Date.parseDate(value, this.format);
4979         }
4980         if (!value) {
4981             value = new Date();
4982         }
4983         
4984         this.value = value.clearTime(true);
4985         if(this.el){
4986             this.update(this.value);
4987         }
4988     },
4989
4990     /**
4991      * Gets the current selected value of the date field
4992      * @return {Date} The selected date
4993      */
4994     getValue : function(){
4995         return this.value;
4996     },
4997
4998     // private
4999     focus : function(){
5000         if(this.el){
5001             this.update(this.activeDate);
5002         }
5003     },
5004
5005     // privateval
5006     onRender : function(container, position){
5007         
5008         var m = [
5009              '<table cellspacing="0">',
5010                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5011                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5012         var dn = this.dayNames;
5013         for(var i = 0; i < 7; i++){
5014             var d = this.startDay+i;
5015             if(d > 6){
5016                 d = d-7;
5017             }
5018             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5019         }
5020         m[m.length] = "</tr></thead><tbody><tr>";
5021         for(var i = 0; i < 42; i++) {
5022             if(i % 7 == 0 && i != 0){
5023                 m[m.length] = "</tr><tr>";
5024             }
5025             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5026         }
5027         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5028             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5029
5030         var el = document.createElement("div");
5031         el.className = "x-date-picker";
5032         el.innerHTML = m.join("");
5033
5034         container.dom.insertBefore(el, position);
5035
5036         this.el = Roo.get(el);
5037         this.eventEl = Roo.get(el.firstChild);
5038
5039         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5040             handler: this.showPrevMonth,
5041             scope: this,
5042             preventDefault:true,
5043             stopDefault:true
5044         });
5045
5046         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5047             handler: this.showNextMonth,
5048             scope: this,
5049             preventDefault:true,
5050             stopDefault:true
5051         });
5052
5053         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5054
5055         this.monthPicker = this.el.down('div.x-date-mp');
5056         this.monthPicker.enableDisplayMode('block');
5057         
5058         var kn = new Roo.KeyNav(this.eventEl, {
5059             "left" : function(e){
5060                 e.ctrlKey ?
5061                     this.showPrevMonth() :
5062                     this.update(this.activeDate.add("d", -1));
5063             },
5064
5065             "right" : function(e){
5066                 e.ctrlKey ?
5067                     this.showNextMonth() :
5068                     this.update(this.activeDate.add("d", 1));
5069             },
5070
5071             "up" : function(e){
5072                 e.ctrlKey ?
5073                     this.showNextYear() :
5074                     this.update(this.activeDate.add("d", -7));
5075             },
5076
5077             "down" : function(e){
5078                 e.ctrlKey ?
5079                     this.showPrevYear() :
5080                     this.update(this.activeDate.add("d", 7));
5081             },
5082
5083             "pageUp" : function(e){
5084                 this.showNextMonth();
5085             },
5086
5087             "pageDown" : function(e){
5088                 this.showPrevMonth();
5089             },
5090
5091             "enter" : function(e){
5092                 e.stopPropagation();
5093                 return true;
5094             },
5095
5096             scope : this
5097         });
5098
5099         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5100
5101         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5102
5103         this.el.unselectable();
5104         
5105         this.cells = this.el.select("table.x-date-inner tbody td");
5106         this.textNodes = this.el.query("table.x-date-inner tbody span");
5107
5108         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5109             text: "&#160;",
5110             tooltip: this.monthYearText
5111         });
5112
5113         this.mbtn.on('click', this.showMonthPicker, this);
5114         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5115
5116
5117         var today = (new Date()).dateFormat(this.format);
5118         
5119         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5120         if (this.showClear) {
5121             baseTb.add( new Roo.Toolbar.Fill());
5122         }
5123         baseTb.add({
5124             text: String.format(this.todayText, today),
5125             tooltip: String.format(this.todayTip, today),
5126             handler: this.selectToday,
5127             scope: this
5128         });
5129         
5130         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5131             
5132         //});
5133         if (this.showClear) {
5134             
5135             baseTb.add( new Roo.Toolbar.Fill());
5136             baseTb.add({
5137                 text: '&#160;',
5138                 cls: 'x-btn-icon x-btn-clear',
5139                 handler: function() {
5140                     //this.value = '';
5141                     this.fireEvent("select", this, '');
5142                 },
5143                 scope: this
5144             });
5145         }
5146         
5147         
5148         if(Roo.isIE){
5149             this.el.repaint();
5150         }
5151         this.update(this.value);
5152     },
5153
5154     createMonthPicker : function(){
5155         if(!this.monthPicker.dom.firstChild){
5156             var buf = ['<table border="0" cellspacing="0">'];
5157             for(var i = 0; i < 6; i++){
5158                 buf.push(
5159                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5160                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5161                     i == 0 ?
5162                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5163                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5164                 );
5165             }
5166             buf.push(
5167                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5168                     this.okText,
5169                     '</button><button type="button" class="x-date-mp-cancel">',
5170                     this.cancelText,
5171                     '</button></td></tr>',
5172                 '</table>'
5173             );
5174             this.monthPicker.update(buf.join(''));
5175             this.monthPicker.on('click', this.onMonthClick, this);
5176             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5177
5178             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5179             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5180
5181             this.mpMonths.each(function(m, a, i){
5182                 i += 1;
5183                 if((i%2) == 0){
5184                     m.dom.xmonth = 5 + Math.round(i * .5);
5185                 }else{
5186                     m.dom.xmonth = Math.round((i-1) * .5);
5187                 }
5188             });
5189         }
5190     },
5191
5192     showMonthPicker : function(){
5193         this.createMonthPicker();
5194         var size = this.el.getSize();
5195         this.monthPicker.setSize(size);
5196         this.monthPicker.child('table').setSize(size);
5197
5198         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5199         this.updateMPMonth(this.mpSelMonth);
5200         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5201         this.updateMPYear(this.mpSelYear);
5202
5203         this.monthPicker.slideIn('t', {duration:.2});
5204     },
5205
5206     updateMPYear : function(y){
5207         this.mpyear = y;
5208         var ys = this.mpYears.elements;
5209         for(var i = 1; i <= 10; i++){
5210             var td = ys[i-1], y2;
5211             if((i%2) == 0){
5212                 y2 = y + Math.round(i * .5);
5213                 td.firstChild.innerHTML = y2;
5214                 td.xyear = y2;
5215             }else{
5216                 y2 = y - (5-Math.round(i * .5));
5217                 td.firstChild.innerHTML = y2;
5218                 td.xyear = y2;
5219             }
5220             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5221         }
5222     },
5223
5224     updateMPMonth : function(sm){
5225         this.mpMonths.each(function(m, a, i){
5226             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5227         });
5228     },
5229
5230     selectMPMonth: function(m){
5231         
5232     },
5233
5234     onMonthClick : function(e, t){
5235         e.stopEvent();
5236         var el = new Roo.Element(t), pn;
5237         if(el.is('button.x-date-mp-cancel')){
5238             this.hideMonthPicker();
5239         }
5240         else if(el.is('button.x-date-mp-ok')){
5241             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.mpMonths.removeClass('x-date-mp-sel');
5246             pn.addClass('x-date-mp-sel');
5247             this.mpSelMonth = pn.dom.xmonth;
5248         }
5249         else if(pn = el.up('td.x-date-mp-year', 2)){
5250             this.mpYears.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelYear = pn.dom.xyear;
5253         }
5254         else if(el.is('a.x-date-mp-prev')){
5255             this.updateMPYear(this.mpyear-10);
5256         }
5257         else if(el.is('a.x-date-mp-next')){
5258             this.updateMPYear(this.mpyear+10);
5259         }
5260     },
5261
5262     onMonthDblClick : function(e, t){
5263         e.stopEvent();
5264         var el = new Roo.Element(t), pn;
5265         if(pn = el.up('td.x-date-mp-month', 2)){
5266             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5267             this.hideMonthPicker();
5268         }
5269         else if(pn = el.up('td.x-date-mp-year', 2)){
5270             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5271             this.hideMonthPicker();
5272         }
5273     },
5274
5275     hideMonthPicker : function(disableAnim){
5276         if(this.monthPicker){
5277             if(disableAnim === true){
5278                 this.monthPicker.hide();
5279             }else{
5280                 this.monthPicker.slideOut('t', {duration:.2});
5281             }
5282         }
5283     },
5284
5285     // private
5286     showPrevMonth : function(e){
5287         this.update(this.activeDate.add("mo", -1));
5288     },
5289
5290     // private
5291     showNextMonth : function(e){
5292         this.update(this.activeDate.add("mo", 1));
5293     },
5294
5295     // private
5296     showPrevYear : function(){
5297         this.update(this.activeDate.add("y", -1));
5298     },
5299
5300     // private
5301     showNextYear : function(){
5302         this.update(this.activeDate.add("y", 1));
5303     },
5304
5305     // private
5306     handleMouseWheel : function(e){
5307         var delta = e.getWheelDelta();
5308         if(delta > 0){
5309             this.showPrevMonth();
5310             e.stopEvent();
5311         } else if(delta < 0){
5312             this.showNextMonth();
5313             e.stopEvent();
5314         }
5315     },
5316
5317     // private
5318     handleDateClick : function(e, t){
5319         e.stopEvent();
5320         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5321             this.setValue(new Date(t.dateValue));
5322             this.fireEvent("select", this, this.value);
5323         }
5324     },
5325
5326     // private
5327     selectToday : function(){
5328         this.setValue(new Date().clearTime());
5329         this.fireEvent("select", this, this.value);
5330     },
5331
5332     // private
5333     update : function(date)
5334     {
5335         var vd = this.activeDate;
5336         this.activeDate = date;
5337         if(vd && this.el){
5338             var t = date.getTime();
5339             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5340                 this.cells.removeClass("x-date-selected");
5341                 this.cells.each(function(c){
5342                    if(c.dom.firstChild.dateValue == t){
5343                        c.addClass("x-date-selected");
5344                        setTimeout(function(){
5345                             try{c.dom.firstChild.focus();}catch(e){}
5346                        }, 50);
5347                        return false;
5348                    }
5349                 });
5350                 return;
5351             }
5352         }
5353         
5354         var days = date.getDaysInMonth();
5355         var firstOfMonth = date.getFirstDateOfMonth();
5356         var startingPos = firstOfMonth.getDay()-this.startDay;
5357
5358         if(startingPos <= this.startDay){
5359             startingPos += 7;
5360         }
5361
5362         var pm = date.add("mo", -1);
5363         var prevStart = pm.getDaysInMonth()-startingPos;
5364
5365         var cells = this.cells.elements;
5366         var textEls = this.textNodes;
5367         days += startingPos;
5368
5369         // convert everything to numbers so it's fast
5370         var day = 86400000;
5371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5372         var today = new Date().clearTime().getTime();
5373         var sel = date.clearTime().getTime();
5374         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5375         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5376         var ddMatch = this.disabledDatesRE;
5377         var ddText = this.disabledDatesText;
5378         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5379         var ddaysText = this.disabledDaysText;
5380         var format = this.format;
5381
5382         var setCellClass = function(cal, cell){
5383             cell.title = "";
5384             var t = d.getTime();
5385             cell.firstChild.dateValue = t;
5386             if(t == today){
5387                 cell.className += " x-date-today";
5388                 cell.title = cal.todayText;
5389             }
5390             if(t == sel){
5391                 cell.className += " x-date-selected";
5392                 setTimeout(function(){
5393                     try{cell.firstChild.focus();}catch(e){}
5394                 }, 50);
5395             }
5396             // disabling
5397             if(t < min) {
5398                 cell.className = " x-date-disabled";
5399                 cell.title = cal.minText;
5400                 return;
5401             }
5402             if(t > max) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.maxText;
5405                 return;
5406             }
5407             if(ddays){
5408                 if(ddays.indexOf(d.getDay()) != -1){
5409                     cell.title = ddaysText;
5410                     cell.className = " x-date-disabled";
5411                 }
5412             }
5413             if(ddMatch && format){
5414                 var fvalue = d.dateFormat(format);
5415                 if(ddMatch.test(fvalue)){
5416                     cell.title = ddText.replace("%0", fvalue);
5417                     cell.className = " x-date-disabled";
5418                 }
5419             }
5420         };
5421
5422         var i = 0;
5423         for(; i < startingPos; i++) {
5424             textEls[i].innerHTML = (++prevStart);
5425             d.setDate(d.getDate()+1);
5426             cells[i].className = "x-date-prevday";
5427             setCellClass(this, cells[i]);
5428         }
5429         for(; i < days; i++){
5430             intDay = i - startingPos + 1;
5431             textEls[i].innerHTML = (intDay);
5432             d.setDate(d.getDate()+1);
5433             cells[i].className = "x-date-active";
5434             setCellClass(this, cells[i]);
5435         }
5436         var extraDays = 0;
5437         for(; i < 42; i++) {
5438              textEls[i].innerHTML = (++extraDays);
5439              d.setDate(d.getDate()+1);
5440              cells[i].className = "x-date-nextday";
5441              setCellClass(this, cells[i]);
5442         }
5443
5444         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5445         this.fireEvent('monthchange', this, date);
5446         
5447         if(!this.internalRender){
5448             var main = this.el.dom.firstChild;
5449             var w = main.offsetWidth;
5450             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5451             Roo.fly(main).setWidth(w);
5452             this.internalRender = true;
5453             // opera does not respect the auto grow header center column
5454             // then, after it gets a width opera refuses to recalculate
5455             // without a second pass
5456             if(Roo.isOpera && !this.secondPass){
5457                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5458                 this.secondPass = true;
5459                 this.update.defer(10, this, [date]);
5460             }
5461         }
5462         
5463         
5464     }
5465 });        /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475 /**
5476  * @class Roo.TabPanel
5477  * @extends Roo.util.Observable
5478  * A lightweight tab container.
5479  * <br><br>
5480  * Usage:
5481  * <pre><code>
5482 // basic tabs 1, built from existing content
5483 var tabs = new Roo.TabPanel("tabs1");
5484 tabs.addTab("script", "View Script");
5485 tabs.addTab("markup", "View Markup");
5486 tabs.activate("script");
5487
5488 // more advanced tabs, built from javascript
5489 var jtabs = new Roo.TabPanel("jtabs");
5490 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5491
5492 // set up the UpdateManager
5493 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5494 var updater = tab2.getUpdateManager();
5495 updater.setDefaultUrl("ajax1.htm");
5496 tab2.on('activate', updater.refresh, updater, true);
5497
5498 // Use setUrl for Ajax loading
5499 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5500 tab3.setUrl("ajax2.htm", null, true);
5501
5502 // Disabled tab
5503 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5504 tab4.disable();
5505
5506 jtabs.activate("jtabs-1");
5507  * </code></pre>
5508  * @constructor
5509  * Create a new TabPanel.
5510  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5511  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5512  */
5513 Roo.TabPanel = function(container, config){
5514     /**
5515     * The container element for this TabPanel.
5516     * @type Roo.Element
5517     */
5518     this.el = Roo.get(container, true);
5519     if(config){
5520         if(typeof config == "boolean"){
5521             this.tabPosition = config ? "bottom" : "top";
5522         }else{
5523             Roo.apply(this, config);
5524         }
5525     }
5526     if(this.tabPosition == "bottom"){
5527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5528         this.el.addClass("x-tabs-bottom");
5529     }
5530     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5531     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5532     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5533     if(Roo.isIE){
5534         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5535     }
5536     if(this.tabPosition != "bottom"){
5537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5538          * @type Roo.Element
5539          */
5540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5541         this.el.addClass("x-tabs-top");
5542     }
5543     this.items = [];
5544
5545     this.bodyEl.setStyle("position", "relative");
5546
5547     this.active = null;
5548     this.activateDelegate = this.activate.createDelegate(this);
5549
5550     this.addEvents({
5551         /**
5552          * @event tabchange
5553          * Fires when the active tab changes
5554          * @param {Roo.TabPanel} this
5555          * @param {Roo.TabPanelItem} activePanel The new active tab
5556          */
5557         "tabchange": true,
5558         /**
5559          * @event beforetabchange
5560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5561          * @param {Roo.TabPanel} this
5562          * @param {Object} e Set cancel to true on this object to cancel the tab change
5563          * @param {Roo.TabPanelItem} tab The tab being changed to
5564          */
5565         "beforetabchange" : true
5566     });
5567
5568     Roo.EventManager.onWindowResize(this.onResize, this);
5569     this.cpad = this.el.getPadding("lr");
5570     this.hiddenCount = 0;
5571
5572
5573     // toolbar on the tabbar support...
5574     if (this.toolbar) {
5575         var tcfg = this.toolbar;
5576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5577         this.toolbar = new Roo.Toolbar(tcfg);
5578         if (Roo.isSafari) {
5579             var tbl = tcfg.container.child('table', true);
5580             tbl.setAttribute('width', '100%');
5581         }
5582         
5583     }
5584    
5585
5586
5587     Roo.TabPanel.superclass.constructor.call(this);
5588 };
5589
5590 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5591     /*
5592      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5593      */
5594     tabPosition : "top",
5595     /*
5596      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5597      */
5598     currentTabWidth : 0,
5599     /*
5600      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5601      */
5602     minTabWidth : 40,
5603     /*
5604      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5605      */
5606     maxTabWidth : 250,
5607     /*
5608      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5609      */
5610     preferredTabWidth : 175,
5611     /*
5612      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5613      */
5614     resizeTabs : false,
5615     /*
5616      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5617      */
5618     monitorResize : true,
5619     /*
5620      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5621      */
5622     toolbar : false,
5623
5624     /**
5625      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5626      * @param {String} id The id of the div to use <b>or create</b>
5627      * @param {String} text The text for the tab
5628      * @param {String} content (optional) Content to put in the TabPanelItem body
5629      * @param {Boolean} closable (optional) True to create a close icon on the tab
5630      * @return {Roo.TabPanelItem} The created TabPanelItem
5631      */
5632     addTab : function(id, text, content, closable){
5633         var item = new Roo.TabPanelItem(this, id, text, closable);
5634         this.addTabItem(item);
5635         if(content){
5636             item.setContent(content);
5637         }
5638         return item;
5639     },
5640
5641     /**
5642      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5643      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5644      * @return {Roo.TabPanelItem}
5645      */
5646     getTab : function(id){
5647         return this.items[id];
5648     },
5649
5650     /**
5651      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5652      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5653      */
5654     hideTab : function(id){
5655         var t = this.items[id];
5656         if(!t.isHidden()){
5657            t.setHidden(true);
5658            this.hiddenCount++;
5659            this.autoSizeTabs();
5660         }
5661     },
5662
5663     /**
5664      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5665      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5666      */
5667     unhideTab : function(id){
5668         var t = this.items[id];
5669         if(t.isHidden()){
5670            t.setHidden(false);
5671            this.hiddenCount--;
5672            this.autoSizeTabs();
5673         }
5674     },
5675
5676     /**
5677      * Adds an existing {@link Roo.TabPanelItem}.
5678      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5679      */
5680     addTabItem : function(item){
5681         this.items[item.id] = item;
5682         this.items.push(item);
5683         if(this.resizeTabs){
5684            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5685            this.autoSizeTabs();
5686         }else{
5687             item.autoSize();
5688         }
5689     },
5690
5691     /**
5692      * Removes a {@link Roo.TabPanelItem}.
5693      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5694      */
5695     removeTab : function(id){
5696         var items = this.items;
5697         var tab = items[id];
5698         if(!tab) { return; }
5699         var index = items.indexOf(tab);
5700         if(this.active == tab && items.length > 1){
5701             var newTab = this.getNextAvailable(index);
5702             if(newTab) {
5703                 newTab.activate();
5704             }
5705         }
5706         this.stripEl.dom.removeChild(tab.pnode.dom);
5707         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5708             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5709         }
5710         items.splice(index, 1);
5711         delete this.items[tab.id];
5712         tab.fireEvent("close", tab);
5713         tab.purgeListeners();
5714         this.autoSizeTabs();
5715     },
5716
5717     getNextAvailable : function(start){
5718         var items = this.items;
5719         var index = start;
5720         // look for a next tab that will slide over to
5721         // replace the one being removed
5722         while(index < items.length){
5723             var item = items[++index];
5724             if(item && !item.isHidden()){
5725                 return item;
5726             }
5727         }
5728         // if one isn't found select the previous tab (on the left)
5729         index = start;
5730         while(index >= 0){
5731             var item = items[--index];
5732             if(item && !item.isHidden()){
5733                 return item;
5734             }
5735         }
5736         return null;
5737     },
5738
5739     /**
5740      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5741      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5742      */
5743     disableTab : function(id){
5744         var tab = this.items[id];
5745         if(tab && this.active != tab){
5746             tab.disable();
5747         }
5748     },
5749
5750     /**
5751      * Enables a {@link Roo.TabPanelItem} that is disabled.
5752      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5753      */
5754     enableTab : function(id){
5755         var tab = this.items[id];
5756         tab.enable();
5757     },
5758
5759     /**
5760      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5761      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5762      * @return {Roo.TabPanelItem} The TabPanelItem.
5763      */
5764     activate : function(id){
5765         var tab = this.items[id];
5766         if(!tab){
5767             return null;
5768         }
5769         if(tab == this.active || tab.disabled){
5770             return tab;
5771         }
5772         var e = {};
5773         this.fireEvent("beforetabchange", this, e, tab);
5774         if(e.cancel !== true && !tab.disabled){
5775             if(this.active){
5776                 this.active.hide();
5777             }
5778             this.active = this.items[id];
5779             this.active.show();
5780             this.fireEvent("tabchange", this, this.active);
5781         }
5782         return tab;
5783     },
5784
5785     /**
5786      * Gets the active {@link Roo.TabPanelItem}.
5787      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5788      */
5789     getActiveTab : function(){
5790         return this.active;
5791     },
5792
5793     /**
5794      * Updates the tab body element to fit the height of the container element
5795      * for overflow scrolling
5796      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5797      */
5798     syncHeight : function(targetHeight){
5799         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5800         var bm = this.bodyEl.getMargins();
5801         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5802         this.bodyEl.setHeight(newHeight);
5803         return newHeight;
5804     },
5805
5806     onResize : function(){
5807         if(this.monitorResize){
5808             this.autoSizeTabs();
5809         }
5810     },
5811
5812     /**
5813      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5814      */
5815     beginUpdate : function(){
5816         this.updating = true;
5817     },
5818
5819     /**
5820      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5821      */
5822     endUpdate : function(){
5823         this.updating = false;
5824         this.autoSizeTabs();
5825     },
5826
5827     /**
5828      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5829      */
5830     autoSizeTabs : function(){
5831         var count = this.items.length;
5832         var vcount = count - this.hiddenCount;
5833         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5834             return;
5835         }
5836         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5837         var availWidth = Math.floor(w / vcount);
5838         var b = this.stripBody;
5839         if(b.getWidth() > w){
5840             var tabs = this.items;
5841             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5842             if(availWidth < this.minTabWidth){
5843                 /*if(!this.sleft){    // incomplete scrolling code
5844                     this.createScrollButtons();
5845                 }
5846                 this.showScroll();
5847                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5848             }
5849         }else{
5850             if(this.currentTabWidth < this.preferredTabWidth){
5851                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Returns the number of tabs in this TabPanel.
5858      * @return {Number}
5859      */
5860      getCount : function(){
5861          return this.items.length;
5862      },
5863
5864     /**
5865      * Resizes all the tabs to the passed width
5866      * @param {Number} The new width
5867      */
5868     setTabWidth : function(width){
5869         this.currentTabWidth = width;
5870         for(var i = 0, len = this.items.length; i < len; i++) {
5871                 if(!this.items[i].isHidden()) {
5872                 this.items[i].setWidth(width);
5873             }
5874         }
5875     },
5876
5877     /**
5878      * Destroys this TabPanel
5879      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5880      */
5881     destroy : function(removeEl){
5882         Roo.EventManager.removeResizeListener(this.onResize, this);
5883         for(var i = 0, len = this.items.length; i < len; i++){
5884             this.items[i].purgeListeners();
5885         }
5886         if(removeEl === true){
5887             this.el.update("");
5888             this.el.remove();
5889         }
5890     }
5891 });
5892
5893 /**
5894  * @class Roo.TabPanelItem
5895  * @extends Roo.util.Observable
5896  * Represents an individual item (tab plus body) in a TabPanel.
5897  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5898  * @param {String} id The id of this TabPanelItem
5899  * @param {String} text The text for the tab of this TabPanelItem
5900  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5901  */
5902 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5903     /**
5904      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5905      * @type Roo.TabPanel
5906      */
5907     this.tabPanel = tabPanel;
5908     /**
5909      * The id for this TabPanelItem
5910      * @type String
5911      */
5912     this.id = id;
5913     /** @private */
5914     this.disabled = false;
5915     /** @private */
5916     this.text = text;
5917     /** @private */
5918     this.loaded = false;
5919     this.closable = closable;
5920
5921     /**
5922      * The body element for this TabPanelItem.
5923      * @type Roo.Element
5924      */
5925     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5926     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5927     this.bodyEl.setStyle("display", "block");
5928     this.bodyEl.setStyle("zoom", "1");
5929     this.hideAction();
5930
5931     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5932     /** @private */
5933     this.el = Roo.get(els.el, true);
5934     this.inner = Roo.get(els.inner, true);
5935     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5936     this.pnode = Roo.get(els.el.parentNode, true);
5937     this.el.on("mousedown", this.onTabMouseDown, this);
5938     this.el.on("click", this.onTabClick, this);
5939     /** @private */
5940     if(closable){
5941         var c = Roo.get(els.close, true);
5942         c.dom.title = this.closeText;
5943         c.addClassOnOver("close-over");
5944         c.on("click", this.closeClick, this);
5945      }
5946
5947     this.addEvents({
5948          /**
5949          * @event activate
5950          * Fires when this tab becomes the active tab.
5951          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5952          * @param {Roo.TabPanelItem} this
5953          */
5954         "activate": true,
5955         /**
5956          * @event beforeclose
5957          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5958          * @param {Roo.TabPanelItem} this
5959          * @param {Object} e Set cancel to true on this object to cancel the close.
5960          */
5961         "beforeclose": true,
5962         /**
5963          * @event close
5964          * Fires when this tab is closed.
5965          * @param {Roo.TabPanelItem} this
5966          */
5967          "close": true,
5968         /**
5969          * @event deactivate
5970          * Fires when this tab is no longer the active tab.
5971          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5972          * @param {Roo.TabPanelItem} this
5973          */
5974          "deactivate" : true
5975     });
5976     this.hidden = false;
5977
5978     Roo.TabPanelItem.superclass.constructor.call(this);
5979 };
5980
5981 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5982     purgeListeners : function(){
5983        Roo.util.Observable.prototype.purgeListeners.call(this);
5984        this.el.removeAllListeners();
5985     },
5986     /**
5987      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5988      */
5989     show : function(){
5990         this.pnode.addClass("on");
5991         this.showAction();
5992         if(Roo.isOpera){
5993             this.tabPanel.stripWrap.repaint();
5994         }
5995         this.fireEvent("activate", this.tabPanel, this);
5996     },
5997
5998     /**
5999      * Returns true if this tab is the active tab.
6000      * @return {Boolean}
6001      */
6002     isActive : function(){
6003         return this.tabPanel.getActiveTab() == this;
6004     },
6005
6006     /**
6007      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6008      */
6009     hide : function(){
6010         this.pnode.removeClass("on");
6011         this.hideAction();
6012         this.fireEvent("deactivate", this.tabPanel, this);
6013     },
6014
6015     hideAction : function(){
6016         this.bodyEl.hide();
6017         this.bodyEl.setStyle("position", "absolute");
6018         this.bodyEl.setLeft("-20000px");
6019         this.bodyEl.setTop("-20000px");
6020     },
6021
6022     showAction : function(){
6023         this.bodyEl.setStyle("position", "relative");
6024         this.bodyEl.setTop("");
6025         this.bodyEl.setLeft("");
6026         this.bodyEl.show();
6027     },
6028
6029     /**
6030      * Set the tooltip for the tab.
6031      * @param {String} tooltip The tab's tooltip
6032      */
6033     setTooltip : function(text){
6034         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6035             this.textEl.dom.qtip = text;
6036             this.textEl.dom.removeAttribute('title');
6037         }else{
6038             this.textEl.dom.title = text;
6039         }
6040     },
6041
6042     onTabClick : function(e){
6043         e.preventDefault();
6044         this.tabPanel.activate(this.id);
6045     },
6046
6047     onTabMouseDown : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     getWidth : function(){
6053         return this.inner.getWidth();
6054     },
6055
6056     setWidth : function(width){
6057         var iwidth = width - this.pnode.getPadding("lr");
6058         this.inner.setWidth(iwidth);
6059         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6060         this.pnode.setWidth(width);
6061     },
6062
6063     /**
6064      * Show or hide the tab
6065      * @param {Boolean} hidden True to hide or false to show.
6066      */
6067     setHidden : function(hidden){
6068         this.hidden = hidden;
6069         this.pnode.setStyle("display", hidden ? "none" : "");
6070     },
6071
6072     /**
6073      * Returns true if this tab is "hidden"
6074      * @return {Boolean}
6075      */
6076     isHidden : function(){
6077         return this.hidden;
6078     },
6079
6080     /**
6081      * Returns the text for this tab
6082      * @return {String}
6083      */
6084     getText : function(){
6085         return this.text;
6086     },
6087
6088     autoSize : function(){
6089         //this.el.beginMeasure();
6090         this.textEl.setWidth(1);
6091         /*
6092          *  #2804 [new] Tabs in Roojs
6093          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6094          */
6095         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6096         //this.el.endMeasure();
6097     },
6098
6099     /**
6100      * Sets the text for the tab (Note: this also sets the tooltip text)
6101      * @param {String} text The tab's text and tooltip
6102      */
6103     setText : function(text){
6104         this.text = text;
6105         this.textEl.update(text);
6106         this.setTooltip(text);
6107         if(!this.tabPanel.resizeTabs){
6108             this.autoSize();
6109         }
6110     },
6111     /**
6112      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6113      */
6114     activate : function(){
6115         this.tabPanel.activate(this.id);
6116     },
6117
6118     /**
6119      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6120      */
6121     disable : function(){
6122         if(this.tabPanel.active != this){
6123             this.disabled = true;
6124             this.pnode.addClass("disabled");
6125         }
6126     },
6127
6128     /**
6129      * Enables this TabPanelItem if it was previously disabled.
6130      */
6131     enable : function(){
6132         this.disabled = false;
6133         this.pnode.removeClass("disabled");
6134     },
6135
6136     /**
6137      * Sets the content for this TabPanelItem.
6138      * @param {String} content The content
6139      * @param {Boolean} loadScripts true to look for and load scripts
6140      */
6141     setContent : function(content, loadScripts){
6142         this.bodyEl.update(content, loadScripts);
6143     },
6144
6145     /**
6146      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6147      * @return {Roo.UpdateManager} The UpdateManager
6148      */
6149     getUpdateManager : function(){
6150         return this.bodyEl.getUpdateManager();
6151     },
6152
6153     /**
6154      * Set a URL to be used to load the content for this TabPanelItem.
6155      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6156      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6157      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6158      * @return {Roo.UpdateManager} The UpdateManager
6159      */
6160     setUrl : function(url, params, loadOnce){
6161         if(this.refreshDelegate){
6162             this.un('activate', this.refreshDelegate);
6163         }
6164         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6165         this.on("activate", this.refreshDelegate);
6166         return this.bodyEl.getUpdateManager();
6167     },
6168
6169     /** @private */
6170     _handleRefresh : function(url, params, loadOnce){
6171         if(!loadOnce || !this.loaded){
6172             var updater = this.bodyEl.getUpdateManager();
6173             updater.update(url, params, this._setLoaded.createDelegate(this));
6174         }
6175     },
6176
6177     /**
6178      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6179      *   Will fail silently if the setUrl method has not been called.
6180      *   This does not activate the panel, just updates its content.
6181      */
6182     refresh : function(){
6183         if(this.refreshDelegate){
6184            this.loaded = false;
6185            this.refreshDelegate();
6186         }
6187     },
6188
6189     /** @private */
6190     _setLoaded : function(){
6191         this.loaded = true;
6192     },
6193
6194     /** @private */
6195     closeClick : function(e){
6196         var o = {};
6197         e.stopEvent();
6198         this.fireEvent("beforeclose", this, o);
6199         if(o.cancel !== true){
6200             this.tabPanel.removeTab(this.id);
6201         }
6202     },
6203     /**
6204      * The text displayed in the tooltip for the close icon.
6205      * @type String
6206      */
6207     closeText : "Close this tab"
6208 });
6209
6210 /** @private */
6211 Roo.TabPanel.prototype.createStrip = function(container){
6212     var strip = document.createElement("div");
6213     strip.className = "x-tabs-wrap";
6214     container.appendChild(strip);
6215     return strip;
6216 };
6217 /** @private */
6218 Roo.TabPanel.prototype.createStripList = function(strip){
6219     // div wrapper for retard IE
6220     // returns the "tr" element.
6221     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6222         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6223         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6224     return strip.firstChild.firstChild.firstChild.firstChild;
6225 };
6226 /** @private */
6227 Roo.TabPanel.prototype.createBody = function(container){
6228     var body = document.createElement("div");
6229     Roo.id(body, "tab-body");
6230     Roo.fly(body).addClass("x-tabs-body");
6231     container.appendChild(body);
6232     return body;
6233 };
6234 /** @private */
6235 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6236     var body = Roo.getDom(id);
6237     if(!body){
6238         body = document.createElement("div");
6239         body.id = id;
6240     }
6241     Roo.fly(body).addClass("x-tabs-item-body");
6242     bodyEl.insertBefore(body, bodyEl.firstChild);
6243     return body;
6244 };
6245 /** @private */
6246 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6247     var td = document.createElement("td");
6248     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6249     //stripEl.appendChild(td);
6250     if(closable){
6251         td.className = "x-tabs-closable";
6252         if(!this.closeTpl){
6253             this.closeTpl = new Roo.Template(
6254                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6255                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6256                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6257             );
6258         }
6259         var el = this.closeTpl.overwrite(td, {"text": text});
6260         var close = el.getElementsByTagName("div")[0];
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "close": close, "inner": inner};
6263     } else {
6264         if(!this.tabTpl){
6265             this.tabTpl = new Roo.Template(
6266                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6267                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6268             );
6269         }
6270         var el = this.tabTpl.overwrite(td, {"text": text});
6271         var inner = el.getElementsByTagName("em")[0];
6272         return {"el": el, "inner": inner};
6273     }
6274 };/*
6275  * Based on:
6276  * Ext JS Library 1.1.1
6277  * Copyright(c) 2006-2007, Ext JS, LLC.
6278  *
6279  * Originally Released Under LGPL - original licence link has changed is not relivant.
6280  *
6281  * Fork - LGPL
6282  * <script type="text/javascript">
6283  */
6284
6285 /**
6286  * @class Roo.Button
6287  * @extends Roo.util.Observable
6288  * Simple Button class
6289  * @cfg {String} text The button text
6290  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6291  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6292  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6293  * @cfg {Object} scope The scope of the handler
6294  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6295  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6296  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6297  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6298  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6299  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6300    applies if enableToggle = true)
6301  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6302  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6303   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6304  * @constructor
6305  * Create a new button
6306  * @param {Object} config The config object
6307  */
6308 Roo.Button = function(renderTo, config)
6309 {
6310     if (!config) {
6311         config = renderTo;
6312         renderTo = config.renderTo || false;
6313     }
6314     
6315     Roo.apply(this, config);
6316     this.addEvents({
6317         /**
6318              * @event click
6319              * Fires when this button is clicked
6320              * @param {Button} this
6321              * @param {EventObject} e The click event
6322              */
6323             "click" : true,
6324         /**
6325              * @event toggle
6326              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6327              * @param {Button} this
6328              * @param {Boolean} pressed
6329              */
6330             "toggle" : true,
6331         /**
6332              * @event mouseover
6333              * Fires when the mouse hovers over the button
6334              * @param {Button} this
6335              * @param {Event} e The event object
6336              */
6337         'mouseover' : true,
6338         /**
6339              * @event mouseout
6340              * Fires when the mouse exits the button
6341              * @param {Button} this
6342              * @param {Event} e The event object
6343              */
6344         'mouseout': true,
6345          /**
6346              * @event render
6347              * Fires when the button is rendered
6348              * @param {Button} this
6349              */
6350         'render': true
6351     });
6352     if(this.menu){
6353         this.menu = Roo.menu.MenuMgr.get(this.menu);
6354     }
6355     // register listeners first!!  - so render can be captured..
6356     Roo.util.Observable.call(this);
6357     if(renderTo){
6358         this.render(renderTo);
6359     }
6360     
6361   
6362 };
6363
6364 Roo.extend(Roo.Button, Roo.util.Observable, {
6365     /**
6366      * 
6367      */
6368     
6369     /**
6370      * Read-only. True if this button is hidden
6371      * @type Boolean
6372      */
6373     hidden : false,
6374     /**
6375      * Read-only. True if this button is disabled
6376      * @type Boolean
6377      */
6378     disabled : false,
6379     /**
6380      * Read-only. True if this button is pressed (only if enableToggle = true)
6381      * @type Boolean
6382      */
6383     pressed : false,
6384
6385     /**
6386      * @cfg {Number} tabIndex 
6387      * The DOM tabIndex for this button (defaults to undefined)
6388      */
6389     tabIndex : undefined,
6390
6391     /**
6392      * @cfg {Boolean} enableToggle
6393      * True to enable pressed/not pressed toggling (defaults to false)
6394      */
6395     enableToggle: false,
6396     /**
6397      * @cfg {Roo.menu.Menu} menu
6398      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6399      */
6400     menu : undefined,
6401     /**
6402      * @cfg {String} menuAlign
6403      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6404      */
6405     menuAlign : "tl-bl?",
6406
6407     /**
6408      * @cfg {String} iconCls
6409      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6410      */
6411     iconCls : undefined,
6412     /**
6413      * @cfg {String} type
6414      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6415      */
6416     type : 'button',
6417
6418     // private
6419     menuClassTarget: 'tr',
6420
6421     /**
6422      * @cfg {String} clickEvent
6423      * The type of event to map to the button's event handler (defaults to 'click')
6424      */
6425     clickEvent : 'click',
6426
6427     /**
6428      * @cfg {Boolean} handleMouseEvents
6429      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6430      */
6431     handleMouseEvents : true,
6432
6433     /**
6434      * @cfg {String} tooltipType
6435      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6436      */
6437     tooltipType : 'qtip',
6438
6439     /**
6440      * @cfg {String} cls
6441      * A CSS class to apply to the button's main element.
6442      */
6443     
6444     /**
6445      * @cfg {Roo.Template} template (Optional)
6446      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6447      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6448      * require code modifications if required elements (e.g. a button) aren't present.
6449      */
6450
6451     // private
6452     render : function(renderTo){
6453         var btn;
6454         if(this.hideParent){
6455             this.parentEl = Roo.get(renderTo);
6456         }
6457         if(!this.dhconfig){
6458             if(!this.template){
6459                 if(!Roo.Button.buttonTemplate){
6460                     // hideous table template
6461                     Roo.Button.buttonTemplate = new Roo.Template(
6462                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6463                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6464                         "</tr></tbody></table>");
6465                 }
6466                 this.template = Roo.Button.buttonTemplate;
6467             }
6468             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6469             var btnEl = btn.child("button:first");
6470             btnEl.on('focus', this.onFocus, this);
6471             btnEl.on('blur', this.onBlur, this);
6472             if(this.cls){
6473                 btn.addClass(this.cls);
6474             }
6475             if(this.icon){
6476                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6477             }
6478             if(this.iconCls){
6479                 btnEl.addClass(this.iconCls);
6480                 if(!this.cls){
6481                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6482                 }
6483             }
6484             if(this.tabIndex !== undefined){
6485                 btnEl.dom.tabIndex = this.tabIndex;
6486             }
6487             if(this.tooltip){
6488                 if(typeof this.tooltip == 'object'){
6489                     Roo.QuickTips.tips(Roo.apply({
6490                           target: btnEl.id
6491                     }, this.tooltip));
6492                 } else {
6493                     btnEl.dom[this.tooltipType] = this.tooltip;
6494                 }
6495             }
6496         }else{
6497             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6498         }
6499         this.el = btn;
6500         if(this.id){
6501             this.el.dom.id = this.el.id = this.id;
6502         }
6503         if(this.menu){
6504             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6505             this.menu.on("show", this.onMenuShow, this);
6506             this.menu.on("hide", this.onMenuHide, this);
6507         }
6508         btn.addClass("x-btn");
6509         if(Roo.isIE && !Roo.isIE7){
6510             this.autoWidth.defer(1, this);
6511         }else{
6512             this.autoWidth();
6513         }
6514         if(this.handleMouseEvents){
6515             btn.on("mouseover", this.onMouseOver, this);
6516             btn.on("mouseout", this.onMouseOut, this);
6517             btn.on("mousedown", this.onMouseDown, this);
6518         }
6519         btn.on(this.clickEvent, this.onClick, this);
6520         //btn.on("mouseup", this.onMouseUp, this);
6521         if(this.hidden){
6522             this.hide();
6523         }
6524         if(this.disabled){
6525             this.disable();
6526         }
6527         Roo.ButtonToggleMgr.register(this);
6528         if(this.pressed){
6529             this.el.addClass("x-btn-pressed");
6530         }
6531         if(this.repeat){
6532             var repeater = new Roo.util.ClickRepeater(btn,
6533                 typeof this.repeat == "object" ? this.repeat : {}
6534             );
6535             repeater.on("click", this.onClick,  this);
6536         }
6537         
6538         this.fireEvent('render', this);
6539         
6540     },
6541     /**
6542      * Returns the button's underlying element
6543      * @return {Roo.Element} The element
6544      */
6545     getEl : function(){
6546         return this.el;  
6547     },
6548     
6549     /**
6550      * Destroys this Button and removes any listeners.
6551      */
6552     destroy : function(){
6553         Roo.ButtonToggleMgr.unregister(this);
6554         this.el.removeAllListeners();
6555         this.purgeListeners();
6556         this.el.remove();
6557     },
6558
6559     // private
6560     autoWidth : function(){
6561         if(this.el){
6562             this.el.setWidth("auto");
6563             if(Roo.isIE7 && Roo.isStrict){
6564                 var ib = this.el.child('button');
6565                 if(ib && ib.getWidth() > 20){
6566                     ib.clip();
6567                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6568                 }
6569             }
6570             if(this.minWidth){
6571                 if(this.hidden){
6572                     this.el.beginMeasure();
6573                 }
6574                 if(this.el.getWidth() < this.minWidth){
6575                     this.el.setWidth(this.minWidth);
6576                 }
6577                 if(this.hidden){
6578                     this.el.endMeasure();
6579                 }
6580             }
6581         }
6582     },
6583
6584     /**
6585      * Assigns this button's click handler
6586      * @param {Function} handler The function to call when the button is clicked
6587      * @param {Object} scope (optional) Scope for the function passed in
6588      */
6589     setHandler : function(handler, scope){
6590         this.handler = handler;
6591         this.scope = scope;  
6592     },
6593     
6594     /**
6595      * Sets this button's text
6596      * @param {String} text The button text
6597      */
6598     setText : function(text){
6599         this.text = text;
6600         if(this.el){
6601             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6602         }
6603         this.autoWidth();
6604     },
6605     
6606     /**
6607      * Gets the text for this button
6608      * @return {String} The button text
6609      */
6610     getText : function(){
6611         return this.text;  
6612     },
6613     
6614     /**
6615      * Show this button
6616      */
6617     show: function(){
6618         this.hidden = false;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6621         }
6622     },
6623     
6624     /**
6625      * Hide this button
6626      */
6627     hide: function(){
6628         this.hidden = true;
6629         if(this.el){
6630             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6631         }
6632     },
6633     
6634     /**
6635      * Convenience function for boolean show/hide
6636      * @param {Boolean} visible True to show, false to hide
6637      */
6638     setVisible: function(visible){
6639         if(visible) {
6640             this.show();
6641         }else{
6642             this.hide();
6643         }
6644     },
6645     
6646     /**
6647      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6648      * @param {Boolean} state (optional) Force a particular state
6649      */
6650     toggle : function(state){
6651         state = state === undefined ? !this.pressed : state;
6652         if(state != this.pressed){
6653             if(state){
6654                 this.el.addClass("x-btn-pressed");
6655                 this.pressed = true;
6656                 this.fireEvent("toggle", this, true);
6657             }else{
6658                 this.el.removeClass("x-btn-pressed");
6659                 this.pressed = false;
6660                 this.fireEvent("toggle", this, false);
6661             }
6662             if(this.toggleHandler){
6663                 this.toggleHandler.call(this.scope || this, this, state);
6664             }
6665         }
6666     },
6667     
6668     /**
6669      * Focus the button
6670      */
6671     focus : function(){
6672         this.el.child('button:first').focus();
6673     },
6674     
6675     /**
6676      * Disable this button
6677      */
6678     disable : function(){
6679         if(this.el){
6680             this.el.addClass("x-btn-disabled");
6681         }
6682         this.disabled = true;
6683     },
6684     
6685     /**
6686      * Enable this button
6687      */
6688     enable : function(){
6689         if(this.el){
6690             this.el.removeClass("x-btn-disabled");
6691         }
6692         this.disabled = false;
6693     },
6694
6695     /**
6696      * Convenience function for boolean enable/disable
6697      * @param {Boolean} enabled True to enable, false to disable
6698      */
6699     setDisabled : function(v){
6700         this[v !== true ? "enable" : "disable"]();
6701     },
6702
6703     // private
6704     onClick : function(e)
6705     {
6706         if(e){
6707             e.preventDefault();
6708         }
6709         if(e.button != 0){
6710             return;
6711         }
6712         if(!this.disabled){
6713             if(this.enableToggle){
6714                 this.toggle();
6715             }
6716             if(this.menu && !this.menu.isVisible()){
6717                 this.menu.show(this.el, this.menuAlign);
6718             }
6719             this.fireEvent("click", this, e);
6720             if(this.handler){
6721                 this.el.removeClass("x-btn-over");
6722                 this.handler.call(this.scope || this, this, e);
6723             }
6724         }
6725     },
6726     // private
6727     onMouseOver : function(e){
6728         if(!this.disabled){
6729             this.el.addClass("x-btn-over");
6730             this.fireEvent('mouseover', this, e);
6731         }
6732     },
6733     // private
6734     onMouseOut : function(e){
6735         if(!e.within(this.el,  true)){
6736             this.el.removeClass("x-btn-over");
6737             this.fireEvent('mouseout', this, e);
6738         }
6739     },
6740     // private
6741     onFocus : function(e){
6742         if(!this.disabled){
6743             this.el.addClass("x-btn-focus");
6744         }
6745     },
6746     // private
6747     onBlur : function(e){
6748         this.el.removeClass("x-btn-focus");
6749     },
6750     // private
6751     onMouseDown : function(e){
6752         if(!this.disabled && e.button == 0){
6753             this.el.addClass("x-btn-click");
6754             Roo.get(document).on('mouseup', this.onMouseUp, this);
6755         }
6756     },
6757     // private
6758     onMouseUp : function(e){
6759         if(e.button == 0){
6760             this.el.removeClass("x-btn-click");
6761             Roo.get(document).un('mouseup', this.onMouseUp, this);
6762         }
6763     },
6764     // private
6765     onMenuShow : function(e){
6766         this.el.addClass("x-btn-menu-active");
6767     },
6768     // private
6769     onMenuHide : function(e){
6770         this.el.removeClass("x-btn-menu-active");
6771     }   
6772 });
6773
6774 // Private utility class used by Button
6775 Roo.ButtonToggleMgr = function(){
6776    var groups = {};
6777    
6778    function toggleGroup(btn, state){
6779        if(state){
6780            var g = groups[btn.toggleGroup];
6781            for(var i = 0, l = g.length; i < l; i++){
6782                if(g[i] != btn){
6783                    g[i].toggle(false);
6784                }
6785            }
6786        }
6787    }
6788    
6789    return {
6790        register : function(btn){
6791            if(!btn.toggleGroup){
6792                return;
6793            }
6794            var g = groups[btn.toggleGroup];
6795            if(!g){
6796                g = groups[btn.toggleGroup] = [];
6797            }
6798            g.push(btn);
6799            btn.on("toggle", toggleGroup);
6800        },
6801        
6802        unregister : function(btn){
6803            if(!btn.toggleGroup){
6804                return;
6805            }
6806            var g = groups[btn.toggleGroup];
6807            if(g){
6808                g.remove(btn);
6809                btn.un("toggle", toggleGroup);
6810            }
6811        }
6812    };
6813 }();/*
6814  * Based on:
6815  * Ext JS Library 1.1.1
6816  * Copyright(c) 2006-2007, Ext JS, LLC.
6817  *
6818  * Originally Released Under LGPL - original licence link has changed is not relivant.
6819  *
6820  * Fork - LGPL
6821  * <script type="text/javascript">
6822  */
6823  
6824 /**
6825  * @class Roo.SplitButton
6826  * @extends Roo.Button
6827  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6828  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6829  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6830  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6831  * @cfg {String} arrowTooltip The title attribute of the arrow
6832  * @constructor
6833  * Create a new menu button
6834  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6835  * @param {Object} config The config object
6836  */
6837 Roo.SplitButton = function(renderTo, config){
6838     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6839     /**
6840      * @event arrowclick
6841      * Fires when this button's arrow is clicked
6842      * @param {SplitButton} this
6843      * @param {EventObject} e The click event
6844      */
6845     this.addEvents({"arrowclick":true});
6846 };
6847
6848 Roo.extend(Roo.SplitButton, Roo.Button, {
6849     render : function(renderTo){
6850         // this is one sweet looking template!
6851         var tpl = new Roo.Template(
6852             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6853             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6854             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6855             "</tbody></table></td><td>",
6856             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6857             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6858             "</tbody></table></td></tr></table>"
6859         );
6860         var btn = tpl.append(renderTo, [this.text, this.type], true);
6861         var btnEl = btn.child("button");
6862         if(this.cls){
6863             btn.addClass(this.cls);
6864         }
6865         if(this.icon){
6866             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6867         }
6868         if(this.iconCls){
6869             btnEl.addClass(this.iconCls);
6870             if(!this.cls){
6871                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6872             }
6873         }
6874         this.el = btn;
6875         if(this.handleMouseEvents){
6876             btn.on("mouseover", this.onMouseOver, this);
6877             btn.on("mouseout", this.onMouseOut, this);
6878             btn.on("mousedown", this.onMouseDown, this);
6879             btn.on("mouseup", this.onMouseUp, this);
6880         }
6881         btn.on(this.clickEvent, this.onClick, this);
6882         if(this.tooltip){
6883             if(typeof this.tooltip == 'object'){
6884                 Roo.QuickTips.tips(Roo.apply({
6885                       target: btnEl.id
6886                 }, this.tooltip));
6887             } else {
6888                 btnEl.dom[this.tooltipType] = this.tooltip;
6889             }
6890         }
6891         if(this.arrowTooltip){
6892             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6893         }
6894         if(this.hidden){
6895             this.hide();
6896         }
6897         if(this.disabled){
6898             this.disable();
6899         }
6900         if(this.pressed){
6901             this.el.addClass("x-btn-pressed");
6902         }
6903         if(Roo.isIE && !Roo.isIE7){
6904             this.autoWidth.defer(1, this);
6905         }else{
6906             this.autoWidth();
6907         }
6908         if(this.menu){
6909             this.menu.on("show", this.onMenuShow, this);
6910             this.menu.on("hide", this.onMenuHide, this);
6911         }
6912         this.fireEvent('render', this);
6913     },
6914
6915     // private
6916     autoWidth : function(){
6917         if(this.el){
6918             var tbl = this.el.child("table:first");
6919             var tbl2 = this.el.child("table:last");
6920             this.el.setWidth("auto");
6921             tbl.setWidth("auto");
6922             if(Roo.isIE7 && Roo.isStrict){
6923                 var ib = this.el.child('button:first');
6924                 if(ib && ib.getWidth() > 20){
6925                     ib.clip();
6926                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6927                 }
6928             }
6929             if(this.minWidth){
6930                 if(this.hidden){
6931                     this.el.beginMeasure();
6932                 }
6933                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6934                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6935                 }
6936                 if(this.hidden){
6937                     this.el.endMeasure();
6938                 }
6939             }
6940             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6941         } 
6942     },
6943     /**
6944      * Sets this button's click handler
6945      * @param {Function} handler The function to call when the button is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setHandler : function(handler, scope){
6949         this.handler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Sets this button's arrow click handler
6955      * @param {Function} handler The function to call when the arrow is clicked
6956      * @param {Object} scope (optional) Scope for the function passed above
6957      */
6958     setArrowHandler : function(handler, scope){
6959         this.arrowHandler = handler;
6960         this.scope = scope;  
6961     },
6962     
6963     /**
6964      * Focus the button
6965      */
6966     focus : function(){
6967         if(this.el){
6968             this.el.child("button:first").focus();
6969         }
6970     },
6971
6972     // private
6973     onClick : function(e){
6974         e.preventDefault();
6975         if(!this.disabled){
6976             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6977                 if(this.menu && !this.menu.isVisible()){
6978                     this.menu.show(this.el, this.menuAlign);
6979                 }
6980                 this.fireEvent("arrowclick", this, e);
6981                 if(this.arrowHandler){
6982                     this.arrowHandler.call(this.scope || this, this, e);
6983                 }
6984             }else{
6985                 this.fireEvent("click", this, e);
6986                 if(this.handler){
6987                     this.handler.call(this.scope || this, this, e);
6988                 }
6989             }
6990         }
6991     },
6992     // private
6993     onMouseDown : function(e){
6994         if(!this.disabled){
6995             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6996         }
6997     },
6998     // private
6999     onMouseUp : function(e){
7000         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7001     }   
7002 });
7003
7004
7005 // backwards compat
7006 Roo.MenuButton = Roo.SplitButton;/*
7007  * Based on:
7008  * Ext JS Library 1.1.1
7009  * Copyright(c) 2006-2007, Ext JS, LLC.
7010  *
7011  * Originally Released Under LGPL - original licence link has changed is not relivant.
7012  *
7013  * Fork - LGPL
7014  * <script type="text/javascript">
7015  */
7016
7017 /**
7018  * @class Roo.Toolbar
7019  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7020  * Basic Toolbar class.
7021  * @constructor
7022  * Creates a new Toolbar
7023  * @param {Object} container The config object
7024  */ 
7025 Roo.Toolbar = function(container, buttons, config)
7026 {
7027     /// old consturctor format still supported..
7028     if(container instanceof Array){ // omit the container for later rendering
7029         buttons = container;
7030         config = buttons;
7031         container = null;
7032     }
7033     if (typeof(container) == 'object' && container.xtype) {
7034         config = container;
7035         container = config.container;
7036         buttons = config.buttons || []; // not really - use items!!
7037     }
7038     var xitems = [];
7039     if (config && config.items) {
7040         xitems = config.items;
7041         delete config.items;
7042     }
7043     Roo.apply(this, config);
7044     this.buttons = buttons;
7045     
7046     if(container){
7047         this.render(container);
7048     }
7049     this.xitems = xitems;
7050     Roo.each(xitems, function(b) {
7051         this.add(b);
7052     }, this);
7053     
7054 };
7055
7056 Roo.Toolbar.prototype = {
7057     /**
7058      * @cfg {Array} items
7059      * array of button configs or elements to add (will be converted to a MixedCollection)
7060      */
7061     items: false,
7062     /**
7063      * @cfg {String/HTMLElement/Element} container
7064      * The id or element that will contain the toolbar
7065      */
7066     // private
7067     render : function(ct){
7068         this.el = Roo.get(ct);
7069         if(this.cls){
7070             this.el.addClass(this.cls);
7071         }
7072         // using a table allows for vertical alignment
7073         // 100% width is needed by Safari...
7074         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7075         this.tr = this.el.child("tr", true);
7076         var autoId = 0;
7077         this.items = new Roo.util.MixedCollection(false, function(o){
7078             return o.id || ("item" + (++autoId));
7079         });
7080         if(this.buttons){
7081             this.add.apply(this, this.buttons);
7082             delete this.buttons;
7083         }
7084     },
7085
7086     /**
7087      * Adds element(s) to the toolbar -- this function takes a variable number of 
7088      * arguments of mixed type and adds them to the toolbar.
7089      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7090      * <ul>
7091      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7092      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7093      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7094      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7095      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7096      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7097      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7098      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7099      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7100      * </ul>
7101      * @param {Mixed} arg2
7102      * @param {Mixed} etc.
7103      */
7104     add : function(){
7105         var a = arguments, l = a.length;
7106         for(var i = 0; i < l; i++){
7107             this._add(a[i]);
7108         }
7109     },
7110     // private..
7111     _add : function(el) {
7112         
7113         if (el.xtype) {
7114             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7115         }
7116         
7117         if (el.applyTo){ // some kind of form field
7118             return this.addField(el);
7119         } 
7120         if (el.render){ // some kind of Toolbar.Item
7121             return this.addItem(el);
7122         }
7123         if (typeof el == "string"){ // string
7124             if(el == "separator" || el == "-"){
7125                 return this.addSeparator();
7126             }
7127             if (el == " "){
7128                 return this.addSpacer();
7129             }
7130             if(el == "->"){
7131                 return this.addFill();
7132             }
7133             return this.addText(el);
7134             
7135         }
7136         if(el.tagName){ // element
7137             return this.addElement(el);
7138         }
7139         if(typeof el == "object"){ // must be button config?
7140             return this.addButton(el);
7141         }
7142         // and now what?!?!
7143         return false;
7144         
7145     },
7146     
7147     /**
7148      * Add an Xtype element
7149      * @param {Object} xtype Xtype Object
7150      * @return {Object} created Object
7151      */
7152     addxtype : function(e){
7153         return this.add(e);  
7154     },
7155     
7156     /**
7157      * Returns the Element for this toolbar.
7158      * @return {Roo.Element}
7159      */
7160     getEl : function(){
7161         return this.el;  
7162     },
7163     
7164     /**
7165      * Adds a separator
7166      * @return {Roo.Toolbar.Item} The separator item
7167      */
7168     addSeparator : function(){
7169         return this.addItem(new Roo.Toolbar.Separator());
7170     },
7171
7172     /**
7173      * Adds a spacer element
7174      * @return {Roo.Toolbar.Spacer} The spacer item
7175      */
7176     addSpacer : function(){
7177         return this.addItem(new Roo.Toolbar.Spacer());
7178     },
7179
7180     /**
7181      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7182      * @return {Roo.Toolbar.Fill} The fill item
7183      */
7184     addFill : function(){
7185         return this.addItem(new Roo.Toolbar.Fill());
7186     },
7187
7188     /**
7189      * Adds any standard HTML element to the toolbar
7190      * @param {String/HTMLElement/Element} el The element or id of the element to add
7191      * @return {Roo.Toolbar.Item} The element's item
7192      */
7193     addElement : function(el){
7194         return this.addItem(new Roo.Toolbar.Item(el));
7195     },
7196     /**
7197      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7198      * @type Roo.util.MixedCollection  
7199      */
7200     items : false,
7201      
7202     /**
7203      * Adds any Toolbar.Item or subclass
7204      * @param {Roo.Toolbar.Item} item
7205      * @return {Roo.Toolbar.Item} The item
7206      */
7207     addItem : function(item){
7208         var td = this.nextBlock();
7209         item.render(td);
7210         this.items.add(item);
7211         return item;
7212     },
7213     
7214     /**
7215      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7216      * @param {Object/Array} config A button config or array of configs
7217      * @return {Roo.Toolbar.Button/Array}
7218      */
7219     addButton : function(config){
7220         if(config instanceof Array){
7221             var buttons = [];
7222             for(var i = 0, len = config.length; i < len; i++) {
7223                 buttons.push(this.addButton(config[i]));
7224             }
7225             return buttons;
7226         }
7227         var b = config;
7228         if(!(config instanceof Roo.Toolbar.Button)){
7229             b = config.split ?
7230                 new Roo.Toolbar.SplitButton(config) :
7231                 new Roo.Toolbar.Button(config);
7232         }
7233         var td = this.nextBlock();
7234         b.render(td);
7235         this.items.add(b);
7236         return b;
7237     },
7238     
7239     /**
7240      * Adds text to the toolbar
7241      * @param {String} text The text to add
7242      * @return {Roo.Toolbar.Item} The element's item
7243      */
7244     addText : function(text){
7245         return this.addItem(new Roo.Toolbar.TextItem(text));
7246     },
7247     
7248     /**
7249      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7250      * @param {Number} index The index where the item is to be inserted
7251      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7252      * @return {Roo.Toolbar.Button/Item}
7253      */
7254     insertButton : function(index, item){
7255         if(item instanceof Array){
7256             var buttons = [];
7257             for(var i = 0, len = item.length; i < len; i++) {
7258                buttons.push(this.insertButton(index + i, item[i]));
7259             }
7260             return buttons;
7261         }
7262         if (!(item instanceof Roo.Toolbar.Button)){
7263            item = new Roo.Toolbar.Button(item);
7264         }
7265         var td = document.createElement("td");
7266         this.tr.insertBefore(td, this.tr.childNodes[index]);
7267         item.render(td);
7268         this.items.insert(index, item);
7269         return item;
7270     },
7271     
7272     /**
7273      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7274      * @param {Object} config
7275      * @return {Roo.Toolbar.Item} The element's item
7276      */
7277     addDom : function(config, returnEl){
7278         var td = this.nextBlock();
7279         Roo.DomHelper.overwrite(td, config);
7280         var ti = new Roo.Toolbar.Item(td.firstChild);
7281         ti.render(td);
7282         this.items.add(ti);
7283         return ti;
7284     },
7285
7286     /**
7287      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7288      * @type Roo.util.MixedCollection  
7289      */
7290     fields : false,
7291     
7292     /**
7293      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7294      * Note: the field should not have been rendered yet. For a field that has already been
7295      * rendered, use {@link #addElement}.
7296      * @param {Roo.form.Field} field
7297      * @return {Roo.ToolbarItem}
7298      */
7299      
7300       
7301     addField : function(field) {
7302         if (!this.fields) {
7303             var autoId = 0;
7304             this.fields = new Roo.util.MixedCollection(false, function(o){
7305                 return o.id || ("item" + (++autoId));
7306             });
7307
7308         }
7309         
7310         var td = this.nextBlock();
7311         field.render(td);
7312         var ti = new Roo.Toolbar.Item(td.firstChild);
7313         ti.render(td);
7314         this.items.add(ti);
7315         this.fields.add(field);
7316         return ti;
7317     },
7318     /**
7319      * Hide the toolbar
7320      * @method hide
7321      */
7322      
7323       
7324     hide : function()
7325     {
7326         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7327         this.el.child('div').hide();
7328     },
7329     /**
7330      * Show the toolbar
7331      * @method show
7332      */
7333     show : function()
7334     {
7335         this.el.child('div').show();
7336     },
7337       
7338     // private
7339     nextBlock : function(){
7340         var td = document.createElement("td");
7341         this.tr.appendChild(td);
7342         return td;
7343     },
7344
7345     // private
7346     destroy : function(){
7347         if(this.items){ // rendered?
7348             Roo.destroy.apply(Roo, this.items.items);
7349         }
7350         if(this.fields){ // rendered?
7351             Roo.destroy.apply(Roo, this.fields.items);
7352         }
7353         Roo.Element.uncache(this.el, this.tr);
7354     }
7355 };
7356
7357 /**
7358  * @class Roo.Toolbar.Item
7359  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7360  * @constructor
7361  * Creates a new Item
7362  * @param {HTMLElement} el 
7363  */
7364 Roo.Toolbar.Item = function(el){
7365     var cfg = {};
7366     if (typeof (el.xtype) != 'undefined') {
7367         cfg = el;
7368         el = cfg.el;
7369     }
7370     
7371     this.el = Roo.getDom(el);
7372     this.id = Roo.id(this.el);
7373     this.hidden = false;
7374     
7375     this.addEvents({
7376          /**
7377              * @event render
7378              * Fires when the button is rendered
7379              * @param {Button} this
7380              */
7381         'render': true
7382     });
7383     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7384 };
7385 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7386 //Roo.Toolbar.Item.prototype = {
7387     
7388     /**
7389      * Get this item's HTML Element
7390      * @return {HTMLElement}
7391      */
7392     getEl : function(){
7393        return this.el;  
7394     },
7395
7396     // private
7397     render : function(td){
7398         
7399          this.td = td;
7400         td.appendChild(this.el);
7401         
7402         this.fireEvent('render', this);
7403     },
7404     
7405     /**
7406      * Removes and destroys this item.
7407      */
7408     destroy : function(){
7409         this.td.parentNode.removeChild(this.td);
7410     },
7411     
7412     /**
7413      * Shows this item.
7414      */
7415     show: function(){
7416         this.hidden = false;
7417         this.td.style.display = "";
7418     },
7419     
7420     /**
7421      * Hides this item.
7422      */
7423     hide: function(){
7424         this.hidden = true;
7425         this.td.style.display = "none";
7426     },
7427     
7428     /**
7429      * Convenience function for boolean show/hide.
7430      * @param {Boolean} visible true to show/false to hide
7431      */
7432     setVisible: function(visible){
7433         if(visible) {
7434             this.show();
7435         }else{
7436             this.hide();
7437         }
7438     },
7439     
7440     /**
7441      * Try to focus this item.
7442      */
7443     focus : function(){
7444         Roo.fly(this.el).focus();
7445     },
7446     
7447     /**
7448      * Disables this item.
7449      */
7450     disable : function(){
7451         Roo.fly(this.td).addClass("x-item-disabled");
7452         this.disabled = true;
7453         this.el.disabled = true;
7454     },
7455     
7456     /**
7457      * Enables this item.
7458      */
7459     enable : function(){
7460         Roo.fly(this.td).removeClass("x-item-disabled");
7461         this.disabled = false;
7462         this.el.disabled = false;
7463     }
7464 });
7465
7466
7467 /**
7468  * @class Roo.Toolbar.Separator
7469  * @extends Roo.Toolbar.Item
7470  * A simple toolbar separator class
7471  * @constructor
7472  * Creates a new Separator
7473  */
7474 Roo.Toolbar.Separator = function(cfg){
7475     
7476     var s = document.createElement("span");
7477     s.className = "ytb-sep";
7478     if (cfg) {
7479         cfg.el = s;
7480     }
7481     
7482     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Spacer
7492  * @extends Roo.Toolbar.Item
7493  * A simple element that adds extra horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Spacer = function(cfg){
7498     var s = document.createElement("div");
7499     s.className = "ytb-spacer";
7500     if (cfg) {
7501         cfg.el = s;
7502     }
7503     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7504 };
7505 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7506     enable:Roo.emptyFn,
7507     disable:Roo.emptyFn,
7508     focus:Roo.emptyFn
7509 });
7510
7511 /**
7512  * @class Roo.Toolbar.Fill
7513  * @extends Roo.Toolbar.Spacer
7514  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7515  * @constructor
7516  * Creates a new Spacer
7517  */
7518 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7519     // private
7520     render : function(td){
7521         td.style.width = '100%';
7522         Roo.Toolbar.Fill.superclass.render.call(this, td);
7523     }
7524 });
7525
7526 /**
7527  * @class Roo.Toolbar.TextItem
7528  * @extends Roo.Toolbar.Item
7529  * A simple class that renders text directly into a toolbar.
7530  * @constructor
7531  * Creates a new TextItem
7532  * @cfg {string} text 
7533  */
7534 Roo.Toolbar.TextItem = function(cfg){
7535     var  text = cfg || "";
7536     if (typeof(cfg) == 'object') {
7537         text = cfg.text || "";
7538     }  else {
7539         cfg = null;
7540     }
7541     var s = document.createElement("span");
7542     s.className = "ytb-text";
7543     s.innerHTML = text;
7544     if (cfg) {
7545         cfg.el  = s;
7546     }
7547     
7548     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7549 };
7550 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7551     
7552      
7553     enable:Roo.emptyFn,
7554     disable:Roo.emptyFn,
7555     focus:Roo.emptyFn,
7556      /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.el.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.el.style.display = "none";
7570     }
7571     
7572 });
7573
7574 /**
7575  * @class Roo.Toolbar.Button
7576  * @extends Roo.Button
7577  * A button that renders into a toolbar.
7578  * @constructor
7579  * Creates a new Button
7580  * @param {Object} config A standard {@link Roo.Button} config object
7581  */
7582 Roo.Toolbar.Button = function(config){
7583     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7584 };
7585 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7586 {
7587     
7588     
7589     render : function(td){
7590         this.td = td;
7591         Roo.Toolbar.Button.superclass.render.call(this, td);
7592     },
7593     
7594     /**
7595      * Removes and destroys this button
7596      */
7597     destroy : function(){
7598         Roo.Toolbar.Button.superclass.destroy.call(this);
7599         this.td.parentNode.removeChild(this.td);
7600     },
7601     
7602     /**
7603      * Shows this button
7604      */
7605     show: function(){
7606         this.hidden = false;
7607         this.td.style.display = "";
7608     },
7609     
7610     /**
7611      * Hides this button
7612      */
7613     hide: function(){
7614         this.hidden = true;
7615         this.td.style.display = "none";
7616     },
7617
7618     /**
7619      * Disables this item
7620      */
7621     disable : function(){
7622         Roo.fly(this.td).addClass("x-item-disabled");
7623         this.disabled = true;
7624     },
7625
7626     /**
7627      * Enables this item
7628      */
7629     enable : function(){
7630         Roo.fly(this.td).removeClass("x-item-disabled");
7631         this.disabled = false;
7632     }
7633 });
7634 // backwards compat
7635 Roo.ToolbarButton = Roo.Toolbar.Button;
7636
7637 /**
7638  * @class Roo.Toolbar.SplitButton
7639  * @extends Roo.SplitButton
7640  * A menu button that renders into a toolbar.
7641  * @constructor
7642  * Creates a new SplitButton
7643  * @param {Object} config A standard {@link Roo.SplitButton} config object
7644  */
7645 Roo.Toolbar.SplitButton = function(config){
7646     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7647 };
7648 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7649     render : function(td){
7650         this.td = td;
7651         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7652     },
7653     
7654     /**
7655      * Removes and destroys this button
7656      */
7657     destroy : function(){
7658         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7659         this.td.parentNode.removeChild(this.td);
7660     },
7661     
7662     /**
7663      * Shows this button
7664      */
7665     show: function(){
7666         this.hidden = false;
7667         this.td.style.display = "";
7668     },
7669     
7670     /**
7671      * Hides this button
7672      */
7673     hide: function(){
7674         this.hidden = true;
7675         this.td.style.display = "none";
7676     }
7677 });
7678
7679 // backwards compat
7680 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  
7691 /**
7692  * @class Roo.PagingToolbar
7693  * @extends Roo.Toolbar
7694  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7695  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7696  * @constructor
7697  * Create a new PagingToolbar
7698  * @param {Object} config The config object
7699  */
7700 Roo.PagingToolbar = function(el, ds, config)
7701 {
7702     // old args format still supported... - xtype is prefered..
7703     if (typeof(el) == 'object' && el.xtype) {
7704         // created from xtype...
7705         config = el;
7706         ds = el.dataSource;
7707         el = config.container;
7708     }
7709     var items = [];
7710     if (config.items) {
7711         items = config.items;
7712         config.items = [];
7713     }
7714     
7715     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7716     this.ds = ds;
7717     this.cursor = 0;
7718     this.renderButtons(this.el);
7719     this.bind(ds);
7720     
7721     // supprot items array.
7722    
7723     Roo.each(items, function(e) {
7724         this.add(Roo.factory(e));
7725     },this);
7726     
7727 };
7728
7729 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7730    
7731     /**
7732      * @cfg {String/HTMLElement/Element} container
7733      * container The id or element that will contain the toolbar
7734      */
7735     /**
7736      * @cfg {Boolean} displayInfo
7737      * True to display the displayMsg (defaults to false)
7738      */
7739     
7740     
7741     /**
7742      * @cfg {Number} pageSize
7743      * The number of records to display per page (defaults to 20)
7744      */
7745     pageSize: 20,
7746     /**
7747      * @cfg {String} displayMsg
7748      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7749      */
7750     displayMsg : 'Displaying {0} - {1} of {2}',
7751     /**
7752      * @cfg {String} emptyMsg
7753      * The message to display when no records are found (defaults to "No data to display")
7754      */
7755     emptyMsg : 'No data to display',
7756     /**
7757      * Customizable piece of the default paging text (defaults to "Page")
7758      * @type String
7759      */
7760     beforePageText : "Page",
7761     /**
7762      * Customizable piece of the default paging text (defaults to "of %0")
7763      * @type String
7764      */
7765     afterPageText : "of {0}",
7766     /**
7767      * Customizable piece of the default paging text (defaults to "First Page")
7768      * @type String
7769      */
7770     firstText : "First Page",
7771     /**
7772      * Customizable piece of the default paging text (defaults to "Previous Page")
7773      * @type String
7774      */
7775     prevText : "Previous Page",
7776     /**
7777      * Customizable piece of the default paging text (defaults to "Next Page")
7778      * @type String
7779      */
7780     nextText : "Next Page",
7781     /**
7782      * Customizable piece of the default paging text (defaults to "Last Page")
7783      * @type String
7784      */
7785     lastText : "Last Page",
7786     /**
7787      * Customizable piece of the default paging text (defaults to "Refresh")
7788      * @type String
7789      */
7790     refreshText : "Refresh",
7791
7792     // private
7793     renderButtons : function(el){
7794         Roo.PagingToolbar.superclass.render.call(this, el);
7795         this.first = this.addButton({
7796             tooltip: this.firstText,
7797             cls: "x-btn-icon x-grid-page-first",
7798             disabled: true,
7799             handler: this.onClick.createDelegate(this, ["first"])
7800         });
7801         this.prev = this.addButton({
7802             tooltip: this.prevText,
7803             cls: "x-btn-icon x-grid-page-prev",
7804             disabled: true,
7805             handler: this.onClick.createDelegate(this, ["prev"])
7806         });
7807         //this.addSeparator();
7808         this.add(this.beforePageText);
7809         this.field = Roo.get(this.addDom({
7810            tag: "input",
7811            type: "text",
7812            size: "3",
7813            value: "1",
7814            cls: "x-grid-page-number"
7815         }).el);
7816         this.field.on("keydown", this.onPagingKeydown, this);
7817         this.field.on("focus", function(){this.dom.select();});
7818         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7819         this.field.setHeight(18);
7820         //this.addSeparator();
7821         this.next = this.addButton({
7822             tooltip: this.nextText,
7823             cls: "x-btn-icon x-grid-page-next",
7824             disabled: true,
7825             handler: this.onClick.createDelegate(this, ["next"])
7826         });
7827         this.last = this.addButton({
7828             tooltip: this.lastText,
7829             cls: "x-btn-icon x-grid-page-last",
7830             disabled: true,
7831             handler: this.onClick.createDelegate(this, ["last"])
7832         });
7833         //this.addSeparator();
7834         this.loading = this.addButton({
7835             tooltip: this.refreshText,
7836             cls: "x-btn-icon x-grid-loading",
7837             handler: this.onClick.createDelegate(this, ["refresh"])
7838         });
7839
7840         if(this.displayInfo){
7841             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7842         }
7843     },
7844
7845     // private
7846     updateInfo : function(){
7847         if(this.displayEl){
7848             var count = this.ds.getCount();
7849             var msg = count == 0 ?
7850                 this.emptyMsg :
7851                 String.format(
7852                     this.displayMsg,
7853                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7854                 );
7855             this.displayEl.update(msg);
7856         }
7857     },
7858
7859     // private
7860     onLoad : function(ds, r, o){
7861        this.cursor = o.params ? o.params.start : 0;
7862        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7863
7864        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7865        this.field.dom.value = ap;
7866        this.first.setDisabled(ap == 1);
7867        this.prev.setDisabled(ap == 1);
7868        this.next.setDisabled(ap == ps);
7869        this.last.setDisabled(ap == ps);
7870        this.loading.enable();
7871        this.updateInfo();
7872     },
7873
7874     // private
7875     getPageData : function(){
7876         var total = this.ds.getTotalCount();
7877         return {
7878             total : total,
7879             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7880             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7881         };
7882     },
7883
7884     // private
7885     onLoadError : function(){
7886         this.loading.enable();
7887     },
7888
7889     // private
7890     onPagingKeydown : function(e){
7891         var k = e.getKey();
7892         var d = this.getPageData();
7893         if(k == e.RETURN){
7894             var v = this.field.dom.value, pageNum;
7895             if(!v || isNaN(pageNum = parseInt(v, 10))){
7896                 this.field.dom.value = d.activePage;
7897                 return;
7898             }
7899             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7900             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7901             e.stopEvent();
7902         }
7903         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
7904         {
7905           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7906           this.field.dom.value = pageNum;
7907           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7908           e.stopEvent();
7909         }
7910         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7911         {
7912           var v = this.field.dom.value, pageNum; 
7913           var increment = (e.shiftKey) ? 10 : 1;
7914           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7915             increment *= -1;
7916           }
7917           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7918             this.field.dom.value = d.activePage;
7919             return;
7920           }
7921           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7922           {
7923             this.field.dom.value = parseInt(v, 10) + increment;
7924             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7925             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7926           }
7927           e.stopEvent();
7928         }
7929     },
7930
7931     // private
7932     beforeLoad : function(){
7933         if(this.loading){
7934             this.loading.disable();
7935         }
7936     },
7937
7938     // private
7939     onClick : function(which){
7940         var ds = this.ds;
7941         switch(which){
7942             case "first":
7943                 ds.load({params:{start: 0, limit: this.pageSize}});
7944             break;
7945             case "prev":
7946                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7947             break;
7948             case "next":
7949                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7950             break;
7951             case "last":
7952                 var total = ds.getTotalCount();
7953                 var extra = total % this.pageSize;
7954                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7955                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7956             break;
7957             case "refresh":
7958                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7959             break;
7960         }
7961     },
7962
7963     /**
7964      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7965      * @param {Roo.data.Store} store The data store to unbind
7966      */
7967     unbind : function(ds){
7968         ds.un("beforeload", this.beforeLoad, this);
7969         ds.un("load", this.onLoad, this);
7970         ds.un("loadexception", this.onLoadError, this);
7971         ds.un("remove", this.updateInfo, this);
7972         ds.un("add", this.updateInfo, this);
7973         this.ds = undefined;
7974     },
7975
7976     /**
7977      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7978      * @param {Roo.data.Store} store The data store to bind
7979      */
7980     bind : function(ds){
7981         ds.on("beforeload", this.beforeLoad, this);
7982         ds.on("load", this.onLoad, this);
7983         ds.on("loadexception", this.onLoadError, this);
7984         ds.on("remove", this.updateInfo, this);
7985         ds.on("add", this.updateInfo, this);
7986         this.ds = ds;
7987     }
7988 });/*
7989  * Based on:
7990  * Ext JS Library 1.1.1
7991  * Copyright(c) 2006-2007, Ext JS, LLC.
7992  *
7993  * Originally Released Under LGPL - original licence link has changed is not relivant.
7994  *
7995  * Fork - LGPL
7996  * <script type="text/javascript">
7997  */
7998
7999 /**
8000  * @class Roo.Resizable
8001  * @extends Roo.util.Observable
8002  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8003  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8004  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8005  * the element will be wrapped for you automatically.</p>
8006  * <p>Here is the list of valid resize handles:</p>
8007  * <pre>
8008 Value   Description
8009 ------  -------------------
8010  'n'     north
8011  's'     south
8012  'e'     east
8013  'w'     west
8014  'nw'    northwest
8015  'sw'    southwest
8016  'se'    southeast
8017  'ne'    northeast
8018  'hd'    horizontal drag
8019  'all'   all
8020 </pre>
8021  * <p>Here's an example showing the creation of a typical Resizable:</p>
8022  * <pre><code>
8023 var resizer = new Roo.Resizable("element-id", {
8024     handles: 'all',
8025     minWidth: 200,
8026     minHeight: 100,
8027     maxWidth: 500,
8028     maxHeight: 400,
8029     pinned: true
8030 });
8031 resizer.on("resize", myHandler);
8032 </code></pre>
8033  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8034  * resizer.east.setDisplayed(false);</p>
8035  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8036  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8037  * resize operation's new size (defaults to [0, 0])
8038  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8039  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8040  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8041  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8042  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8043  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8044  * @cfg {Number} width The width of the element in pixels (defaults to null)
8045  * @cfg {Number} height The height of the element in pixels (defaults to null)
8046  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8047  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8048  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8049  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8050  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8051  * in favor of the handles config option (defaults to false)
8052  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8053  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8054  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8055  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8056  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8057  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8058  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8059  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8060  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8061  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8062  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8063  * @constructor
8064  * Create a new resizable component
8065  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8066  * @param {Object} config configuration options
8067   */
8068 Roo.Resizable = function(el, config)
8069 {
8070     this.el = Roo.get(el);
8071
8072     if(config && config.wrap){
8073         config.resizeChild = this.el;
8074         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8075         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8076         this.el.setStyle("overflow", "hidden");
8077         this.el.setPositioning(config.resizeChild.getPositioning());
8078         config.resizeChild.clearPositioning();
8079         if(!config.width || !config.height){
8080             var csize = config.resizeChild.getSize();
8081             this.el.setSize(csize.width, csize.height);
8082         }
8083         if(config.pinned && !config.adjustments){
8084             config.adjustments = "auto";
8085         }
8086     }
8087
8088     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8089     this.proxy.unselectable();
8090     this.proxy.enableDisplayMode('block');
8091
8092     Roo.apply(this, config);
8093
8094     if(this.pinned){
8095         this.disableTrackOver = true;
8096         this.el.addClass("x-resizable-pinned");
8097     }
8098     // if the element isn't positioned, make it relative
8099     var position = this.el.getStyle("position");
8100     if(position != "absolute" && position != "fixed"){
8101         this.el.setStyle("position", "relative");
8102     }
8103     if(!this.handles){ // no handles passed, must be legacy style
8104         this.handles = 's,e,se';
8105         if(this.multiDirectional){
8106             this.handles += ',n,w';
8107         }
8108     }
8109     if(this.handles == "all"){
8110         this.handles = "n s e w ne nw se sw";
8111     }
8112     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8113     var ps = Roo.Resizable.positions;
8114     for(var i = 0, len = hs.length; i < len; i++){
8115         if(hs[i] && ps[hs[i]]){
8116             var pos = ps[hs[i]];
8117             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8118         }
8119     }
8120     // legacy
8121     this.corner = this.southeast;
8122     
8123     // updateBox = the box can move..
8124     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8125         this.updateBox = true;
8126     }
8127
8128     this.activeHandle = null;
8129
8130     if(this.resizeChild){
8131         if(typeof this.resizeChild == "boolean"){
8132             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8133         }else{
8134             this.resizeChild = Roo.get(this.resizeChild, true);
8135         }
8136     }
8137     
8138     if(this.adjustments == "auto"){
8139         var rc = this.resizeChild;
8140         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8141         if(rc && (hw || hn)){
8142             rc.position("relative");
8143             rc.setLeft(hw ? hw.el.getWidth() : 0);
8144             rc.setTop(hn ? hn.el.getHeight() : 0);
8145         }
8146         this.adjustments = [
8147             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8148             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8149         ];
8150     }
8151
8152     if(this.draggable){
8153         this.dd = this.dynamic ?
8154             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8155         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8156     }
8157
8158     // public events
8159     this.addEvents({
8160         /**
8161          * @event beforeresize
8162          * Fired before resize is allowed. Set enabled to false to cancel resize.
8163          * @param {Roo.Resizable} this
8164          * @param {Roo.EventObject} e The mousedown event
8165          */
8166         "beforeresize" : true,
8167         /**
8168          * @event resizing
8169          * Fired a resizing.
8170          * @param {Roo.Resizable} this
8171          * @param {Number} x The new x position
8172          * @param {Number} y The new y position
8173          * @param {Number} w The new w width
8174          * @param {Number} h The new h hight
8175          * @param {Roo.EventObject} e The mouseup event
8176          */
8177         "resizing" : true,
8178         /**
8179          * @event resize
8180          * Fired after a resize.
8181          * @param {Roo.Resizable} this
8182          * @param {Number} width The new width
8183          * @param {Number} height The new height
8184          * @param {Roo.EventObject} e The mouseup event
8185          */
8186         "resize" : true
8187     });
8188
8189     if(this.width !== null && this.height !== null){
8190         this.resizeTo(this.width, this.height);
8191     }else{
8192         this.updateChildSize();
8193     }
8194     if(Roo.isIE){
8195         this.el.dom.style.zoom = 1;
8196     }
8197     Roo.Resizable.superclass.constructor.call(this);
8198 };
8199
8200 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8201         resizeChild : false,
8202         adjustments : [0, 0],
8203         minWidth : 5,
8204         minHeight : 5,
8205         maxWidth : 10000,
8206         maxHeight : 10000,
8207         enabled : true,
8208         animate : false,
8209         duration : .35,
8210         dynamic : false,
8211         handles : false,
8212         multiDirectional : false,
8213         disableTrackOver : false,
8214         easing : 'easeOutStrong',
8215         widthIncrement : 0,
8216         heightIncrement : 0,
8217         pinned : false,
8218         width : null,
8219         height : null,
8220         preserveRatio : false,
8221         transparent: false,
8222         minX: 0,
8223         minY: 0,
8224         draggable: false,
8225
8226         /**
8227          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8228          */
8229         constrainTo: undefined,
8230         /**
8231          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8232          */
8233         resizeRegion: undefined,
8234
8235
8236     /**
8237      * Perform a manual resize
8238      * @param {Number} width
8239      * @param {Number} height
8240      */
8241     resizeTo : function(width, height){
8242         this.el.setSize(width, height);
8243         this.updateChildSize();
8244         this.fireEvent("resize", this, width, height, null);
8245     },
8246
8247     // private
8248     startSizing : function(e, handle){
8249         this.fireEvent("beforeresize", this, e);
8250         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8251
8252             if(!this.overlay){
8253                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8254                 this.overlay.unselectable();
8255                 this.overlay.enableDisplayMode("block");
8256                 this.overlay.on("mousemove", this.onMouseMove, this);
8257                 this.overlay.on("mouseup", this.onMouseUp, this);
8258             }
8259             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8260
8261             this.resizing = true;
8262             this.startBox = this.el.getBox();
8263             this.startPoint = e.getXY();
8264             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8265                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8266
8267             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8268             this.overlay.show();
8269
8270             if(this.constrainTo) {
8271                 var ct = Roo.get(this.constrainTo);
8272                 this.resizeRegion = ct.getRegion().adjust(
8273                     ct.getFrameWidth('t'),
8274                     ct.getFrameWidth('l'),
8275                     -ct.getFrameWidth('b'),
8276                     -ct.getFrameWidth('r')
8277                 );
8278             }
8279
8280             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8281             this.proxy.show();
8282             this.proxy.setBox(this.startBox);
8283             if(!this.dynamic){
8284                 this.proxy.setStyle('visibility', 'visible');
8285             }
8286         }
8287     },
8288
8289     // private
8290     onMouseDown : function(handle, e){
8291         if(this.enabled){
8292             e.stopEvent();
8293             this.activeHandle = handle;
8294             this.startSizing(e, handle);
8295         }
8296     },
8297
8298     // private
8299     onMouseUp : function(e){
8300         var size = this.resizeElement();
8301         this.resizing = false;
8302         this.handleOut();
8303         this.overlay.hide();
8304         this.proxy.hide();
8305         this.fireEvent("resize", this, size.width, size.height, e);
8306     },
8307
8308     // private
8309     updateChildSize : function(){
8310         
8311         if(this.resizeChild){
8312             var el = this.el;
8313             var child = this.resizeChild;
8314             var adj = this.adjustments;
8315             if(el.dom.offsetWidth){
8316                 var b = el.getSize(true);
8317                 child.setSize(b.width+adj[0], b.height+adj[1]);
8318             }
8319             // Second call here for IE
8320             // The first call enables instant resizing and
8321             // the second call corrects scroll bars if they
8322             // exist
8323             if(Roo.isIE){
8324                 setTimeout(function(){
8325                     if(el.dom.offsetWidth){
8326                         var b = el.getSize(true);
8327                         child.setSize(b.width+adj[0], b.height+adj[1]);
8328                     }
8329                 }, 10);
8330             }
8331         }
8332     },
8333
8334     // private
8335     snap : function(value, inc, min){
8336         if(!inc || !value) {
8337             return value;
8338         }
8339         var newValue = value;
8340         var m = value % inc;
8341         if(m > 0){
8342             if(m > (inc/2)){
8343                 newValue = value + (inc-m);
8344             }else{
8345                 newValue = value - m;
8346             }
8347         }
8348         return Math.max(min, newValue);
8349     },
8350
8351     // private
8352     resizeElement : function(){
8353         var box = this.proxy.getBox();
8354         if(this.updateBox){
8355             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8356         }else{
8357             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8358         }
8359         this.updateChildSize();
8360         if(!this.dynamic){
8361             this.proxy.hide();
8362         }
8363         return box;
8364     },
8365
8366     // private
8367     constrain : function(v, diff, m, mx){
8368         if(v - diff < m){
8369             diff = v - m;
8370         }else if(v - diff > mx){
8371             diff = mx - v;
8372         }
8373         return diff;
8374     },
8375
8376     // private
8377     onMouseMove : function(e){
8378         
8379         if(this.enabled){
8380             try{// try catch so if something goes wrong the user doesn't get hung
8381
8382             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8383                 return;
8384             }
8385
8386             //var curXY = this.startPoint;
8387             var curSize = this.curSize || this.startBox;
8388             var x = this.startBox.x, y = this.startBox.y;
8389             var ox = x, oy = y;
8390             var w = curSize.width, h = curSize.height;
8391             var ow = w, oh = h;
8392             var mw = this.minWidth, mh = this.minHeight;
8393             var mxw = this.maxWidth, mxh = this.maxHeight;
8394             var wi = this.widthIncrement;
8395             var hi = this.heightIncrement;
8396
8397             var eventXY = e.getXY();
8398             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8399             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8400
8401             var pos = this.activeHandle.position;
8402
8403             switch(pos){
8404                 case "east":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     break;
8408              
8409                 case "south":
8410                     h += diffY;
8411                     h = Math.min(Math.max(mh, h), mxh);
8412                     break;
8413                 case "southeast":
8414                     w += diffX;
8415                     h += diffY;
8416                     w = Math.min(Math.max(mw, w), mxw);
8417                     h = Math.min(Math.max(mh, h), mxh);
8418                     break;
8419                 case "north":
8420                     diffY = this.constrain(h, diffY, mh, mxh);
8421                     y += diffY;
8422                     h -= diffY;
8423                     break;
8424                 case "hdrag":
8425                     
8426                     if (wi) {
8427                         var adiffX = Math.abs(diffX);
8428                         var sub = (adiffX % wi); // how much 
8429                         if (sub > (wi/2)) { // far enough to snap
8430                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8431                         } else {
8432                             // remove difference.. 
8433                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8434                         }
8435                     }
8436                     x += diffX;
8437                     x = Math.max(this.minX, x);
8438                     break;
8439                 case "west":
8440                     diffX = this.constrain(w, diffX, mw, mxw);
8441                     x += diffX;
8442                     w -= diffX;
8443                     break;
8444                 case "northeast":
8445                     w += diffX;
8446                     w = Math.min(Math.max(mw, w), mxw);
8447                     diffY = this.constrain(h, diffY, mh, mxh);
8448                     y += diffY;
8449                     h -= diffY;
8450                     break;
8451                 case "northwest":
8452                     diffX = this.constrain(w, diffX, mw, mxw);
8453                     diffY = this.constrain(h, diffY, mh, mxh);
8454                     y += diffY;
8455                     h -= diffY;
8456                     x += diffX;
8457                     w -= diffX;
8458                     break;
8459                case "southwest":
8460                     diffX = this.constrain(w, diffX, mw, mxw);
8461                     h += diffY;
8462                     h = Math.min(Math.max(mh, h), mxh);
8463                     x += diffX;
8464                     w -= diffX;
8465                     break;
8466             }
8467
8468             var sw = this.snap(w, wi, mw);
8469             var sh = this.snap(h, hi, mh);
8470             if(sw != w || sh != h){
8471                 switch(pos){
8472                     case "northeast":
8473                         y -= sh - h;
8474                     break;
8475                     case "north":
8476                         y -= sh - h;
8477                         break;
8478                     case "southwest":
8479                         x -= sw - w;
8480                     break;
8481                     case "west":
8482                         x -= sw - w;
8483                         break;
8484                     case "northwest":
8485                         x -= sw - w;
8486                         y -= sh - h;
8487                     break;
8488                 }
8489                 w = sw;
8490                 h = sh;
8491             }
8492
8493             if(this.preserveRatio){
8494                 switch(pos){
8495                     case "southeast":
8496                     case "east":
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                        break;
8501                     case "south":
8502                         w = ow * (h/oh);
8503                         w = Math.min(Math.max(mw, w), mxw);
8504                         h = oh * (w/ow);
8505                         break;
8506                     case "northeast":
8507                         w = ow * (h/oh);
8508                         w = Math.min(Math.max(mw, w), mxw);
8509                         h = oh * (w/ow);
8510                     break;
8511                     case "north":
8512                         var tw = w;
8513                         w = ow * (h/oh);
8514                         w = Math.min(Math.max(mw, w), mxw);
8515                         h = oh * (w/ow);
8516                         x += (tw - w) / 2;
8517                         break;
8518                     case "southwest":
8519                         h = oh * (w/ow);
8520                         h = Math.min(Math.max(mh, h), mxh);
8521                         var tw = w;
8522                         w = ow * (h/oh);
8523                         x += tw - w;
8524                         break;
8525                     case "west":
8526                         var th = h;
8527                         h = oh * (w/ow);
8528                         h = Math.min(Math.max(mh, h), mxh);
8529                         y += (th - h) / 2;
8530                         var tw = w;
8531                         w = ow * (h/oh);
8532                         x += tw - w;
8533                        break;
8534                     case "northwest":
8535                         var tw = w;
8536                         var th = h;
8537                         h = oh * (w/ow);
8538                         h = Math.min(Math.max(mh, h), mxh);
8539                         w = ow * (h/oh);
8540                         y += th - h;
8541                         x += tw - w;
8542                        break;
8543
8544                 }
8545             }
8546             if (pos == 'hdrag') {
8547                 w = ow;
8548             }
8549             this.proxy.setBounds(x, y, w, h);
8550             if(this.dynamic){
8551                 this.resizeElement();
8552             }
8553             }catch(e){}
8554         }
8555         this.fireEvent("resizing", this, x, y, w, h, e);
8556     },
8557
8558     // private
8559     handleOver : function(){
8560         if(this.enabled){
8561             this.el.addClass("x-resizable-over");
8562         }
8563     },
8564
8565     // private
8566     handleOut : function(){
8567         if(!this.resizing){
8568             this.el.removeClass("x-resizable-over");
8569         }
8570     },
8571
8572     /**
8573      * Returns the element this component is bound to.
8574      * @return {Roo.Element}
8575      */
8576     getEl : function(){
8577         return this.el;
8578     },
8579
8580     /**
8581      * Returns the resizeChild element (or null).
8582      * @return {Roo.Element}
8583      */
8584     getResizeChild : function(){
8585         return this.resizeChild;
8586     },
8587     groupHandler : function()
8588     {
8589         
8590     },
8591     /**
8592      * Destroys this resizable. If the element was wrapped and
8593      * removeEl is not true then the element remains.
8594      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8595      */
8596     destroy : function(removeEl){
8597         this.proxy.remove();
8598         if(this.overlay){
8599             this.overlay.removeAllListeners();
8600             this.overlay.remove();
8601         }
8602         var ps = Roo.Resizable.positions;
8603         for(var k in ps){
8604             if(typeof ps[k] != "function" && this[ps[k]]){
8605                 var h = this[ps[k]];
8606                 h.el.removeAllListeners();
8607                 h.el.remove();
8608             }
8609         }
8610         if(removeEl){
8611             this.el.update("");
8612             this.el.remove();
8613         }
8614     }
8615 });
8616
8617 // private
8618 // hash to map config positions to true positions
8619 Roo.Resizable.positions = {
8620     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8621     hd: "hdrag"
8622 };
8623
8624 // private
8625 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8626     if(!this.tpl){
8627         // only initialize the template if resizable is used
8628         var tpl = Roo.DomHelper.createTemplate(
8629             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8630         );
8631         tpl.compile();
8632         Roo.Resizable.Handle.prototype.tpl = tpl;
8633     }
8634     this.position = pos;
8635     this.rz = rz;
8636     // show north drag fro topdra
8637     var handlepos = pos == 'hdrag' ? 'north' : pos;
8638     
8639     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8640     if (pos == 'hdrag') {
8641         this.el.setStyle('cursor', 'pointer');
8642     }
8643     this.el.unselectable();
8644     if(transparent){
8645         this.el.setOpacity(0);
8646     }
8647     this.el.on("mousedown", this.onMouseDown, this);
8648     if(!disableTrackOver){
8649         this.el.on("mouseover", this.onMouseOver, this);
8650         this.el.on("mouseout", this.onMouseOut, this);
8651     }
8652 };
8653
8654 // private
8655 Roo.Resizable.Handle.prototype = {
8656     afterResize : function(rz){
8657         Roo.log('after?');
8658         // do nothing
8659     },
8660     // private
8661     onMouseDown : function(e){
8662         this.rz.onMouseDown(this, e);
8663     },
8664     // private
8665     onMouseOver : function(e){
8666         this.rz.handleOver(this, e);
8667     },
8668     // private
8669     onMouseOut : function(e){
8670         this.rz.handleOut(this, e);
8671     }
8672 };/*
8673  * Based on:
8674  * Ext JS Library 1.1.1
8675  * Copyright(c) 2006-2007, Ext JS, LLC.
8676  *
8677  * Originally Released Under LGPL - original licence link has changed is not relivant.
8678  *
8679  * Fork - LGPL
8680  * <script type="text/javascript">
8681  */
8682
8683 /**
8684  * @class Roo.Editor
8685  * @extends Roo.Component
8686  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8687  * @constructor
8688  * Create a new Editor
8689  * @param {Roo.form.Field} field The Field object (or descendant)
8690  * @param {Object} config The config object
8691  */
8692 Roo.Editor = function(field, config){
8693     Roo.Editor.superclass.constructor.call(this, config);
8694     this.field = field;
8695     this.addEvents({
8696         /**
8697              * @event beforestartedit
8698              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8699              * false from the handler of this event.
8700              * @param {Editor} this
8701              * @param {Roo.Element} boundEl The underlying element bound to this editor
8702              * @param {Mixed} value The field value being set
8703              */
8704         "beforestartedit" : true,
8705         /**
8706              * @event startedit
8707              * Fires when this editor is displayed
8708              * @param {Roo.Element} boundEl The underlying element bound to this editor
8709              * @param {Mixed} value The starting field value
8710              */
8711         "startedit" : true,
8712         /**
8713              * @event beforecomplete
8714              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8715              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8716              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8717              * event will not fire since no edit actually occurred.
8718              * @param {Editor} this
8719              * @param {Mixed} value The current field value
8720              * @param {Mixed} startValue The original field value
8721              */
8722         "beforecomplete" : true,
8723         /**
8724              * @event complete
8725              * Fires after editing is complete and any changed value has been written to the underlying field.
8726              * @param {Editor} this
8727              * @param {Mixed} value The current field value
8728              * @param {Mixed} startValue The original field value
8729              */
8730         "complete" : true,
8731         /**
8732          * @event specialkey
8733          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8734          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8735          * @param {Roo.form.Field} this
8736          * @param {Roo.EventObject} e The event object
8737          */
8738         "specialkey" : true
8739     });
8740 };
8741
8742 Roo.extend(Roo.Editor, Roo.Component, {
8743     /**
8744      * @cfg {Boolean/String} autosize
8745      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8746      * or "height" to adopt the height only (defaults to false)
8747      */
8748     /**
8749      * @cfg {Boolean} revertInvalid
8750      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8751      * validation fails (defaults to true)
8752      */
8753     /**
8754      * @cfg {Boolean} ignoreNoChange
8755      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8756      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8757      * will never be ignored.
8758      */
8759     /**
8760      * @cfg {Boolean} hideEl
8761      * False to keep the bound element visible while the editor is displayed (defaults to true)
8762      */
8763     /**
8764      * @cfg {Mixed} value
8765      * The data value of the underlying field (defaults to "")
8766      */
8767     value : "",
8768     /**
8769      * @cfg {String} alignment
8770      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8771      */
8772     alignment: "c-c?",
8773     /**
8774      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8775      * for bottom-right shadow (defaults to "frame")
8776      */
8777     shadow : "frame",
8778     /**
8779      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8780      */
8781     constrain : false,
8782     /**
8783      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8784      */
8785     completeOnEnter : false,
8786     /**
8787      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8788      */
8789     cancelOnEsc : false,
8790     /**
8791      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8792      */
8793     updateEl : false,
8794
8795     // private
8796     onRender : function(ct, position){
8797         this.el = new Roo.Layer({
8798             shadow: this.shadow,
8799             cls: "x-editor",
8800             parentEl : ct,
8801             shim : this.shim,
8802             shadowOffset:4,
8803             id: this.id,
8804             constrain: this.constrain
8805         });
8806         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8807         if(this.field.msgTarget != 'title'){
8808             this.field.msgTarget = 'qtip';
8809         }
8810         this.field.render(this.el);
8811         if(Roo.isGecko){
8812             this.field.el.dom.setAttribute('autocomplete', 'off');
8813         }
8814         this.field.on("specialkey", this.onSpecialKey, this);
8815         if(this.swallowKeys){
8816             this.field.el.swallowEvent(['keydown','keypress']);
8817         }
8818         this.field.show();
8819         this.field.on("blur", this.onBlur, this);
8820         if(this.field.grow){
8821             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8822         }
8823     },
8824
8825     onSpecialKey : function(field, e)
8826     {
8827         //Roo.log('editor onSpecialKey');
8828         if(this.completeOnEnter && e.getKey() == e.ENTER){
8829             e.stopEvent();
8830             this.completeEdit();
8831             return;
8832         }
8833         // do not fire special key otherwise it might hide close the editor...
8834         if(e.getKey() == e.ENTER){    
8835             return;
8836         }
8837         if(this.cancelOnEsc && e.getKey() == e.ESC){
8838             this.cancelEdit();
8839             return;
8840         } 
8841         this.fireEvent('specialkey', field, e);
8842     
8843     },
8844
8845     /**
8846      * Starts the editing process and shows the editor.
8847      * @param {String/HTMLElement/Element} el The element to edit
8848      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8849       * to the innerHTML of el.
8850      */
8851     startEdit : function(el, value){
8852         if(this.editing){
8853             this.completeEdit();
8854         }
8855         this.boundEl = Roo.get(el);
8856         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8857         if(!this.rendered){
8858             this.render(this.parentEl || document.body);
8859         }
8860         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8861             return;
8862         }
8863         this.startValue = v;
8864         this.field.setValue(v);
8865         if(this.autoSize){
8866             var sz = this.boundEl.getSize();
8867             switch(this.autoSize){
8868                 case "width":
8869                 this.setSize(sz.width,  "");
8870                 break;
8871                 case "height":
8872                 this.setSize("",  sz.height);
8873                 break;
8874                 default:
8875                 this.setSize(sz.width,  sz.height);
8876             }
8877         }
8878         this.el.alignTo(this.boundEl, this.alignment);
8879         this.editing = true;
8880         if(Roo.QuickTips){
8881             Roo.QuickTips.disable();
8882         }
8883         this.show();
8884     },
8885
8886     /**
8887      * Sets the height and width of this editor.
8888      * @param {Number} width The new width
8889      * @param {Number} height The new height
8890      */
8891     setSize : function(w, h){
8892         this.field.setSize(w, h);
8893         if(this.el){
8894             this.el.sync();
8895         }
8896     },
8897
8898     /**
8899      * Realigns the editor to the bound field based on the current alignment config value.
8900      */
8901     realign : function(){
8902         this.el.alignTo(this.boundEl, this.alignment);
8903     },
8904
8905     /**
8906      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8907      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8908      */
8909     completeEdit : function(remainVisible){
8910         if(!this.editing){
8911             return;
8912         }
8913         var v = this.getValue();
8914         if(this.revertInvalid !== false && !this.field.isValid()){
8915             v = this.startValue;
8916             this.cancelEdit(true);
8917         }
8918         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8919             this.editing = false;
8920             this.hide();
8921             return;
8922         }
8923         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8924             this.editing = false;
8925             if(this.updateEl && this.boundEl){
8926                 this.boundEl.update(v);
8927             }
8928             if(remainVisible !== true){
8929                 this.hide();
8930             }
8931             this.fireEvent("complete", this, v, this.startValue);
8932         }
8933     },
8934
8935     // private
8936     onShow : function(){
8937         this.el.show();
8938         if(this.hideEl !== false){
8939             this.boundEl.hide();
8940         }
8941         this.field.show();
8942         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8943             this.fixIEFocus = true;
8944             this.deferredFocus.defer(50, this);
8945         }else{
8946             this.field.focus();
8947         }
8948         this.fireEvent("startedit", this.boundEl, this.startValue);
8949     },
8950
8951     deferredFocus : function(){
8952         if(this.editing){
8953             this.field.focus();
8954         }
8955     },
8956
8957     /**
8958      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8959      * reverted to the original starting value.
8960      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8961      * cancel (defaults to false)
8962      */
8963     cancelEdit : function(remainVisible){
8964         if(this.editing){
8965             this.setValue(this.startValue);
8966             if(remainVisible !== true){
8967                 this.hide();
8968             }
8969         }
8970     },
8971
8972     // private
8973     onBlur : function(){
8974         if(this.allowBlur !== true && this.editing){
8975             this.completeEdit();
8976         }
8977     },
8978
8979     // private
8980     onHide : function(){
8981         if(this.editing){
8982             this.completeEdit();
8983             return;
8984         }
8985         this.field.blur();
8986         if(this.field.collapse){
8987             this.field.collapse();
8988         }
8989         this.el.hide();
8990         if(this.hideEl !== false){
8991             this.boundEl.show();
8992         }
8993         if(Roo.QuickTips){
8994             Roo.QuickTips.enable();
8995         }
8996     },
8997
8998     /**
8999      * Sets the data value of the editor
9000      * @param {Mixed} value Any valid value supported by the underlying field
9001      */
9002     setValue : function(v){
9003         this.field.setValue(v);
9004     },
9005
9006     /**
9007      * Gets the data value of the editor
9008      * @return {Mixed} The data value
9009      */
9010     getValue : function(){
9011         return this.field.getValue();
9012     }
9013 });/*
9014  * Based on:
9015  * Ext JS Library 1.1.1
9016  * Copyright(c) 2006-2007, Ext JS, LLC.
9017  *
9018  * Originally Released Under LGPL - original licence link has changed is not relivant.
9019  *
9020  * Fork - LGPL
9021  * <script type="text/javascript">
9022  */
9023  
9024 /**
9025  * @class Roo.BasicDialog
9026  * @extends Roo.util.Observable
9027  * @parent none builder
9028  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9029  * <pre><code>
9030 var dlg = new Roo.BasicDialog("my-dlg", {
9031     height: 200,
9032     width: 300,
9033     minHeight: 100,
9034     minWidth: 150,
9035     modal: true,
9036     proxyDrag: true,
9037     shadow: true
9038 });
9039 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9040 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9041 dlg.addButton('Cancel', dlg.hide, dlg);
9042 dlg.show();
9043 </code></pre>
9044   <b>A Dialog should always be a direct child of the body element.</b>
9045  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9046  * @cfg {String} title Default text to display in the title bar (defaults to null)
9047  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9048  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9049  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9050  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9051  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9052  * (defaults to null with no animation)
9053  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9054  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9055  * property for valid values (defaults to 'all')
9056  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9057  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9058  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9059  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9060  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9061  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9062  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9063  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9064  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9065  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9066  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9067  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9068  * draggable = true (defaults to false)
9069  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9070  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9071  * shadow (defaults to false)
9072  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9073  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9074  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9075  * @cfg {Array} buttons Array of buttons
9076  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9077  * @constructor
9078  * Create a new BasicDialog.
9079  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9080  * @param {Object} config Configuration options
9081  */
9082 Roo.BasicDialog = function(el, config){
9083     this.el = Roo.get(el);
9084     var dh = Roo.DomHelper;
9085     if(!this.el && config && config.autoCreate){
9086         if(typeof config.autoCreate == "object"){
9087             if(!config.autoCreate.id){
9088                 config.autoCreate.id = el;
9089             }
9090             this.el = dh.append(document.body,
9091                         config.autoCreate, true);
9092         }else{
9093             this.el = dh.append(document.body,
9094                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9095         }
9096     }
9097     el = this.el;
9098     el.setDisplayed(true);
9099     el.hide = this.hideAction;
9100     this.id = el.id;
9101     el.addClass("x-dlg");
9102
9103     Roo.apply(this, config);
9104
9105     this.proxy = el.createProxy("x-dlg-proxy");
9106     this.proxy.hide = this.hideAction;
9107     this.proxy.setOpacity(.5);
9108     this.proxy.hide();
9109
9110     if(config.width){
9111         el.setWidth(config.width);
9112     }
9113     if(config.height){
9114         el.setHeight(config.height);
9115     }
9116     this.size = el.getSize();
9117     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9118         this.xy = [config.x,config.y];
9119     }else{
9120         this.xy = el.getCenterXY(true);
9121     }
9122     /** The header element @type Roo.Element */
9123     this.header = el.child("> .x-dlg-hd");
9124     /** The body element @type Roo.Element */
9125     this.body = el.child("> .x-dlg-bd");
9126     /** The footer element @type Roo.Element */
9127     this.footer = el.child("> .x-dlg-ft");
9128
9129     if(!this.header){
9130         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9131     }
9132     if(!this.body){
9133         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9134     }
9135
9136     this.header.unselectable();
9137     if(this.title){
9138         this.header.update(this.title);
9139     }
9140     // this element allows the dialog to be focused for keyboard event
9141     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9142     this.focusEl.swallowEvent("click", true);
9143
9144     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9145
9146     // wrap the body and footer for special rendering
9147     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9148     if(this.footer){
9149         this.bwrap.dom.appendChild(this.footer.dom);
9150     }
9151
9152     this.bg = this.el.createChild({
9153         tag: "div", cls:"x-dlg-bg",
9154         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9155     });
9156     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9157
9158
9159     if(this.autoScroll !== false && !this.autoTabs){
9160         this.body.setStyle("overflow", "auto");
9161     }
9162
9163     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9164
9165     if(this.closable !== false){
9166         this.el.addClass("x-dlg-closable");
9167         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9168         this.close.on("click", this.closeClick, this);
9169         this.close.addClassOnOver("x-dlg-close-over");
9170     }
9171     if(this.collapsible !== false){
9172         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9173         this.collapseBtn.on("click", this.collapseClick, this);
9174         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9175         this.header.on("dblclick", this.collapseClick, this);
9176     }
9177     if(this.resizable !== false){
9178         this.el.addClass("x-dlg-resizable");
9179         this.resizer = new Roo.Resizable(el, {
9180             minWidth: this.minWidth || 80,
9181             minHeight:this.minHeight || 80,
9182             handles: this.resizeHandles || "all",
9183             pinned: true
9184         });
9185         this.resizer.on("beforeresize", this.beforeResize, this);
9186         this.resizer.on("resize", this.onResize, this);
9187     }
9188     if(this.draggable !== false){
9189         el.addClass("x-dlg-draggable");
9190         if (!this.proxyDrag) {
9191             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9192         }
9193         else {
9194             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9195         }
9196         dd.setHandleElId(this.header.id);
9197         dd.endDrag = this.endMove.createDelegate(this);
9198         dd.startDrag = this.startMove.createDelegate(this);
9199         dd.onDrag = this.onDrag.createDelegate(this);
9200         dd.scroll = false;
9201         this.dd = dd;
9202     }
9203     if(this.modal){
9204         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9205         this.mask.enableDisplayMode("block");
9206         this.mask.hide();
9207         this.el.addClass("x-dlg-modal");
9208     }
9209     if(this.shadow){
9210         this.shadow = new Roo.Shadow({
9211             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9212             offset : this.shadowOffset
9213         });
9214     }else{
9215         this.shadowOffset = 0;
9216     }
9217     if(Roo.useShims && this.shim !== false){
9218         this.shim = this.el.createShim();
9219         this.shim.hide = this.hideAction;
9220         this.shim.hide();
9221     }else{
9222         this.shim = false;
9223     }
9224     if(this.autoTabs){
9225         this.initTabs();
9226     }
9227     if (this.buttons) { 
9228         var bts= this.buttons;
9229         this.buttons = [];
9230         Roo.each(bts, function(b) {
9231             this.addButton(b);
9232         }, this);
9233     }
9234     
9235     
9236     this.addEvents({
9237         /**
9238          * @event keydown
9239          * Fires when a key is pressed
9240          * @param {Roo.BasicDialog} this
9241          * @param {Roo.EventObject} e
9242          */
9243         "keydown" : true,
9244         /**
9245          * @event move
9246          * Fires when this dialog is moved by the user.
9247          * @param {Roo.BasicDialog} this
9248          * @param {Number} x The new page X
9249          * @param {Number} y The new page Y
9250          */
9251         "move" : true,
9252         /**
9253          * @event resize
9254          * Fires when this dialog is resized by the user.
9255          * @param {Roo.BasicDialog} this
9256          * @param {Number} width The new width
9257          * @param {Number} height The new height
9258          */
9259         "resize" : true,
9260         /**
9261          * @event beforehide
9262          * Fires before this dialog is hidden.
9263          * @param {Roo.BasicDialog} this
9264          */
9265         "beforehide" : true,
9266         /**
9267          * @event hide
9268          * Fires when this dialog is hidden.
9269          * @param {Roo.BasicDialog} this
9270          */
9271         "hide" : true,
9272         /**
9273          * @event beforeshow
9274          * Fires before this dialog is shown.
9275          * @param {Roo.BasicDialog} this
9276          */
9277         "beforeshow" : true,
9278         /**
9279          * @event show
9280          * Fires when this dialog is shown.
9281          * @param {Roo.BasicDialog} this
9282          */
9283         "show" : true
9284     });
9285     el.on("keydown", this.onKeyDown, this);
9286     el.on("mousedown", this.toFront, this);
9287     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9288     this.el.hide();
9289     Roo.DialogManager.register(this);
9290     Roo.BasicDialog.superclass.constructor.call(this);
9291 };
9292
9293 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9294     shadowOffset: Roo.isIE ? 6 : 5,
9295     minHeight: 80,
9296     minWidth: 200,
9297     minButtonWidth: 75,
9298     defaultButton: null,
9299     buttonAlign: "right",
9300     tabTag: 'div',
9301     firstShow: true,
9302
9303     /**
9304      * Sets the dialog title text
9305      * @param {String} text The title text to display
9306      * @return {Roo.BasicDialog} this
9307      */
9308     setTitle : function(text){
9309         this.header.update(text);
9310         return this;
9311     },
9312
9313     // private
9314     closeClick : function(){
9315         this.hide();
9316     },
9317
9318     // private
9319     collapseClick : function(){
9320         this[this.collapsed ? "expand" : "collapse"]();
9321     },
9322
9323     /**
9324      * Collapses the dialog to its minimized state (only the title bar is visible).
9325      * Equivalent to the user clicking the collapse dialog button.
9326      */
9327     collapse : function(){
9328         if(!this.collapsed){
9329             this.collapsed = true;
9330             this.el.addClass("x-dlg-collapsed");
9331             this.restoreHeight = this.el.getHeight();
9332             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9333         }
9334     },
9335
9336     /**
9337      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9338      * clicking the expand dialog button.
9339      */
9340     expand : function(){
9341         if(this.collapsed){
9342             this.collapsed = false;
9343             this.el.removeClass("x-dlg-collapsed");
9344             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9345         }
9346     },
9347
9348     /**
9349      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9350      * @return {Roo.TabPanel} The tabs component
9351      */
9352     initTabs : function(){
9353         var tabs = this.getTabs();
9354         while(tabs.getTab(0)){
9355             tabs.removeTab(0);
9356         }
9357         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9358             var dom = el.dom;
9359             tabs.addTab(Roo.id(dom), dom.title);
9360             dom.title = "";
9361         });
9362         tabs.activate(0);
9363         return tabs;
9364     },
9365
9366     // private
9367     beforeResize : function(){
9368         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9369     },
9370
9371     // private
9372     onResize : function(){
9373         this.refreshSize();
9374         this.syncBodyHeight();
9375         this.adjustAssets();
9376         this.focus();
9377         this.fireEvent("resize", this, this.size.width, this.size.height);
9378     },
9379
9380     // private
9381     onKeyDown : function(e){
9382         if(this.isVisible()){
9383             this.fireEvent("keydown", this, e);
9384         }
9385     },
9386
9387     /**
9388      * Resizes the dialog.
9389      * @param {Number} width
9390      * @param {Number} height
9391      * @return {Roo.BasicDialog} this
9392      */
9393     resizeTo : function(width, height){
9394         this.el.setSize(width, height);
9395         this.size = {width: width, height: height};
9396         this.syncBodyHeight();
9397         if(this.fixedcenter){
9398             this.center();
9399         }
9400         if(this.isVisible()){
9401             this.constrainXY();
9402             this.adjustAssets();
9403         }
9404         this.fireEvent("resize", this, width, height);
9405         return this;
9406     },
9407
9408
9409     /**
9410      * Resizes the dialog to fit the specified content size.
9411      * @param {Number} width
9412      * @param {Number} height
9413      * @return {Roo.BasicDialog} this
9414      */
9415     setContentSize : function(w, h){
9416         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9417         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9418         //if(!this.el.isBorderBox()){
9419             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9420             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9421         //}
9422         if(this.tabs){
9423             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9424             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9425         }
9426         this.resizeTo(w, h);
9427         return this;
9428     },
9429
9430     /**
9431      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9432      * executed in response to a particular key being pressed while the dialog is active.
9433      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9434      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9435      * @param {Function} fn The function to call
9436      * @param {Object} scope (optional) The scope of the function
9437      * @return {Roo.BasicDialog} this
9438      */
9439     addKeyListener : function(key, fn, scope){
9440         var keyCode, shift, ctrl, alt;
9441         if(typeof key == "object" && !(key instanceof Array)){
9442             keyCode = key["key"];
9443             shift = key["shift"];
9444             ctrl = key["ctrl"];
9445             alt = key["alt"];
9446         }else{
9447             keyCode = key;
9448         }
9449         var handler = function(dlg, e){
9450             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9451                 var k = e.getKey();
9452                 if(keyCode instanceof Array){
9453                     for(var i = 0, len = keyCode.length; i < len; i++){
9454                         if(keyCode[i] == k){
9455                           fn.call(scope || window, dlg, k, e);
9456                           return;
9457                         }
9458                     }
9459                 }else{
9460                     if(k == keyCode){
9461                         fn.call(scope || window, dlg, k, e);
9462                     }
9463                 }
9464             }
9465         };
9466         this.on("keydown", handler);
9467         return this;
9468     },
9469
9470     /**
9471      * Returns the TabPanel component (creates it if it doesn't exist).
9472      * Note: If you wish to simply check for the existence of tabs without creating them,
9473      * check for a null 'tabs' property.
9474      * @return {Roo.TabPanel} The tabs component
9475      */
9476     getTabs : function(){
9477         if(!this.tabs){
9478             this.el.addClass("x-dlg-auto-tabs");
9479             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9480             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9481         }
9482         return this.tabs;
9483     },
9484
9485     /**
9486      * Adds a button to the footer section of the dialog.
9487      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9488      * object or a valid Roo.DomHelper element config
9489      * @param {Function} handler The function called when the button is clicked
9490      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9491      * @return {Roo.Button} The new button
9492      */
9493     addButton : function(config, handler, scope){
9494         var dh = Roo.DomHelper;
9495         if(!this.footer){
9496             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9497         }
9498         if(!this.btnContainer){
9499             var tb = this.footer.createChild({
9500
9501                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9502                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9503             }, null, true);
9504             this.btnContainer = tb.firstChild.firstChild.firstChild;
9505         }
9506         var bconfig = {
9507             handler: handler,
9508             scope: scope,
9509             minWidth: this.minButtonWidth,
9510             hideParent:true
9511         };
9512         if(typeof config == "string"){
9513             bconfig.text = config;
9514         }else{
9515             if(config.tag){
9516                 bconfig.dhconfig = config;
9517             }else{
9518                 Roo.apply(bconfig, config);
9519             }
9520         }
9521         var fc = false;
9522         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9523             bconfig.position = Math.max(0, bconfig.position);
9524             fc = this.btnContainer.childNodes[bconfig.position];
9525         }
9526          
9527         var btn = new Roo.Button(
9528             fc ? 
9529                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9530                 : this.btnContainer.appendChild(document.createElement("td")),
9531             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9532             bconfig
9533         );
9534         this.syncBodyHeight();
9535         if(!this.buttons){
9536             /**
9537              * Array of all the buttons that have been added to this dialog via addButton
9538              * @type Array
9539              */
9540             this.buttons = [];
9541         }
9542         this.buttons.push(btn);
9543         return btn;
9544     },
9545
9546     /**
9547      * Sets the default button to be focused when the dialog is displayed.
9548      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9549      * @return {Roo.BasicDialog} this
9550      */
9551     setDefaultButton : function(btn){
9552         this.defaultButton = btn;
9553         return this;
9554     },
9555
9556     // private
9557     getHeaderFooterHeight : function(safe){
9558         var height = 0;
9559         if(this.header){
9560            height += this.header.getHeight();
9561         }
9562         if(this.footer){
9563            var fm = this.footer.getMargins();
9564             height += (this.footer.getHeight()+fm.top+fm.bottom);
9565         }
9566         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9567         height += this.centerBg.getPadding("tb");
9568         return height;
9569     },
9570
9571     // private
9572     syncBodyHeight : function()
9573     {
9574         var bd = this.body, // the text
9575             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9576             bw = this.bwrap;
9577         var height = this.size.height - this.getHeaderFooterHeight(false);
9578         bd.setHeight(height-bd.getMargins("tb"));
9579         var hh = this.header.getHeight();
9580         var h = this.size.height-hh;
9581         cb.setHeight(h);
9582         
9583         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9584         bw.setHeight(h-cb.getPadding("tb"));
9585         
9586         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9587         bd.setWidth(bw.getWidth(true));
9588         if(this.tabs){
9589             this.tabs.syncHeight();
9590             if(Roo.isIE){
9591                 this.tabs.el.repaint();
9592             }
9593         }
9594     },
9595
9596     /**
9597      * Restores the previous state of the dialog if Roo.state is configured.
9598      * @return {Roo.BasicDialog} this
9599      */
9600     restoreState : function(){
9601         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9602         if(box && box.width){
9603             this.xy = [box.x, box.y];
9604             this.resizeTo(box.width, box.height);
9605         }
9606         return this;
9607     },
9608
9609     // private
9610     beforeShow : function(){
9611         this.expand();
9612         if(this.fixedcenter){
9613             this.xy = this.el.getCenterXY(true);
9614         }
9615         if(this.modal){
9616             Roo.get(document.body).addClass("x-body-masked");
9617             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9618             this.mask.show();
9619         }
9620         this.constrainXY();
9621     },
9622
9623     // private
9624     animShow : function(){
9625         var b = Roo.get(this.animateTarget).getBox();
9626         this.proxy.setSize(b.width, b.height);
9627         this.proxy.setLocation(b.x, b.y);
9628         this.proxy.show();
9629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9630                     true, .35, this.showEl.createDelegate(this));
9631     },
9632
9633     /**
9634      * Shows the dialog.
9635      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9636      * @return {Roo.BasicDialog} this
9637      */
9638     show : function(animateTarget){
9639         if (this.fireEvent("beforeshow", this) === false){
9640             return;
9641         }
9642         if(this.syncHeightBeforeShow){
9643             this.syncBodyHeight();
9644         }else if(this.firstShow){
9645             this.firstShow = false;
9646             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9647         }
9648         this.animateTarget = animateTarget || this.animateTarget;
9649         if(!this.el.isVisible()){
9650             this.beforeShow();
9651             if(this.animateTarget && Roo.get(this.animateTarget)){
9652                 this.animShow();
9653             }else{
9654                 this.showEl();
9655             }
9656         }
9657         return this;
9658     },
9659
9660     // private
9661     showEl : function(){
9662         this.proxy.hide();
9663         this.el.setXY(this.xy);
9664         this.el.show();
9665         this.adjustAssets(true);
9666         this.toFront();
9667         this.focus();
9668         // IE peekaboo bug - fix found by Dave Fenwick
9669         if(Roo.isIE){
9670             this.el.repaint();
9671         }
9672         this.fireEvent("show", this);
9673     },
9674
9675     /**
9676      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9677      * dialog itself will receive focus.
9678      */
9679     focus : function(){
9680         if(this.defaultButton){
9681             this.defaultButton.focus();
9682         }else{
9683             this.focusEl.focus();
9684         }
9685     },
9686
9687     // private
9688     constrainXY : function(){
9689         if(this.constraintoviewport !== false){
9690             if(!this.viewSize){
9691                 if(this.container){
9692                     var s = this.container.getSize();
9693                     this.viewSize = [s.width, s.height];
9694                 }else{
9695                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9696                 }
9697             }
9698             var s = Roo.get(this.container||document).getScroll();
9699
9700             var x = this.xy[0], y = this.xy[1];
9701             var w = this.size.width, h = this.size.height;
9702             var vw = this.viewSize[0], vh = this.viewSize[1];
9703             // only move it if it needs it
9704             var moved = false;
9705             // first validate right/bottom
9706             if(x + w > vw+s.left){
9707                 x = vw - w;
9708                 moved = true;
9709             }
9710             if(y + h > vh+s.top){
9711                 y = vh - h;
9712                 moved = true;
9713             }
9714             // then make sure top/left isn't negative
9715             if(x < s.left){
9716                 x = s.left;
9717                 moved = true;
9718             }
9719             if(y < s.top){
9720                 y = s.top;
9721                 moved = true;
9722             }
9723             if(moved){
9724                 // cache xy
9725                 this.xy = [x, y];
9726                 if(this.isVisible()){
9727                     this.el.setLocation(x, y);
9728                     this.adjustAssets();
9729                 }
9730             }
9731         }
9732     },
9733
9734     // private
9735     onDrag : function(){
9736         if(!this.proxyDrag){
9737             this.xy = this.el.getXY();
9738             this.adjustAssets();
9739         }
9740     },
9741
9742     // private
9743     adjustAssets : function(doShow){
9744         var x = this.xy[0], y = this.xy[1];
9745         var w = this.size.width, h = this.size.height;
9746         if(doShow === true){
9747             if(this.shadow){
9748                 this.shadow.show(this.el);
9749             }
9750             if(this.shim){
9751                 this.shim.show();
9752             }
9753         }
9754         if(this.shadow && this.shadow.isVisible()){
9755             this.shadow.show(this.el);
9756         }
9757         if(this.shim && this.shim.isVisible()){
9758             this.shim.setBounds(x, y, w, h);
9759         }
9760     },
9761
9762     // private
9763     adjustViewport : function(w, h){
9764         if(!w || !h){
9765             w = Roo.lib.Dom.getViewWidth();
9766             h = Roo.lib.Dom.getViewHeight();
9767         }
9768         // cache the size
9769         this.viewSize = [w, h];
9770         if(this.modal && this.mask.isVisible()){
9771             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9772             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9773         }
9774         if(this.isVisible()){
9775             this.constrainXY();
9776         }
9777     },
9778
9779     /**
9780      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9781      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9782      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9783      */
9784     destroy : function(removeEl){
9785         if(this.isVisible()){
9786             this.animateTarget = null;
9787             this.hide();
9788         }
9789         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9790         if(this.tabs){
9791             this.tabs.destroy(removeEl);
9792         }
9793         Roo.destroy(
9794              this.shim,
9795              this.proxy,
9796              this.resizer,
9797              this.close,
9798              this.mask
9799         );
9800         if(this.dd){
9801             this.dd.unreg();
9802         }
9803         if(this.buttons){
9804            for(var i = 0, len = this.buttons.length; i < len; i++){
9805                this.buttons[i].destroy();
9806            }
9807         }
9808         this.el.removeAllListeners();
9809         if(removeEl === true){
9810             this.el.update("");
9811             this.el.remove();
9812         }
9813         Roo.DialogManager.unregister(this);
9814     },
9815
9816     // private
9817     startMove : function(){
9818         if(this.proxyDrag){
9819             this.proxy.show();
9820         }
9821         if(this.constraintoviewport !== false){
9822             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9823         }
9824     },
9825
9826     // private
9827     endMove : function(){
9828         if(!this.proxyDrag){
9829             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9830         }else{
9831             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9832             this.proxy.hide();
9833         }
9834         this.refreshSize();
9835         this.adjustAssets();
9836         this.focus();
9837         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9838     },
9839
9840     /**
9841      * Brings this dialog to the front of any other visible dialogs
9842      * @return {Roo.BasicDialog} this
9843      */
9844     toFront : function(){
9845         Roo.DialogManager.bringToFront(this);
9846         return this;
9847     },
9848
9849     /**
9850      * Sends this dialog to the back (under) of any other visible dialogs
9851      * @return {Roo.BasicDialog} this
9852      */
9853     toBack : function(){
9854         Roo.DialogManager.sendToBack(this);
9855         return this;
9856     },
9857
9858     /**
9859      * Centers this dialog in the viewport
9860      * @return {Roo.BasicDialog} this
9861      */
9862     center : function(){
9863         var xy = this.el.getCenterXY(true);
9864         this.moveTo(xy[0], xy[1]);
9865         return this;
9866     },
9867
9868     /**
9869      * Moves the dialog's top-left corner to the specified point
9870      * @param {Number} x
9871      * @param {Number} y
9872      * @return {Roo.BasicDialog} this
9873      */
9874     moveTo : function(x, y){
9875         this.xy = [x,y];
9876         if(this.isVisible()){
9877             this.el.setXY(this.xy);
9878             this.adjustAssets();
9879         }
9880         return this;
9881     },
9882
9883     /**
9884      * Aligns the dialog to the specified element
9885      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9886      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9887      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9888      * @return {Roo.BasicDialog} this
9889      */
9890     alignTo : function(element, position, offsets){
9891         this.xy = this.el.getAlignToXY(element, position, offsets);
9892         if(this.isVisible()){
9893             this.el.setXY(this.xy);
9894             this.adjustAssets();
9895         }
9896         return this;
9897     },
9898
9899     /**
9900      * Anchors an element to another element and realigns it when the window is resized.
9901      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9902      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9903      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9904      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9905      * is a number, it is used as the buffer delay (defaults to 50ms).
9906      * @return {Roo.BasicDialog} this
9907      */
9908     anchorTo : function(el, alignment, offsets, monitorScroll){
9909         var action = function(){
9910             this.alignTo(el, alignment, offsets);
9911         };
9912         Roo.EventManager.onWindowResize(action, this);
9913         var tm = typeof monitorScroll;
9914         if(tm != 'undefined'){
9915             Roo.EventManager.on(window, 'scroll', action, this,
9916                 {buffer: tm == 'number' ? monitorScroll : 50});
9917         }
9918         action.call(this);
9919         return this;
9920     },
9921
9922     /**
9923      * Returns true if the dialog is visible
9924      * @return {Boolean}
9925      */
9926     isVisible : function(){
9927         return this.el.isVisible();
9928     },
9929
9930     // private
9931     animHide : function(callback){
9932         var b = Roo.get(this.animateTarget).getBox();
9933         this.proxy.show();
9934         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9935         this.el.hide();
9936         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9937                     this.hideEl.createDelegate(this, [callback]));
9938     },
9939
9940     /**
9941      * Hides the dialog.
9942      * @param {Function} callback (optional) Function to call when the dialog is hidden
9943      * @return {Roo.BasicDialog} this
9944      */
9945     hide : function(callback){
9946         if (this.fireEvent("beforehide", this) === false){
9947             return;
9948         }
9949         if(this.shadow){
9950             this.shadow.hide();
9951         }
9952         if(this.shim) {
9953           this.shim.hide();
9954         }
9955         // sometimes animateTarget seems to get set.. causing problems...
9956         // this just double checks..
9957         if(this.animateTarget && Roo.get(this.animateTarget)) {
9958            this.animHide(callback);
9959         }else{
9960             this.el.hide();
9961             this.hideEl(callback);
9962         }
9963         return this;
9964     },
9965
9966     // private
9967     hideEl : function(callback){
9968         this.proxy.hide();
9969         if(this.modal){
9970             this.mask.hide();
9971             Roo.get(document.body).removeClass("x-body-masked");
9972         }
9973         this.fireEvent("hide", this);
9974         if(typeof callback == "function"){
9975             callback();
9976         }
9977     },
9978
9979     // private
9980     hideAction : function(){
9981         this.setLeft("-10000px");
9982         this.setTop("-10000px");
9983         this.setStyle("visibility", "hidden");
9984     },
9985
9986     // private
9987     refreshSize : function(){
9988         this.size = this.el.getSize();
9989         this.xy = this.el.getXY();
9990         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9991     },
9992
9993     // private
9994     // z-index is managed by the DialogManager and may be overwritten at any time
9995     setZIndex : function(index){
9996         if(this.modal){
9997             this.mask.setStyle("z-index", index);
9998         }
9999         if(this.shim){
10000             this.shim.setStyle("z-index", ++index);
10001         }
10002         if(this.shadow){
10003             this.shadow.setZIndex(++index);
10004         }
10005         this.el.setStyle("z-index", ++index);
10006         if(this.proxy){
10007             this.proxy.setStyle("z-index", ++index);
10008         }
10009         if(this.resizer){
10010             this.resizer.proxy.setStyle("z-index", ++index);
10011         }
10012
10013         this.lastZIndex = index;
10014     },
10015
10016     /**
10017      * Returns the element for this dialog
10018      * @return {Roo.Element} The underlying dialog Element
10019      */
10020     getEl : function(){
10021         return this.el;
10022     }
10023 });
10024
10025 /**
10026  * @class Roo.DialogManager
10027  * Provides global access to BasicDialogs that have been created and
10028  * support for z-indexing (layering) multiple open dialogs.
10029  */
10030 Roo.DialogManager = function(){
10031     var list = {};
10032     var accessList = [];
10033     var front = null;
10034
10035     // private
10036     var sortDialogs = function(d1, d2){
10037         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10038     };
10039
10040     // private
10041     var orderDialogs = function(){
10042         accessList.sort(sortDialogs);
10043         var seed = Roo.DialogManager.zseed;
10044         for(var i = 0, len = accessList.length; i < len; i++){
10045             var dlg = accessList[i];
10046             if(dlg){
10047                 dlg.setZIndex(seed + (i*10));
10048             }
10049         }
10050     };
10051
10052     return {
10053         /**
10054          * The starting z-index for BasicDialogs (defaults to 9000)
10055          * @type Number The z-index value
10056          */
10057         zseed : 9000,
10058
10059         // private
10060         register : function(dlg){
10061             list[dlg.id] = dlg;
10062             accessList.push(dlg);
10063         },
10064
10065         // private
10066         unregister : function(dlg){
10067             delete list[dlg.id];
10068             var i=0;
10069             var len=0;
10070             if(!accessList.indexOf){
10071                 for(  i = 0, len = accessList.length; i < len; i++){
10072                     if(accessList[i] == dlg){
10073                         accessList.splice(i, 1);
10074                         return;
10075                     }
10076                 }
10077             }else{
10078                  i = accessList.indexOf(dlg);
10079                 if(i != -1){
10080                     accessList.splice(i, 1);
10081                 }
10082             }
10083         },
10084
10085         /**
10086          * Gets a registered dialog by id
10087          * @param {String/Object} id The id of the dialog or a dialog
10088          * @return {Roo.BasicDialog} this
10089          */
10090         get : function(id){
10091             return typeof id == "object" ? id : list[id];
10092         },
10093
10094         /**
10095          * Brings the specified dialog to the front
10096          * @param {String/Object} dlg The id of the dialog or a dialog
10097          * @return {Roo.BasicDialog} this
10098          */
10099         bringToFront : function(dlg){
10100             dlg = this.get(dlg);
10101             if(dlg != front){
10102                 front = dlg;
10103                 dlg._lastAccess = new Date().getTime();
10104                 orderDialogs();
10105             }
10106             return dlg;
10107         },
10108
10109         /**
10110          * Sends the specified dialog to the back
10111          * @param {String/Object} dlg The id of the dialog or a dialog
10112          * @return {Roo.BasicDialog} this
10113          */
10114         sendToBack : function(dlg){
10115             dlg = this.get(dlg);
10116             dlg._lastAccess = -(new Date().getTime());
10117             orderDialogs();
10118             return dlg;
10119         },
10120
10121         /**
10122          * Hides all dialogs
10123          */
10124         hideAll : function(){
10125             for(var id in list){
10126                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10127                     list[id].hide();
10128                 }
10129             }
10130         }
10131     };
10132 }();
10133
10134 /**
10135  * @class Roo.LayoutDialog
10136  * @extends Roo.BasicDialog
10137  * @children Roo.ContentPanel
10138  * @parent builder none
10139  * Dialog which provides adjustments for working with a layout in a Dialog.
10140  * Add your necessary layout config options to the dialog's config.<br>
10141  * Example usage (including a nested layout):
10142  * <pre><code>
10143 if(!dialog){
10144     dialog = new Roo.LayoutDialog("download-dlg", {
10145         modal: true,
10146         width:600,
10147         height:450,
10148         shadow:true,
10149         minWidth:500,
10150         minHeight:350,
10151         autoTabs:true,
10152         proxyDrag:true,
10153         // layout config merges with the dialog config
10154         center:{
10155             tabPosition: "top",
10156             alwaysShowTabs: true
10157         }
10158     });
10159     dialog.addKeyListener(27, dialog.hide, dialog);
10160     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10161     dialog.addButton("Build It!", this.getDownload, this);
10162
10163     // we can even add nested layouts
10164     var innerLayout = new Roo.BorderLayout("dl-inner", {
10165         east: {
10166             initialSize: 200,
10167             autoScroll:true,
10168             split:true
10169         },
10170         center: {
10171             autoScroll:true
10172         }
10173     });
10174     innerLayout.beginUpdate();
10175     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10176     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10177     innerLayout.endUpdate(true);
10178
10179     var layout = dialog.getLayout();
10180     layout.beginUpdate();
10181     layout.add("center", new Roo.ContentPanel("standard-panel",
10182                         {title: "Download the Source", fitToFrame:true}));
10183     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10184                {title: "Build your own roo.js"}));
10185     layout.getRegion("center").showPanel(sp);
10186     layout.endUpdate();
10187 }
10188 </code></pre>
10189     * @constructor
10190     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10191     * @param {Object} config configuration options
10192   */
10193 Roo.LayoutDialog = function(el, cfg){
10194     
10195     var config=  cfg;
10196     if (typeof(cfg) == 'undefined') {
10197         config = Roo.apply({}, el);
10198         // not sure why we use documentElement here.. - it should always be body.
10199         // IE7 borks horribly if we use documentElement.
10200         // webkit also does not like documentElement - it creates a body element...
10201         el = Roo.get( document.body || document.documentElement ).createChild();
10202         //config.autoCreate = true;
10203     }
10204     
10205     
10206     config.autoTabs = false;
10207     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10208     this.body.setStyle({overflow:"hidden", position:"relative"});
10209     this.layout = new Roo.BorderLayout(this.body.dom, config);
10210     this.layout.monitorWindowResize = false;
10211     this.el.addClass("x-dlg-auto-layout");
10212     // fix case when center region overwrites center function
10213     this.center = Roo.BasicDialog.prototype.center;
10214     this.on("show", this.layout.layout, this.layout, true);
10215     if (config.items) {
10216         var xitems = config.items;
10217         delete config.items;
10218         Roo.each(xitems, this.addxtype, this);
10219     }
10220     
10221     
10222 };
10223 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10224     
10225     
10226     /**
10227      * @cfg {Roo.LayoutRegion} east  
10228      */
10229     /**
10230      * @cfg {Roo.LayoutRegion} west
10231      */
10232     /**
10233      * @cfg {Roo.LayoutRegion} south
10234      */
10235     /**
10236      * @cfg {Roo.LayoutRegion} north
10237      */
10238     /**
10239      * @cfg {Roo.LayoutRegion} center
10240      */
10241     /**
10242      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10243      */
10244     
10245     
10246     /**
10247      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10248      * @deprecated
10249      */
10250     endUpdate : function(){
10251         this.layout.endUpdate();
10252     },
10253
10254     /**
10255      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10256      *  @deprecated
10257      */
10258     beginUpdate : function(){
10259         this.layout.beginUpdate();
10260     },
10261
10262     /**
10263      * Get the BorderLayout for this dialog
10264      * @return {Roo.BorderLayout}
10265      */
10266     getLayout : function(){
10267         return this.layout;
10268     },
10269
10270     showEl : function(){
10271         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10272         if(Roo.isIE7){
10273             this.layout.layout();
10274         }
10275     },
10276
10277     // private
10278     // Use the syncHeightBeforeShow config option to control this automatically
10279     syncBodyHeight : function(){
10280         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10281         if(this.layout){this.layout.layout();}
10282     },
10283     
10284       /**
10285      * Add an xtype element (actually adds to the layout.)
10286      * @return {Object} xdata xtype object data.
10287      */
10288     
10289     addxtype : function(c) {
10290         return this.layout.addxtype(c);
10291     }
10292 });/*
10293  * Based on:
10294  * Ext JS Library 1.1.1
10295  * Copyright(c) 2006-2007, Ext JS, LLC.
10296  *
10297  * Originally Released Under LGPL - original licence link has changed is not relivant.
10298  *
10299  * Fork - LGPL
10300  * <script type="text/javascript">
10301  */
10302  
10303 /**
10304  * @class Roo.MessageBox
10305  * @static
10306  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10307  * Example usage:
10308  *<pre><code>
10309 // Basic alert:
10310 Roo.Msg.alert('Status', 'Changes saved successfully.');
10311
10312 // Prompt for user data:
10313 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10314     if (btn == 'ok'){
10315         // process text value...
10316     }
10317 });
10318
10319 // Show a dialog using config options:
10320 Roo.Msg.show({
10321    title:'Save Changes?',
10322    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10323    buttons: Roo.Msg.YESNOCANCEL,
10324    fn: processResult,
10325    animEl: 'elId'
10326 });
10327 </code></pre>
10328  * @static
10329  */
10330 Roo.MessageBox = function(){
10331     var dlg, opt, mask, waitTimer;
10332     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10333     var buttons, activeTextEl, bwidth;
10334
10335     // private
10336     var handleButton = function(button){
10337         dlg.hide();
10338         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10339     };
10340
10341     // private
10342     var handleHide = function(){
10343         if(opt && opt.cls){
10344             dlg.el.removeClass(opt.cls);
10345         }
10346         if(waitTimer){
10347             Roo.TaskMgr.stop(waitTimer);
10348             waitTimer = null;
10349         }
10350     };
10351
10352     // private
10353     var updateButtons = function(b){
10354         var width = 0;
10355         if(!b){
10356             buttons["ok"].hide();
10357             buttons["cancel"].hide();
10358             buttons["yes"].hide();
10359             buttons["no"].hide();
10360             dlg.footer.dom.style.display = 'none';
10361             return width;
10362         }
10363         dlg.footer.dom.style.display = '';
10364         for(var k in buttons){
10365             if(typeof buttons[k] != "function"){
10366                 if(b[k]){
10367                     buttons[k].show();
10368                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10369                     width += buttons[k].el.getWidth()+15;
10370                 }else{
10371                     buttons[k].hide();
10372                 }
10373             }
10374         }
10375         return width;
10376     };
10377
10378     // private
10379     var handleEsc = function(d, k, e){
10380         if(opt && opt.closable !== false){
10381             dlg.hide();
10382         }
10383         if(e){
10384             e.stopEvent();
10385         }
10386     };
10387
10388     return {
10389         /**
10390          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10391          * @return {Roo.BasicDialog} The BasicDialog element
10392          */
10393         getDialog : function(){
10394            if(!dlg){
10395                 dlg = new Roo.BasicDialog("x-msg-box", {
10396                     autoCreate : true,
10397                     shadow: true,
10398                     draggable: true,
10399                     resizable:false,
10400                     constraintoviewport:false,
10401                     fixedcenter:true,
10402                     collapsible : false,
10403                     shim:true,
10404                     modal: true,
10405                     width:400, height:100,
10406                     buttonAlign:"center",
10407                     closeClick : function(){
10408                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10409                             handleButton("no");
10410                         }else{
10411                             handleButton("cancel");
10412                         }
10413                     }
10414                 });
10415               
10416                 dlg.on("hide", handleHide);
10417                 mask = dlg.mask;
10418                 dlg.addKeyListener(27, handleEsc);
10419                 buttons = {};
10420                 var bt = this.buttonText;
10421                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10422                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10423                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10424                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10425                 bodyEl = dlg.body.createChild({
10426
10427                     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>'
10428                 });
10429                 msgEl = bodyEl.dom.firstChild;
10430                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10431                 textboxEl.enableDisplayMode();
10432                 textboxEl.addKeyListener([10,13], function(){
10433                     if(dlg.isVisible() && opt && opt.buttons){
10434                         if(opt.buttons.ok){
10435                             handleButton("ok");
10436                         }else if(opt.buttons.yes){
10437                             handleButton("yes");
10438                         }
10439                     }
10440                 });
10441                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10442                 textareaEl.enableDisplayMode();
10443                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10444                 progressEl.enableDisplayMode();
10445                 var pf = progressEl.dom.firstChild;
10446                 if (pf) {
10447                     pp = Roo.get(pf.firstChild);
10448                     pp.setHeight(pf.offsetHeight);
10449                 }
10450                 
10451             }
10452             return dlg;
10453         },
10454
10455         /**
10456          * Updates the message box body text
10457          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10458          * the XHTML-compliant non-breaking space character '&amp;#160;')
10459          * @return {Roo.MessageBox} This message box
10460          */
10461         updateText : function(text){
10462             if(!dlg.isVisible() && !opt.width){
10463                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10464             }
10465             msgEl.innerHTML = text || '&#160;';
10466       
10467             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10468             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10469             var w = Math.max(
10470                     Math.min(opt.width || cw , this.maxWidth), 
10471                     Math.max(opt.minWidth || this.minWidth, bwidth)
10472             );
10473             if(opt.prompt){
10474                 activeTextEl.setWidth(w);
10475             }
10476             if(dlg.isVisible()){
10477                 dlg.fixedcenter = false;
10478             }
10479             // to big, make it scroll. = But as usual stupid IE does not support
10480             // !important..
10481             
10482             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10483                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10484                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10485             } else {
10486                 bodyEl.dom.style.height = '';
10487                 bodyEl.dom.style.overflowY = '';
10488             }
10489             if (cw > w) {
10490                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10491             } else {
10492                 bodyEl.dom.style.overflowX = '';
10493             }
10494             
10495             dlg.setContentSize(w, bodyEl.getHeight());
10496             if(dlg.isVisible()){
10497                 dlg.fixedcenter = true;
10498             }
10499             return this;
10500         },
10501
10502         /**
10503          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10504          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10505          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10506          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10507          * @return {Roo.MessageBox} This message box
10508          */
10509         updateProgress : function(value, text){
10510             if(text){
10511                 this.updateText(text);
10512             }
10513             if (pp) { // weird bug on my firefox - for some reason this is not defined
10514                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10515             }
10516             return this;
10517         },        
10518
10519         /**
10520          * Returns true if the message box is currently displayed
10521          * @return {Boolean} True if the message box is visible, else false
10522          */
10523         isVisible : function(){
10524             return dlg && dlg.isVisible();  
10525         },
10526
10527         /**
10528          * Hides the message box if it is displayed
10529          */
10530         hide : function(){
10531             if(this.isVisible()){
10532                 dlg.hide();
10533             }  
10534         },
10535
10536         /**
10537          * Displays a new message box, or reinitializes an existing message box, based on the config options
10538          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10539          * The following config object properties are supported:
10540          * <pre>
10541 Property    Type             Description
10542 ----------  ---------------  ------------------------------------------------------------------------------------
10543 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10544                                    closes (defaults to undefined)
10545 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10546                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10547 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10548                                    progress and wait dialogs will ignore this property and always hide the
10549                                    close button as they can only be closed programmatically.
10550 cls               String           A custom CSS class to apply to the message box element
10551 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10552                                    displayed (defaults to 75)
10553 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10554                                    function will be btn (the name of the button that was clicked, if applicable,
10555                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10556                                    Progress and wait dialogs will ignore this option since they do not respond to
10557                                    user actions and can only be closed programmatically, so any required function
10558                                    should be called by the same code after it closes the dialog.
10559 icon              String           A CSS class that provides a background image to be used as an icon for
10560                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10561 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10562 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10563 modal             Boolean          False to allow user interaction with the page while the message box is
10564                                    displayed (defaults to true)
10565 msg               String           A string that will replace the existing message box body text (defaults
10566                                    to the XHTML-compliant non-breaking space character '&#160;')
10567 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10568 progress          Boolean          True to display a progress bar (defaults to false)
10569 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10570 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10571 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10572 title             String           The title text
10573 value             String           The string value to set into the active textbox element if displayed
10574 wait              Boolean          True to display a progress bar (defaults to false)
10575 width             Number           The width of the dialog in pixels
10576 </pre>
10577          *
10578          * Example usage:
10579          * <pre><code>
10580 Roo.Msg.show({
10581    title: 'Address',
10582    msg: 'Please enter your address:',
10583    width: 300,
10584    buttons: Roo.MessageBox.OKCANCEL,
10585    multiline: true,
10586    fn: saveAddress,
10587    animEl: 'addAddressBtn'
10588 });
10589 </code></pre>
10590          * @param {Object} config Configuration options
10591          * @return {Roo.MessageBox} This message box
10592          */
10593         show : function(options)
10594         {
10595             
10596             // this causes nightmares if you show one dialog after another
10597             // especially on callbacks..
10598              
10599             if(this.isVisible()){
10600                 
10601                 this.hide();
10602                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10603                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10604                 Roo.log("New Dialog Message:" +  options.msg )
10605                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10606                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10607                 
10608             }
10609             var d = this.getDialog();
10610             opt = options;
10611             d.setTitle(opt.title || "&#160;");
10612             d.close.setDisplayed(opt.closable !== false);
10613             activeTextEl = textboxEl;
10614             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10615             if(opt.prompt){
10616                 if(opt.multiline){
10617                     textboxEl.hide();
10618                     textareaEl.show();
10619                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10620                         opt.multiline : this.defaultTextHeight);
10621                     activeTextEl = textareaEl;
10622                 }else{
10623                     textboxEl.show();
10624                     textareaEl.hide();
10625                 }
10626             }else{
10627                 textboxEl.hide();
10628                 textareaEl.hide();
10629             }
10630             progressEl.setDisplayed(opt.progress === true);
10631             this.updateProgress(0);
10632             activeTextEl.dom.value = opt.value || "";
10633             if(opt.prompt){
10634                 dlg.setDefaultButton(activeTextEl);
10635             }else{
10636                 var bs = opt.buttons;
10637                 var db = null;
10638                 if(bs && bs.ok){
10639                     db = buttons["ok"];
10640                 }else if(bs && bs.yes){
10641                     db = buttons["yes"];
10642                 }
10643                 dlg.setDefaultButton(db);
10644             }
10645             bwidth = updateButtons(opt.buttons);
10646             this.updateText(opt.msg);
10647             if(opt.cls){
10648                 d.el.addClass(opt.cls);
10649             }
10650             d.proxyDrag = opt.proxyDrag === true;
10651             d.modal = opt.modal !== false;
10652             d.mask = opt.modal !== false ? mask : false;
10653             if(!d.isVisible()){
10654                 // force it to the end of the z-index stack so it gets a cursor in FF
10655                 document.body.appendChild(dlg.el.dom);
10656                 d.animateTarget = null;
10657                 d.show(options.animEl);
10658             }
10659             dlg.toFront();
10660             return this;
10661         },
10662
10663         /**
10664          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10665          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10666          * and closing the message box when the process is complete.
10667          * @param {String} title The title bar text
10668          * @param {String} msg The message box body text
10669          * @return {Roo.MessageBox} This message box
10670          */
10671         progress : function(title, msg){
10672             this.show({
10673                 title : title,
10674                 msg : msg,
10675                 buttons: false,
10676                 progress:true,
10677                 closable:false,
10678                 minWidth: this.minProgressWidth,
10679                 modal : true
10680             });
10681             return this;
10682         },
10683
10684         /**
10685          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10686          * If a callback function is passed it will be called after the user clicks the button, and the
10687          * id of the button that was clicked will be passed as the only parameter to the callback
10688          * (could also be the top-right close button).
10689          * @param {String} title The title bar text
10690          * @param {String} msg The message box body text
10691          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10692          * @param {Object} scope (optional) The scope of the callback function
10693          * @return {Roo.MessageBox} This message box
10694          */
10695         alert : function(title, msg, fn, scope){
10696             this.show({
10697                 title : title,
10698                 msg : msg,
10699                 buttons: this.OK,
10700                 fn: fn,
10701                 scope : scope,
10702                 modal : true
10703             });
10704             return this;
10705         },
10706
10707         /**
10708          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10709          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10710          * You are responsible for closing the message box when the process is complete.
10711          * @param {String} msg The message box body text
10712          * @param {String} title (optional) The title bar text
10713          * @return {Roo.MessageBox} This message box
10714          */
10715         wait : function(msg, title){
10716             this.show({
10717                 title : title,
10718                 msg : msg,
10719                 buttons: false,
10720                 closable:false,
10721                 progress:true,
10722                 modal:true,
10723                 width:300,
10724                 wait:true
10725             });
10726             waitTimer = Roo.TaskMgr.start({
10727                 run: function(i){
10728                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10729                 },
10730                 interval: 1000
10731             });
10732             return this;
10733         },
10734
10735         /**
10736          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10737          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10738          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10739          * @param {String} title The title bar text
10740          * @param {String} msg The message box body text
10741          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10742          * @param {Object} scope (optional) The scope of the callback function
10743          * @return {Roo.MessageBox} This message box
10744          */
10745         confirm : function(title, msg, fn, scope){
10746             this.show({
10747                 title : title,
10748                 msg : msg,
10749                 buttons: this.YESNO,
10750                 fn: fn,
10751                 scope : scope,
10752                 modal : true
10753             });
10754             return this;
10755         },
10756
10757         /**
10758          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10759          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10760          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10761          * (could also be the top-right close button) and the text that was entered will be passed as the two
10762          * parameters to the callback.
10763          * @param {String} title The title bar text
10764          * @param {String} msg The message box body text
10765          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10766          * @param {Object} scope (optional) The scope of the callback function
10767          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10768          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10769          * @return {Roo.MessageBox} This message box
10770          */
10771         prompt : function(title, msg, fn, scope, multiline){
10772             this.show({
10773                 title : title,
10774                 msg : msg,
10775                 buttons: this.OKCANCEL,
10776                 fn: fn,
10777                 minWidth:250,
10778                 scope : scope,
10779                 prompt:true,
10780                 multiline: multiline,
10781                 modal : true
10782             });
10783             return this;
10784         },
10785
10786         /**
10787          * Button config that displays a single OK button
10788          * @type Object
10789          */
10790         OK : {ok:true},
10791         /**
10792          * Button config that displays Yes and No buttons
10793          * @type Object
10794          */
10795         YESNO : {yes:true, no:true},
10796         /**
10797          * Button config that displays OK and Cancel buttons
10798          * @type Object
10799          */
10800         OKCANCEL : {ok:true, cancel:true},
10801         /**
10802          * Button config that displays Yes, No and Cancel buttons
10803          * @type Object
10804          */
10805         YESNOCANCEL : {yes:true, no:true, cancel:true},
10806
10807         /**
10808          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10809          * @type Number
10810          */
10811         defaultTextHeight : 75,
10812         /**
10813          * The maximum width in pixels of the message box (defaults to 600)
10814          * @type Number
10815          */
10816         maxWidth : 600,
10817         /**
10818          * The minimum width in pixels of the message box (defaults to 100)
10819          * @type Number
10820          */
10821         minWidth : 100,
10822         /**
10823          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10824          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10825          * @type Number
10826          */
10827         minProgressWidth : 250,
10828         /**
10829          * An object containing the default button text strings that can be overriden for localized language support.
10830          * Supported properties are: ok, cancel, yes and no.
10831          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10832          * @type Object
10833          */
10834         buttonText : {
10835             ok : "OK",
10836             cancel : "Cancel",
10837             yes : "Yes",
10838             no : "No"
10839         }
10840     };
10841 }();
10842
10843 /**
10844  * Shorthand for {@link Roo.MessageBox}
10845  */
10846 Roo.Msg = Roo.MessageBox;/*
10847  * Based on:
10848  * Ext JS Library 1.1.1
10849  * Copyright(c) 2006-2007, Ext JS, LLC.
10850  *
10851  * Originally Released Under LGPL - original licence link has changed is not relivant.
10852  *
10853  * Fork - LGPL
10854  * <script type="text/javascript">
10855  */
10856 /**
10857  * @class Roo.QuickTips
10858  * Provides attractive and customizable tooltips for any element.
10859  * @static
10860  */
10861 Roo.QuickTips = function(){
10862     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10863     var ce, bd, xy, dd;
10864     var visible = false, disabled = true, inited = false;
10865     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10866     
10867     var onOver = function(e){
10868         if(disabled){
10869             return;
10870         }
10871         var t = e.getTarget();
10872         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10873             return;
10874         }
10875         if(ce && t == ce.el){
10876             clearTimeout(hideProc);
10877             return;
10878         }
10879         if(t && tagEls[t.id]){
10880             tagEls[t.id].el = t;
10881             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10882             return;
10883         }
10884         var ttp, et = Roo.fly(t);
10885         var ns = cfg.namespace;
10886         if(tm.interceptTitles && t.title){
10887             ttp = t.title;
10888             t.qtip = ttp;
10889             t.removeAttribute("title");
10890             e.preventDefault();
10891         }else{
10892             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10893         }
10894         if(ttp){
10895             showProc = show.defer(tm.showDelay, tm, [{
10896                 el: t, 
10897                 text: ttp.replace(/\\n/g,'<br/>'),
10898                 width: et.getAttributeNS(ns, cfg.width),
10899                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10900                 title: et.getAttributeNS(ns, cfg.title),
10901                     cls: et.getAttributeNS(ns, cfg.cls)
10902             }]);
10903         }
10904     };
10905     
10906     var onOut = function(e){
10907         clearTimeout(showProc);
10908         var t = e.getTarget();
10909         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10910             hideProc = setTimeout(hide, tm.hideDelay);
10911         }
10912     };
10913     
10914     var onMove = function(e){
10915         if(disabled){
10916             return;
10917         }
10918         xy = e.getXY();
10919         xy[1] += 18;
10920         if(tm.trackMouse && ce){
10921             el.setXY(xy);
10922         }
10923     };
10924     
10925     var onDown = function(e){
10926         clearTimeout(showProc);
10927         clearTimeout(hideProc);
10928         if(!e.within(el)){
10929             if(tm.hideOnClick){
10930                 hide();
10931                 tm.disable();
10932                 tm.enable.defer(100, tm);
10933             }
10934         }
10935     };
10936     
10937     var getPad = function(){
10938         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10939     };
10940
10941     var show = function(o){
10942         if(disabled){
10943             return;
10944         }
10945         clearTimeout(dismissProc);
10946         ce = o;
10947         if(removeCls){ // in case manually hidden
10948             el.removeClass(removeCls);
10949             removeCls = null;
10950         }
10951         if(ce.cls){
10952             el.addClass(ce.cls);
10953             removeCls = ce.cls;
10954         }
10955         if(ce.title){
10956             tipTitle.update(ce.title);
10957             tipTitle.show();
10958         }else{
10959             tipTitle.update('');
10960             tipTitle.hide();
10961         }
10962         el.dom.style.width  = tm.maxWidth+'px';
10963         //tipBody.dom.style.width = '';
10964         tipBodyText.update(o.text);
10965         var p = getPad(), w = ce.width;
10966         if(!w){
10967             var td = tipBodyText.dom;
10968             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10969             if(aw > tm.maxWidth){
10970                 w = tm.maxWidth;
10971             }else if(aw < tm.minWidth){
10972                 w = tm.minWidth;
10973             }else{
10974                 w = aw;
10975             }
10976         }
10977         //tipBody.setWidth(w);
10978         el.setWidth(parseInt(w, 10) + p);
10979         if(ce.autoHide === false){
10980             close.setDisplayed(true);
10981             if(dd){
10982                 dd.unlock();
10983             }
10984         }else{
10985             close.setDisplayed(false);
10986             if(dd){
10987                 dd.lock();
10988             }
10989         }
10990         if(xy){
10991             el.avoidY = xy[1]-18;
10992             el.setXY(xy);
10993         }
10994         if(tm.animate){
10995             el.setOpacity(.1);
10996             el.setStyle("visibility", "visible");
10997             el.fadeIn({callback: afterShow});
10998         }else{
10999             afterShow();
11000         }
11001     };
11002     
11003     var afterShow = function(){
11004         if(ce){
11005             el.show();
11006             esc.enable();
11007             if(tm.autoDismiss && ce.autoHide !== false){
11008                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11009             }
11010         }
11011     };
11012     
11013     var hide = function(noanim){
11014         clearTimeout(dismissProc);
11015         clearTimeout(hideProc);
11016         ce = null;
11017         if(el.isVisible()){
11018             esc.disable();
11019             if(noanim !== true && tm.animate){
11020                 el.fadeOut({callback: afterHide});
11021             }else{
11022                 afterHide();
11023             } 
11024         }
11025     };
11026     
11027     var afterHide = function(){
11028         el.hide();
11029         if(removeCls){
11030             el.removeClass(removeCls);
11031             removeCls = null;
11032         }
11033     };
11034     
11035     return {
11036         /**
11037         * @cfg {Number} minWidth
11038         * The minimum width of the quick tip (defaults to 40)
11039         */
11040        minWidth : 40,
11041         /**
11042         * @cfg {Number} maxWidth
11043         * The maximum width of the quick tip (defaults to 300)
11044         */
11045        maxWidth : 300,
11046         /**
11047         * @cfg {Boolean} interceptTitles
11048         * True to automatically use the element's DOM title value if available (defaults to false)
11049         */
11050        interceptTitles : false,
11051         /**
11052         * @cfg {Boolean} trackMouse
11053         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11054         */
11055        trackMouse : false,
11056         /**
11057         * @cfg {Boolean} hideOnClick
11058         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11059         */
11060        hideOnClick : true,
11061         /**
11062         * @cfg {Number} showDelay
11063         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11064         */
11065        showDelay : 500,
11066         /**
11067         * @cfg {Number} hideDelay
11068         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11069         */
11070        hideDelay : 200,
11071         /**
11072         * @cfg {Boolean} autoHide
11073         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11074         * Used in conjunction with hideDelay.
11075         */
11076        autoHide : true,
11077         /**
11078         * @cfg {Boolean}
11079         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11080         * (defaults to true).  Used in conjunction with autoDismissDelay.
11081         */
11082        autoDismiss : true,
11083         /**
11084         * @cfg {Number}
11085         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11086         */
11087        autoDismissDelay : 5000,
11088        /**
11089         * @cfg {Boolean} animate
11090         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11091         */
11092        animate : false,
11093
11094        /**
11095         * @cfg {String} title
11096         * Title text to display (defaults to '').  This can be any valid HTML markup.
11097         */
11098         title: '',
11099        /**
11100         * @cfg {String} text
11101         * Body text to display (defaults to '').  This can be any valid HTML markup.
11102         */
11103         text : '',
11104        /**
11105         * @cfg {String} cls
11106         * A CSS class to apply to the base quick tip element (defaults to '').
11107         */
11108         cls : '',
11109        /**
11110         * @cfg {Number} width
11111         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11112         * minWidth or maxWidth.
11113         */
11114         width : null,
11115
11116     /**
11117      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11118      * or display QuickTips in a page.
11119      */
11120        init : function(){
11121           tm = Roo.QuickTips;
11122           cfg = tm.tagConfig;
11123           if(!inited){
11124               if(!Roo.isReady){ // allow calling of init() before onReady
11125                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11126                   return;
11127               }
11128               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11129               el.fxDefaults = {stopFx: true};
11130               // maximum custom styling
11131               //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>');
11132               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>');              
11133               tipTitle = el.child('h3');
11134               tipTitle.enableDisplayMode("block");
11135               tipBody = el.child('div.x-tip-bd');
11136               tipBodyText = el.child('div.x-tip-bd-inner');
11137               //bdLeft = el.child('div.x-tip-bd-left');
11138               //bdRight = el.child('div.x-tip-bd-right');
11139               close = el.child('div.x-tip-close');
11140               close.enableDisplayMode("block");
11141               close.on("click", hide);
11142               var d = Roo.get(document);
11143               d.on("mousedown", onDown);
11144               d.on("mouseover", onOver);
11145               d.on("mouseout", onOut);
11146               d.on("mousemove", onMove);
11147               esc = d.addKeyListener(27, hide);
11148               esc.disable();
11149               if(Roo.dd.DD){
11150                   dd = el.initDD("default", null, {
11151                       onDrag : function(){
11152                           el.sync();  
11153                       }
11154                   });
11155                   dd.setHandleElId(tipTitle.id);
11156                   dd.lock();
11157               }
11158               inited = true;
11159           }
11160           this.enable(); 
11161        },
11162
11163     /**
11164      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11165      * are supported:
11166      * <pre>
11167 Property    Type                   Description
11168 ----------  ---------------------  ------------------------------------------------------------------------
11169 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11170      * </ul>
11171      * @param {Object} config The config object
11172      */
11173        register : function(config){
11174            var cs = config instanceof Array ? config : arguments;
11175            for(var i = 0, len = cs.length; i < len; i++) {
11176                var c = cs[i];
11177                var target = c.target;
11178                if(target){
11179                    if(target instanceof Array){
11180                        for(var j = 0, jlen = target.length; j < jlen; j++){
11181                            tagEls[target[j]] = c;
11182                        }
11183                    }else{
11184                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11185                    }
11186                }
11187            }
11188        },
11189
11190     /**
11191      * Removes this quick tip from its element and destroys it.
11192      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11193      */
11194        unregister : function(el){
11195            delete tagEls[Roo.id(el)];
11196        },
11197
11198     /**
11199      * Enable this quick tip.
11200      */
11201        enable : function(){
11202            if(inited && disabled){
11203                locks.pop();
11204                if(locks.length < 1){
11205                    disabled = false;
11206                }
11207            }
11208        },
11209
11210     /**
11211      * Disable this quick tip.
11212      */
11213        disable : function(){
11214           disabled = true;
11215           clearTimeout(showProc);
11216           clearTimeout(hideProc);
11217           clearTimeout(dismissProc);
11218           if(ce){
11219               hide(true);
11220           }
11221           locks.push(1);
11222        },
11223
11224     /**
11225      * Returns true if the quick tip is enabled, else false.
11226      */
11227        isEnabled : function(){
11228             return !disabled;
11229        },
11230
11231         // private
11232        tagConfig : {
11233            namespace : "roo", // was ext?? this may break..
11234            alt_namespace : "ext",
11235            attribute : "qtip",
11236            width : "width",
11237            target : "target",
11238            title : "qtitle",
11239            hide : "hide",
11240            cls : "qclass"
11241        }
11242    };
11243 }();
11244
11245 // backwards compat
11246 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11247  * Based on:
11248  * Ext JS Library 1.1.1
11249  * Copyright(c) 2006-2007, Ext JS, LLC.
11250  *
11251  * Originally Released Under LGPL - original licence link has changed is not relivant.
11252  *
11253  * Fork - LGPL
11254  * <script type="text/javascript">
11255  */
11256  
11257
11258 /**
11259  * @class Roo.tree.TreePanel
11260  * @extends Roo.data.Tree
11261  * @cfg {Roo.tree.TreeNode} root The root node
11262  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11263  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11264  * @cfg {Boolean} enableDD true to enable drag and drop
11265  * @cfg {Boolean} enableDrag true to enable just drag
11266  * @cfg {Boolean} enableDrop true to enable just drop
11267  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11268  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11269  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11270  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11271  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11272  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11273  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11274  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11275  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11276  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11277  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11278  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11279  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11280  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11281  * @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>
11282  * @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>
11283  * 
11284  * @constructor
11285  * @param {String/HTMLElement/Element} el The container element
11286  * @param {Object} config
11287  */
11288 Roo.tree.TreePanel = function(el, config){
11289     var root = false;
11290     var loader = false;
11291     if (config.root) {
11292         root = config.root;
11293         delete config.root;
11294     }
11295     if (config.loader) {
11296         loader = config.loader;
11297         delete config.loader;
11298     }
11299     
11300     Roo.apply(this, config);
11301     Roo.tree.TreePanel.superclass.constructor.call(this);
11302     this.el = Roo.get(el);
11303     this.el.addClass('x-tree');
11304     //console.log(root);
11305     if (root) {
11306         this.setRootNode( Roo.factory(root, Roo.tree));
11307     }
11308     if (loader) {
11309         this.loader = Roo.factory(loader, Roo.tree);
11310     }
11311    /**
11312     * Read-only. The id of the container element becomes this TreePanel's id.
11313     */
11314     this.id = this.el.id;
11315     this.addEvents({
11316         /**
11317         * @event beforeload
11318         * Fires before a node is loaded, return false to cancel
11319         * @param {Node} node The node being loaded
11320         */
11321         "beforeload" : true,
11322         /**
11323         * @event load
11324         * Fires when a node is loaded
11325         * @param {Node} node The node that was loaded
11326         */
11327         "load" : true,
11328         /**
11329         * @event textchange
11330         * Fires when the text for a node is changed
11331         * @param {Node} node The node
11332         * @param {String} text The new text
11333         * @param {String} oldText The old text
11334         */
11335         "textchange" : true,
11336         /**
11337         * @event beforeexpand
11338         * Fires before a node is expanded, return false to cancel.
11339         * @param {Node} node The node
11340         * @param {Boolean} deep
11341         * @param {Boolean} anim
11342         */
11343         "beforeexpand" : true,
11344         /**
11345         * @event beforecollapse
11346         * Fires before a node is collapsed, return false to cancel.
11347         * @param {Node} node The node
11348         * @param {Boolean} deep
11349         * @param {Boolean} anim
11350         */
11351         "beforecollapse" : true,
11352         /**
11353         * @event expand
11354         * Fires when a node is expanded
11355         * @param {Node} node The node
11356         */
11357         "expand" : true,
11358         /**
11359         * @event disabledchange
11360         * Fires when the disabled status of a node changes
11361         * @param {Node} node The node
11362         * @param {Boolean} disabled
11363         */
11364         "disabledchange" : true,
11365         /**
11366         * @event collapse
11367         * Fires when a node is collapsed
11368         * @param {Node} node The node
11369         */
11370         "collapse" : true,
11371         /**
11372         * @event beforeclick
11373         * Fires before click processing on a node. Return false to cancel the default action.
11374         * @param {Node} node The node
11375         * @param {Roo.EventObject} e The event object
11376         */
11377         "beforeclick":true,
11378         /**
11379         * @event checkchange
11380         * Fires when a node with a checkbox's checked property changes
11381         * @param {Node} this This node
11382         * @param {Boolean} checked
11383         */
11384         "checkchange":true,
11385         /**
11386         * @event click
11387         * Fires when a node is clicked
11388         * @param {Node} node The node
11389         * @param {Roo.EventObject} e The event object
11390         */
11391         "click":true,
11392         /**
11393         * @event dblclick
11394         * Fires when a node is double clicked
11395         * @param {Node} node The node
11396         * @param {Roo.EventObject} e The event object
11397         */
11398         "dblclick":true,
11399         /**
11400         * @event contextmenu
11401         * Fires when a node is right clicked
11402         * @param {Node} node The node
11403         * @param {Roo.EventObject} e The event object
11404         */
11405         "contextmenu":true,
11406         /**
11407         * @event beforechildrenrendered
11408         * Fires right before the child nodes for a node are rendered
11409         * @param {Node} node The node
11410         */
11411         "beforechildrenrendered":true,
11412         /**
11413         * @event startdrag
11414         * Fires when a node starts being dragged
11415         * @param {Roo.tree.TreePanel} this
11416         * @param {Roo.tree.TreeNode} node
11417         * @param {event} e The raw browser event
11418         */ 
11419        "startdrag" : true,
11420        /**
11421         * @event enddrag
11422         * Fires when a drag operation is complete
11423         * @param {Roo.tree.TreePanel} this
11424         * @param {Roo.tree.TreeNode} node
11425         * @param {event} e The raw browser event
11426         */
11427        "enddrag" : true,
11428        /**
11429         * @event dragdrop
11430         * Fires when a dragged node is dropped on a valid DD target
11431         * @param {Roo.tree.TreePanel} this
11432         * @param {Roo.tree.TreeNode} node
11433         * @param {DD} dd The dd it was dropped on
11434         * @param {event} e The raw browser event
11435         */
11436        "dragdrop" : true,
11437        /**
11438         * @event beforenodedrop
11439         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11440         * passed to handlers has the following properties:<br />
11441         * <ul style="padding:5px;padding-left:16px;">
11442         * <li>tree - The TreePanel</li>
11443         * <li>target - The node being targeted for the drop</li>
11444         * <li>data - The drag data from the drag source</li>
11445         * <li>point - The point of the drop - append, above or below</li>
11446         * <li>source - The drag source</li>
11447         * <li>rawEvent - Raw mouse event</li>
11448         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11449         * to be inserted by setting them on this object.</li>
11450         * <li>cancel - Set this to true to cancel the drop.</li>
11451         * </ul>
11452         * @param {Object} dropEvent
11453         */
11454        "beforenodedrop" : true,
11455        /**
11456         * @event nodedrop
11457         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11458         * passed to handlers has the following properties:<br />
11459         * <ul style="padding:5px;padding-left:16px;">
11460         * <li>tree - The TreePanel</li>
11461         * <li>target - The node being targeted for the drop</li>
11462         * <li>data - The drag data from the drag source</li>
11463         * <li>point - The point of the drop - append, above or below</li>
11464         * <li>source - The drag source</li>
11465         * <li>rawEvent - Raw mouse event</li>
11466         * <li>dropNode - Dropped node(s).</li>
11467         * </ul>
11468         * @param {Object} dropEvent
11469         */
11470        "nodedrop" : true,
11471         /**
11472         * @event nodedragover
11473         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11474         * passed to handlers has the following properties:<br />
11475         * <ul style="padding:5px;padding-left:16px;">
11476         * <li>tree - The TreePanel</li>
11477         * <li>target - The node being targeted for the drop</li>
11478         * <li>data - The drag data from the drag source</li>
11479         * <li>point - The point of the drop - append, above or below</li>
11480         * <li>source - The drag source</li>
11481         * <li>rawEvent - Raw mouse event</li>
11482         * <li>dropNode - Drop node(s) provided by the source.</li>
11483         * <li>cancel - Set this to true to signal drop not allowed.</li>
11484         * </ul>
11485         * @param {Object} dragOverEvent
11486         */
11487        "nodedragover" : true,
11488        /**
11489         * @event appendnode
11490         * Fires when append node to the tree
11491         * @param {Roo.tree.TreePanel} this
11492         * @param {Roo.tree.TreeNode} node
11493         * @param {Number} index The index of the newly appended node
11494         */
11495        "appendnode" : true
11496         
11497     });
11498     if(this.singleExpand){
11499        this.on("beforeexpand", this.restrictExpand, this);
11500     }
11501     if (this.editor) {
11502         this.editor.tree = this;
11503         this.editor = Roo.factory(this.editor, Roo.tree);
11504     }
11505     
11506     if (this.selModel) {
11507         this.selModel = Roo.factory(this.selModel, Roo.tree);
11508     }
11509    
11510 };
11511 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11512     rootVisible : true,
11513     animate: Roo.enableFx,
11514     lines : true,
11515     enableDD : false,
11516     hlDrop : Roo.enableFx,
11517   
11518     renderer: false,
11519     
11520     rendererTip: false,
11521     // private
11522     restrictExpand : function(node){
11523         var p = node.parentNode;
11524         if(p){
11525             if(p.expandedChild && p.expandedChild.parentNode == p){
11526                 p.expandedChild.collapse();
11527             }
11528             p.expandedChild = node;
11529         }
11530     },
11531
11532     // private override
11533     setRootNode : function(node){
11534         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11535         if(!this.rootVisible){
11536             node.ui = new Roo.tree.RootTreeNodeUI(node);
11537         }
11538         return node;
11539     },
11540
11541     /**
11542      * Returns the container element for this TreePanel
11543      */
11544     getEl : function(){
11545         return this.el;
11546     },
11547
11548     /**
11549      * Returns the default TreeLoader for this TreePanel
11550      */
11551     getLoader : function(){
11552         return this.loader;
11553     },
11554
11555     /**
11556      * Expand all nodes
11557      */
11558     expandAll : function(){
11559         this.root.expand(true);
11560     },
11561
11562     /**
11563      * Collapse all nodes
11564      */
11565     collapseAll : function(){
11566         this.root.collapse(true);
11567     },
11568
11569     /**
11570      * Returns the selection model used by this TreePanel
11571      */
11572     getSelectionModel : function(){
11573         if(!this.selModel){
11574             this.selModel = new Roo.tree.DefaultSelectionModel();
11575         }
11576         return this.selModel;
11577     },
11578
11579     /**
11580      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11581      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11582      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11583      * @return {Array}
11584      */
11585     getChecked : function(a, startNode){
11586         startNode = startNode || this.root;
11587         var r = [];
11588         var f = function(){
11589             if(this.attributes.checked){
11590                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11591             }
11592         }
11593         startNode.cascade(f);
11594         return r;
11595     },
11596
11597     /**
11598      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11599      * @param {String} path
11600      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11601      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11602      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11603      */
11604     expandPath : function(path, attr, callback){
11605         attr = attr || "id";
11606         var keys = path.split(this.pathSeparator);
11607         var curNode = this.root;
11608         if(curNode.attributes[attr] != keys[1]){ // invalid root
11609             if(callback){
11610                 callback(false, null);
11611             }
11612             return;
11613         }
11614         var index = 1;
11615         var f = function(){
11616             if(++index == keys.length){
11617                 if(callback){
11618                     callback(true, curNode);
11619                 }
11620                 return;
11621             }
11622             var c = curNode.findChild(attr, keys[index]);
11623             if(!c){
11624                 if(callback){
11625                     callback(false, curNode);
11626                 }
11627                 return;
11628             }
11629             curNode = c;
11630             c.expand(false, false, f);
11631         };
11632         curNode.expand(false, false, f);
11633     },
11634
11635     /**
11636      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11637      * @param {String} path
11638      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11639      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11640      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11641      */
11642     selectPath : function(path, attr, callback){
11643         attr = attr || "id";
11644         var keys = path.split(this.pathSeparator);
11645         var v = keys.pop();
11646         if(keys.length > 0){
11647             var f = function(success, node){
11648                 if(success && node){
11649                     var n = node.findChild(attr, v);
11650                     if(n){
11651                         n.select();
11652                         if(callback){
11653                             callback(true, n);
11654                         }
11655                     }else if(callback){
11656                         callback(false, n);
11657                     }
11658                 }else{
11659                     if(callback){
11660                         callback(false, n);
11661                     }
11662                 }
11663             };
11664             this.expandPath(keys.join(this.pathSeparator), attr, f);
11665         }else{
11666             this.root.select();
11667             if(callback){
11668                 callback(true, this.root);
11669             }
11670         }
11671     },
11672
11673     getTreeEl : function(){
11674         return this.el;
11675     },
11676
11677     /**
11678      * Trigger rendering of this TreePanel
11679      */
11680     render : function(){
11681         if (this.innerCt) {
11682             return this; // stop it rendering more than once!!
11683         }
11684         
11685         this.innerCt = this.el.createChild({tag:"ul",
11686                cls:"x-tree-root-ct " +
11687                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11688
11689         if(this.containerScroll){
11690             Roo.dd.ScrollManager.register(this.el);
11691         }
11692         if((this.enableDD || this.enableDrop) && !this.dropZone){
11693            /**
11694             * The dropZone used by this tree if drop is enabled
11695             * @type Roo.tree.TreeDropZone
11696             */
11697              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11698                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11699            });
11700         }
11701         if((this.enableDD || this.enableDrag) && !this.dragZone){
11702            /**
11703             * The dragZone used by this tree if drag is enabled
11704             * @type Roo.tree.TreeDragZone
11705             */
11706             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11707                ddGroup: this.ddGroup || "TreeDD",
11708                scroll: this.ddScroll
11709            });
11710         }
11711         this.getSelectionModel().init(this);
11712         if (!this.root) {
11713             Roo.log("ROOT not set in tree");
11714             return this;
11715         }
11716         this.root.render();
11717         if(!this.rootVisible){
11718             this.root.renderChildren();
11719         }
11720         return this;
11721     }
11722 });/*
11723  * Based on:
11724  * Ext JS Library 1.1.1
11725  * Copyright(c) 2006-2007, Ext JS, LLC.
11726  *
11727  * Originally Released Under LGPL - original licence link has changed is not relivant.
11728  *
11729  * Fork - LGPL
11730  * <script type="text/javascript">
11731  */
11732  
11733
11734 /**
11735  * @class Roo.tree.DefaultSelectionModel
11736  * @extends Roo.util.Observable
11737  * The default single selection for a TreePanel.
11738  * @param {Object} cfg Configuration
11739  */
11740 Roo.tree.DefaultSelectionModel = function(cfg){
11741    this.selNode = null;
11742    
11743    
11744    
11745    this.addEvents({
11746        /**
11747         * @event selectionchange
11748         * Fires when the selected node changes
11749         * @param {DefaultSelectionModel} this
11750         * @param {TreeNode} node the new selection
11751         */
11752        "selectionchange" : true,
11753
11754        /**
11755         * @event beforeselect
11756         * Fires before the selected node changes, return false to cancel the change
11757         * @param {DefaultSelectionModel} this
11758         * @param {TreeNode} node the new selection
11759         * @param {TreeNode} node the old selection
11760         */
11761        "beforeselect" : true
11762    });
11763    
11764     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11765 };
11766
11767 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11768     init : function(tree){
11769         this.tree = tree;
11770         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11771         tree.on("click", this.onNodeClick, this);
11772     },
11773     
11774     onNodeClick : function(node, e){
11775         if (e.ctrlKey && this.selNode == node)  {
11776             this.unselect(node);
11777             return;
11778         }
11779         this.select(node);
11780     },
11781     
11782     /**
11783      * Select a node.
11784      * @param {TreeNode} node The node to select
11785      * @return {TreeNode} The selected node
11786      */
11787     select : function(node){
11788         var last = this.selNode;
11789         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11790             if(last){
11791                 last.ui.onSelectedChange(false);
11792             }
11793             this.selNode = node;
11794             node.ui.onSelectedChange(true);
11795             this.fireEvent("selectionchange", this, node, last);
11796         }
11797         return node;
11798     },
11799     
11800     /**
11801      * Deselect a node.
11802      * @param {TreeNode} node The node to unselect
11803      */
11804     unselect : function(node){
11805         if(this.selNode == node){
11806             this.clearSelections();
11807         }    
11808     },
11809     
11810     /**
11811      * Clear all selections
11812      */
11813     clearSelections : function(){
11814         var n = this.selNode;
11815         if(n){
11816             n.ui.onSelectedChange(false);
11817             this.selNode = null;
11818             this.fireEvent("selectionchange", this, null);
11819         }
11820         return n;
11821     },
11822     
11823     /**
11824      * Get the selected node
11825      * @return {TreeNode} The selected node
11826      */
11827     getSelectedNode : function(){
11828         return this.selNode;    
11829     },
11830     
11831     /**
11832      * Returns true if the node is selected
11833      * @param {TreeNode} node The node to check
11834      * @return {Boolean}
11835      */
11836     isSelected : function(node){
11837         return this.selNode == node;  
11838     },
11839
11840     /**
11841      * Selects the node above the selected node in the tree, intelligently walking the nodes
11842      * @return TreeNode The new selection
11843      */
11844     selectPrevious : function(){
11845         var s = this.selNode || this.lastSelNode;
11846         if(!s){
11847             return null;
11848         }
11849         var ps = s.previousSibling;
11850         if(ps){
11851             if(!ps.isExpanded() || ps.childNodes.length < 1){
11852                 return this.select(ps);
11853             } else{
11854                 var lc = ps.lastChild;
11855                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11856                     lc = lc.lastChild;
11857                 }
11858                 return this.select(lc);
11859             }
11860         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11861             return this.select(s.parentNode);
11862         }
11863         return null;
11864     },
11865
11866     /**
11867      * Selects the node above the selected node in the tree, intelligently walking the nodes
11868      * @return TreeNode The new selection
11869      */
11870     selectNext : function(){
11871         var s = this.selNode || this.lastSelNode;
11872         if(!s){
11873             return null;
11874         }
11875         if(s.firstChild && s.isExpanded()){
11876              return this.select(s.firstChild);
11877          }else if(s.nextSibling){
11878              return this.select(s.nextSibling);
11879          }else if(s.parentNode){
11880             var newS = null;
11881             s.parentNode.bubble(function(){
11882                 if(this.nextSibling){
11883                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11884                     return false;
11885                 }
11886             });
11887             return newS;
11888          }
11889         return null;
11890     },
11891
11892     onKeyDown : function(e){
11893         var s = this.selNode || this.lastSelNode;
11894         // undesirable, but required
11895         var sm = this;
11896         if(!s){
11897             return;
11898         }
11899         var k = e.getKey();
11900         switch(k){
11901              case e.DOWN:
11902                  e.stopEvent();
11903                  this.selectNext();
11904              break;
11905              case e.UP:
11906                  e.stopEvent();
11907                  this.selectPrevious();
11908              break;
11909              case e.RIGHT:
11910                  e.preventDefault();
11911                  if(s.hasChildNodes()){
11912                      if(!s.isExpanded()){
11913                          s.expand();
11914                      }else if(s.firstChild){
11915                          this.select(s.firstChild, e);
11916                      }
11917                  }
11918              break;
11919              case e.LEFT:
11920                  e.preventDefault();
11921                  if(s.hasChildNodes() && s.isExpanded()){
11922                      s.collapse();
11923                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11924                      this.select(s.parentNode, e);
11925                  }
11926              break;
11927         };
11928     }
11929 });
11930
11931 /**
11932  * @class Roo.tree.MultiSelectionModel
11933  * @extends Roo.util.Observable
11934  * Multi selection for a TreePanel.
11935  * @param {Object} cfg Configuration
11936  */
11937 Roo.tree.MultiSelectionModel = function(){
11938    this.selNodes = [];
11939    this.selMap = {};
11940    this.addEvents({
11941        /**
11942         * @event selectionchange
11943         * Fires when the selected nodes change
11944         * @param {MultiSelectionModel} this
11945         * @param {Array} nodes Array of the selected nodes
11946         */
11947        "selectionchange" : true
11948    });
11949    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11950    
11951 };
11952
11953 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11954     init : function(tree){
11955         this.tree = tree;
11956         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11957         tree.on("click", this.onNodeClick, this);
11958     },
11959     
11960     onNodeClick : function(node, e){
11961         this.select(node, e, e.ctrlKey);
11962     },
11963     
11964     /**
11965      * Select a node.
11966      * @param {TreeNode} node The node to select
11967      * @param {EventObject} e (optional) An event associated with the selection
11968      * @param {Boolean} keepExisting True to retain existing selections
11969      * @return {TreeNode} The selected node
11970      */
11971     select : function(node, e, keepExisting){
11972         if(keepExisting !== true){
11973             this.clearSelections(true);
11974         }
11975         if(this.isSelected(node)){
11976             this.lastSelNode = node;
11977             return node;
11978         }
11979         this.selNodes.push(node);
11980         this.selMap[node.id] = node;
11981         this.lastSelNode = node;
11982         node.ui.onSelectedChange(true);
11983         this.fireEvent("selectionchange", this, this.selNodes);
11984         return node;
11985     },
11986     
11987     /**
11988      * Deselect a node.
11989      * @param {TreeNode} node The node to unselect
11990      */
11991     unselect : function(node){
11992         if(this.selMap[node.id]){
11993             node.ui.onSelectedChange(false);
11994             var sn = this.selNodes;
11995             var index = -1;
11996             if(sn.indexOf){
11997                 index = sn.indexOf(node);
11998             }else{
11999                 for(var i = 0, len = sn.length; i < len; i++){
12000                     if(sn[i] == node){
12001                         index = i;
12002                         break;
12003                     }
12004                 }
12005             }
12006             if(index != -1){
12007                 this.selNodes.splice(index, 1);
12008             }
12009             delete this.selMap[node.id];
12010             this.fireEvent("selectionchange", this, this.selNodes);
12011         }
12012     },
12013     
12014     /**
12015      * Clear all selections
12016      */
12017     clearSelections : function(suppressEvent){
12018         var sn = this.selNodes;
12019         if(sn.length > 0){
12020             for(var i = 0, len = sn.length; i < len; i++){
12021                 sn[i].ui.onSelectedChange(false);
12022             }
12023             this.selNodes = [];
12024             this.selMap = {};
12025             if(suppressEvent !== true){
12026                 this.fireEvent("selectionchange", this, this.selNodes);
12027             }
12028         }
12029     },
12030     
12031     /**
12032      * Returns true if the node is selected
12033      * @param {TreeNode} node The node to check
12034      * @return {Boolean}
12035      */
12036     isSelected : function(node){
12037         return this.selMap[node.id] ? true : false;  
12038     },
12039     
12040     /**
12041      * Returns an array of the selected nodes
12042      * @return {Array}
12043      */
12044     getSelectedNodes : function(){
12045         return this.selNodes;    
12046     },
12047
12048     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12049
12050     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12051
12052     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064 /**
12065  * @class Roo.tree.TreeNode
12066  * @extends Roo.data.Node
12067  * @cfg {String} text The text for this node
12068  * @cfg {Boolean} expanded true to start the node expanded
12069  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12070  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12071  * @cfg {Boolean} disabled true to start the node disabled
12072  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12073  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12074  * @cfg {String} cls A css class to be added to the node
12075  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12076  * @cfg {String} href URL of the link used for the node (defaults to #)
12077  * @cfg {String} hrefTarget target frame for the link
12078  * @cfg {String} qtip An Ext QuickTip for the node
12079  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12080  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12081  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12082  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12083  * (defaults to undefined with no checkbox rendered)
12084  * @constructor
12085  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12086  */
12087 Roo.tree.TreeNode = function(attributes){
12088     attributes = attributes || {};
12089     if(typeof attributes == "string"){
12090         attributes = {text: attributes};
12091     }
12092     this.childrenRendered = false;
12093     this.rendered = false;
12094     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12095     this.expanded = attributes.expanded === true;
12096     this.isTarget = attributes.isTarget !== false;
12097     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12098     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12099
12100     /**
12101      * Read-only. The text for this node. To change it use setText().
12102      * @type String
12103      */
12104     this.text = attributes.text;
12105     /**
12106      * True if this node is disabled.
12107      * @type Boolean
12108      */
12109     this.disabled = attributes.disabled === true;
12110
12111     this.addEvents({
12112         /**
12113         * @event textchange
12114         * Fires when the text for this node is changed
12115         * @param {Node} this This node
12116         * @param {String} text The new text
12117         * @param {String} oldText The old text
12118         */
12119         "textchange" : true,
12120         /**
12121         * @event beforeexpand
12122         * Fires before this node is expanded, return false to cancel.
12123         * @param {Node} this This node
12124         * @param {Boolean} deep
12125         * @param {Boolean} anim
12126         */
12127         "beforeexpand" : true,
12128         /**
12129         * @event beforecollapse
12130         * Fires before this node is collapsed, return false to cancel.
12131         * @param {Node} this This node
12132         * @param {Boolean} deep
12133         * @param {Boolean} anim
12134         */
12135         "beforecollapse" : true,
12136         /**
12137         * @event expand
12138         * Fires when this node is expanded
12139         * @param {Node} this This node
12140         */
12141         "expand" : true,
12142         /**
12143         * @event disabledchange
12144         * Fires when the disabled status of this node changes
12145         * @param {Node} this This node
12146         * @param {Boolean} disabled
12147         */
12148         "disabledchange" : true,
12149         /**
12150         * @event collapse
12151         * Fires when this node is collapsed
12152         * @param {Node} this This node
12153         */
12154         "collapse" : true,
12155         /**
12156         * @event beforeclick
12157         * Fires before click processing. Return false to cancel the default action.
12158         * @param {Node} this This node
12159         * @param {Roo.EventObject} e The event object
12160         */
12161         "beforeclick":true,
12162         /**
12163         * @event checkchange
12164         * Fires when a node with a checkbox's checked property changes
12165         * @param {Node} this This node
12166         * @param {Boolean} checked
12167         */
12168         "checkchange":true,
12169         /**
12170         * @event click
12171         * Fires when this node is clicked
12172         * @param {Node} this This node
12173         * @param {Roo.EventObject} e The event object
12174         */
12175         "click":true,
12176         /**
12177         * @event dblclick
12178         * Fires when this node is double clicked
12179         * @param {Node} this This node
12180         * @param {Roo.EventObject} e The event object
12181         */
12182         "dblclick":true,
12183         /**
12184         * @event contextmenu
12185         * Fires when this node is right clicked
12186         * @param {Node} this This node
12187         * @param {Roo.EventObject} e The event object
12188         */
12189         "contextmenu":true,
12190         /**
12191         * @event beforechildrenrendered
12192         * Fires right before the child nodes for this node are rendered
12193         * @param {Node} this This node
12194         */
12195         "beforechildrenrendered":true
12196     });
12197
12198     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12199
12200     /**
12201      * Read-only. The UI for this node
12202      * @type TreeNodeUI
12203      */
12204     this.ui = new uiClass(this);
12205     
12206     // finally support items[]
12207     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12208         return;
12209     }
12210     
12211     
12212     Roo.each(this.attributes.items, function(c) {
12213         this.appendChild(Roo.factory(c,Roo.Tree));
12214     }, this);
12215     delete this.attributes.items;
12216     
12217     
12218     
12219 };
12220 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12221     preventHScroll: true,
12222     /**
12223      * Returns true if this node is expanded
12224      * @return {Boolean}
12225      */
12226     isExpanded : function(){
12227         return this.expanded;
12228     },
12229
12230     /**
12231      * Returns the UI object for this node
12232      * @return {TreeNodeUI}
12233      */
12234     getUI : function(){
12235         return this.ui;
12236     },
12237
12238     // private override
12239     setFirstChild : function(node){
12240         var of = this.firstChild;
12241         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12242         if(this.childrenRendered && of && node != of){
12243             of.renderIndent(true, true);
12244         }
12245         if(this.rendered){
12246             this.renderIndent(true, true);
12247         }
12248     },
12249
12250     // private override
12251     setLastChild : function(node){
12252         var ol = this.lastChild;
12253         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12254         if(this.childrenRendered && ol && node != ol){
12255             ol.renderIndent(true, true);
12256         }
12257         if(this.rendered){
12258             this.renderIndent(true, true);
12259         }
12260     },
12261
12262     // these methods are overridden to provide lazy rendering support
12263     // private override
12264     appendChild : function()
12265     {
12266         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12267         if(node && this.childrenRendered){
12268             node.render();
12269         }
12270         this.ui.updateExpandIcon();
12271         return node;
12272     },
12273
12274     // private override
12275     removeChild : function(node){
12276         this.ownerTree.getSelectionModel().unselect(node);
12277         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12278         // if it's been rendered remove dom node
12279         if(this.childrenRendered){
12280             node.ui.remove();
12281         }
12282         if(this.childNodes.length < 1){
12283             this.collapse(false, false);
12284         }else{
12285             this.ui.updateExpandIcon();
12286         }
12287         if(!this.firstChild) {
12288             this.childrenRendered = false;
12289         }
12290         return node;
12291     },
12292
12293     // private override
12294     insertBefore : function(node, refNode){
12295         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12296         if(newNode && refNode && this.childrenRendered){
12297             node.render();
12298         }
12299         this.ui.updateExpandIcon();
12300         return newNode;
12301     },
12302
12303     /**
12304      * Sets the text for this node
12305      * @param {String} text
12306      */
12307     setText : function(text){
12308         var oldText = this.text;
12309         this.text = text;
12310         this.attributes.text = text;
12311         if(this.rendered){ // event without subscribing
12312             this.ui.onTextChange(this, text, oldText);
12313         }
12314         this.fireEvent("textchange", this, text, oldText);
12315     },
12316
12317     /**
12318      * Triggers selection of this node
12319      */
12320     select : function(){
12321         this.getOwnerTree().getSelectionModel().select(this);
12322     },
12323
12324     /**
12325      * Triggers deselection of this node
12326      */
12327     unselect : function(){
12328         this.getOwnerTree().getSelectionModel().unselect(this);
12329     },
12330
12331     /**
12332      * Returns true if this node is selected
12333      * @return {Boolean}
12334      */
12335     isSelected : function(){
12336         return this.getOwnerTree().getSelectionModel().isSelected(this);
12337     },
12338
12339     /**
12340      * Expand this node.
12341      * @param {Boolean} deep (optional) True to expand all children as well
12342      * @param {Boolean} anim (optional) false to cancel the default animation
12343      * @param {Function} callback (optional) A callback to be called when
12344      * expanding this node completes (does not wait for deep expand to complete).
12345      * Called with 1 parameter, this node.
12346      */
12347     expand : function(deep, anim, callback){
12348         if(!this.expanded){
12349             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12350                 return;
12351             }
12352             if(!this.childrenRendered){
12353                 this.renderChildren();
12354             }
12355             this.expanded = true;
12356             
12357             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12358                 this.ui.animExpand(function(){
12359                     this.fireEvent("expand", this);
12360                     if(typeof callback == "function"){
12361                         callback(this);
12362                     }
12363                     if(deep === true){
12364                         this.expandChildNodes(true);
12365                     }
12366                 }.createDelegate(this));
12367                 return;
12368             }else{
12369                 this.ui.expand();
12370                 this.fireEvent("expand", this);
12371                 if(typeof callback == "function"){
12372                     callback(this);
12373                 }
12374             }
12375         }else{
12376            if(typeof callback == "function"){
12377                callback(this);
12378            }
12379         }
12380         if(deep === true){
12381             this.expandChildNodes(true);
12382         }
12383     },
12384
12385     isHiddenRoot : function(){
12386         return this.isRoot && !this.getOwnerTree().rootVisible;
12387     },
12388
12389     /**
12390      * Collapse this node.
12391      * @param {Boolean} deep (optional) True to collapse all children as well
12392      * @param {Boolean} anim (optional) false to cancel the default animation
12393      */
12394     collapse : function(deep, anim){
12395         if(this.expanded && !this.isHiddenRoot()){
12396             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12397                 return;
12398             }
12399             this.expanded = false;
12400             if((this.getOwnerTree().animate && anim !== false) || anim){
12401                 this.ui.animCollapse(function(){
12402                     this.fireEvent("collapse", this);
12403                     if(deep === true){
12404                         this.collapseChildNodes(true);
12405                     }
12406                 }.createDelegate(this));
12407                 return;
12408             }else{
12409                 this.ui.collapse();
12410                 this.fireEvent("collapse", this);
12411             }
12412         }
12413         if(deep === true){
12414             var cs = this.childNodes;
12415             for(var i = 0, len = cs.length; i < len; i++) {
12416                 cs[i].collapse(true, false);
12417             }
12418         }
12419     },
12420
12421     // private
12422     delayedExpand : function(delay){
12423         if(!this.expandProcId){
12424             this.expandProcId = this.expand.defer(delay, this);
12425         }
12426     },
12427
12428     // private
12429     cancelExpand : function(){
12430         if(this.expandProcId){
12431             clearTimeout(this.expandProcId);
12432         }
12433         this.expandProcId = false;
12434     },
12435
12436     /**
12437      * Toggles expanded/collapsed state of the node
12438      */
12439     toggle : function(){
12440         if(this.expanded){
12441             this.collapse();
12442         }else{
12443             this.expand();
12444         }
12445     },
12446
12447     /**
12448      * Ensures all parent nodes are expanded
12449      */
12450     ensureVisible : function(callback){
12451         var tree = this.getOwnerTree();
12452         tree.expandPath(this.parentNode.getPath(), false, function(){
12453             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12454             Roo.callback(callback);
12455         }.createDelegate(this));
12456     },
12457
12458     /**
12459      * Expand all child nodes
12460      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12461      */
12462     expandChildNodes : function(deep){
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++) {
12465                 cs[i].expand(deep);
12466         }
12467     },
12468
12469     /**
12470      * Collapse all child nodes
12471      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12472      */
12473     collapseChildNodes : function(deep){
12474         var cs = this.childNodes;
12475         for(var i = 0, len = cs.length; i < len; i++) {
12476                 cs[i].collapse(deep);
12477         }
12478     },
12479
12480     /**
12481      * Disables this node
12482      */
12483     disable : function(){
12484         this.disabled = true;
12485         this.unselect();
12486         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12487             this.ui.onDisableChange(this, true);
12488         }
12489         this.fireEvent("disabledchange", this, true);
12490     },
12491
12492     /**
12493      * Enables this node
12494      */
12495     enable : function(){
12496         this.disabled = false;
12497         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12498             this.ui.onDisableChange(this, false);
12499         }
12500         this.fireEvent("disabledchange", this, false);
12501     },
12502
12503     // private
12504     renderChildren : function(suppressEvent){
12505         if(suppressEvent !== false){
12506             this.fireEvent("beforechildrenrendered", this);
12507         }
12508         var cs = this.childNodes;
12509         for(var i = 0, len = cs.length; i < len; i++){
12510             cs[i].render(true);
12511         }
12512         this.childrenRendered = true;
12513     },
12514
12515     // private
12516     sort : function(fn, scope){
12517         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12518         if(this.childrenRendered){
12519             var cs = this.childNodes;
12520             for(var i = 0, len = cs.length; i < len; i++){
12521                 cs[i].render(true);
12522             }
12523         }
12524     },
12525
12526     // private
12527     render : function(bulkRender){
12528         this.ui.render(bulkRender);
12529         if(!this.rendered){
12530             this.rendered = true;
12531             if(this.expanded){
12532                 this.expanded = false;
12533                 this.expand(false, false);
12534             }
12535         }
12536     },
12537
12538     // private
12539     renderIndent : function(deep, refresh){
12540         if(refresh){
12541             this.ui.childIndent = null;
12542         }
12543         this.ui.renderIndent();
12544         if(deep === true && this.childrenRendered){
12545             var cs = this.childNodes;
12546             for(var i = 0, len = cs.length; i < len; i++){
12547                 cs[i].renderIndent(true, refresh);
12548             }
12549         }
12550     }
12551 });/*
12552  * Based on:
12553  * Ext JS Library 1.1.1
12554  * Copyright(c) 2006-2007, Ext JS, LLC.
12555  *
12556  * Originally Released Under LGPL - original licence link has changed is not relivant.
12557  *
12558  * Fork - LGPL
12559  * <script type="text/javascript">
12560  */
12561  
12562 /**
12563  * @class Roo.tree.AsyncTreeNode
12564  * @extends Roo.tree.TreeNode
12565  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12566  * @constructor
12567  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12568  */
12569  Roo.tree.AsyncTreeNode = function(config){
12570     this.loaded = false;
12571     this.loading = false;
12572     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12573     /**
12574     * @event beforeload
12575     * Fires before this node is loaded, return false to cancel
12576     * @param {Node} this This node
12577     */
12578     this.addEvents({'beforeload':true, 'load': true});
12579     /**
12580     * @event load
12581     * Fires when this node is loaded
12582     * @param {Node} this This node
12583     */
12584     /**
12585      * The loader used by this node (defaults to using the tree's defined loader)
12586      * @type TreeLoader
12587      * @property loader
12588      */
12589 };
12590 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12591     expand : function(deep, anim, callback){
12592         if(this.loading){ // if an async load is already running, waiting til it's done
12593             var timer;
12594             var f = function(){
12595                 if(!this.loading){ // done loading
12596                     clearInterval(timer);
12597                     this.expand(deep, anim, callback);
12598                 }
12599             }.createDelegate(this);
12600             timer = setInterval(f, 200);
12601             return;
12602         }
12603         if(!this.loaded){
12604             if(this.fireEvent("beforeload", this) === false){
12605                 return;
12606             }
12607             this.loading = true;
12608             this.ui.beforeLoad(this);
12609             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12610             if(loader){
12611                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12612                 return;
12613             }
12614         }
12615         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12616     },
12617     
12618     /**
12619      * Returns true if this node is currently loading
12620      * @return {Boolean}
12621      */
12622     isLoading : function(){
12623         return this.loading;  
12624     },
12625     
12626     loadComplete : function(deep, anim, callback){
12627         this.loading = false;
12628         this.loaded = true;
12629         this.ui.afterLoad(this);
12630         this.fireEvent("load", this);
12631         this.expand(deep, anim, callback);
12632     },
12633     
12634     /**
12635      * Returns true if this node has been loaded
12636      * @return {Boolean}
12637      */
12638     isLoaded : function(){
12639         return this.loaded;
12640     },
12641     
12642     hasChildNodes : function(){
12643         if(!this.isLeaf() && !this.loaded){
12644             return true;
12645         }else{
12646             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12647         }
12648     },
12649
12650     /**
12651      * Trigger a reload for this node
12652      * @param {Function} callback
12653      */
12654     reload : function(callback){
12655         this.collapse(false, false);
12656         while(this.firstChild){
12657             this.removeChild(this.firstChild);
12658         }
12659         this.childrenRendered = false;
12660         this.loaded = false;
12661         if(this.isHiddenRoot()){
12662             this.expanded = false;
12663         }
12664         this.expand(false, false, callback);
12665     }
12666 });/*
12667  * Based on:
12668  * Ext JS Library 1.1.1
12669  * Copyright(c) 2006-2007, Ext JS, LLC.
12670  *
12671  * Originally Released Under LGPL - original licence link has changed is not relivant.
12672  *
12673  * Fork - LGPL
12674  * <script type="text/javascript">
12675  */
12676  
12677 /**
12678  * @class Roo.tree.TreeNodeUI
12679  * @constructor
12680  * @param {Object} node The node to render
12681  * The TreeNode UI implementation is separate from the
12682  * tree implementation. Unless you are customizing the tree UI,
12683  * you should never have to use this directly.
12684  */
12685 Roo.tree.TreeNodeUI = function(node){
12686     this.node = node;
12687     this.rendered = false;
12688     this.animating = false;
12689     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12690 };
12691
12692 Roo.tree.TreeNodeUI.prototype = {
12693     removeChild : function(node){
12694         if(this.rendered){
12695             this.ctNode.removeChild(node.ui.getEl());
12696         }
12697     },
12698
12699     beforeLoad : function(){
12700          this.addClass("x-tree-node-loading");
12701     },
12702
12703     afterLoad : function(){
12704          this.removeClass("x-tree-node-loading");
12705     },
12706
12707     onTextChange : function(node, text, oldText){
12708         if(this.rendered){
12709             this.textNode.innerHTML = text;
12710         }
12711     },
12712
12713     onDisableChange : function(node, state){
12714         this.disabled = state;
12715         if(state){
12716             this.addClass("x-tree-node-disabled");
12717         }else{
12718             this.removeClass("x-tree-node-disabled");
12719         }
12720     },
12721
12722     onSelectedChange : function(state){
12723         if(state){
12724             this.focus();
12725             this.addClass("x-tree-selected");
12726         }else{
12727             //this.blur();
12728             this.removeClass("x-tree-selected");
12729         }
12730     },
12731
12732     onMove : function(tree, node, oldParent, newParent, index, refNode){
12733         this.childIndent = null;
12734         if(this.rendered){
12735             var targetNode = newParent.ui.getContainer();
12736             if(!targetNode){//target not rendered
12737                 this.holder = document.createElement("div");
12738                 this.holder.appendChild(this.wrap);
12739                 return;
12740             }
12741             var insertBefore = refNode ? refNode.ui.getEl() : null;
12742             if(insertBefore){
12743                 targetNode.insertBefore(this.wrap, insertBefore);
12744             }else{
12745                 targetNode.appendChild(this.wrap);
12746             }
12747             this.node.renderIndent(true);
12748         }
12749     },
12750
12751     addClass : function(cls){
12752         if(this.elNode){
12753             Roo.fly(this.elNode).addClass(cls);
12754         }
12755     },
12756
12757     removeClass : function(cls){
12758         if(this.elNode){
12759             Roo.fly(this.elNode).removeClass(cls);
12760         }
12761     },
12762
12763     remove : function(){
12764         if(this.rendered){
12765             this.holder = document.createElement("div");
12766             this.holder.appendChild(this.wrap);
12767         }
12768     },
12769
12770     fireEvent : function(){
12771         return this.node.fireEvent.apply(this.node, arguments);
12772     },
12773
12774     initEvents : function(){
12775         this.node.on("move", this.onMove, this);
12776         var E = Roo.EventManager;
12777         var a = this.anchor;
12778
12779         var el = Roo.fly(a, '_treeui');
12780
12781         if(Roo.isOpera){ // opera render bug ignores the CSS
12782             el.setStyle("text-decoration", "none");
12783         }
12784
12785         el.on("click", this.onClick, this);
12786         el.on("dblclick", this.onDblClick, this);
12787
12788         if(this.checkbox){
12789             Roo.EventManager.on(this.checkbox,
12790                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12791         }
12792
12793         el.on("contextmenu", this.onContextMenu, this);
12794
12795         var icon = Roo.fly(this.iconNode);
12796         icon.on("click", this.onClick, this);
12797         icon.on("dblclick", this.onDblClick, this);
12798         icon.on("contextmenu", this.onContextMenu, this);
12799         E.on(this.ecNode, "click", this.ecClick, this, true);
12800
12801         if(this.node.disabled){
12802             this.addClass("x-tree-node-disabled");
12803         }
12804         if(this.node.hidden){
12805             this.addClass("x-tree-node-disabled");
12806         }
12807         var ot = this.node.getOwnerTree();
12808         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12809         if(dd && (!this.node.isRoot || ot.rootVisible)){
12810             Roo.dd.Registry.register(this.elNode, {
12811                 node: this.node,
12812                 handles: this.getDDHandles(),
12813                 isHandle: false
12814             });
12815         }
12816     },
12817
12818     getDDHandles : function(){
12819         return [this.iconNode, this.textNode];
12820     },
12821
12822     hide : function(){
12823         if(this.rendered){
12824             this.wrap.style.display = "none";
12825         }
12826     },
12827
12828     show : function(){
12829         if(this.rendered){
12830             this.wrap.style.display = "";
12831         }
12832     },
12833
12834     onContextMenu : function(e){
12835         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12836             e.preventDefault();
12837             this.focus();
12838             this.fireEvent("contextmenu", this.node, e);
12839         }
12840     },
12841
12842     onClick : function(e){
12843         if(this.dropping){
12844             e.stopEvent();
12845             return;
12846         }
12847         if(this.fireEvent("beforeclick", this.node, e) !== false){
12848             if(!this.disabled && this.node.attributes.href){
12849                 this.fireEvent("click", this.node, e);
12850                 return;
12851             }
12852             e.preventDefault();
12853             if(this.disabled){
12854                 return;
12855             }
12856
12857             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12858                 this.node.toggle();
12859             }
12860
12861             this.fireEvent("click", this.node, e);
12862         }else{
12863             e.stopEvent();
12864         }
12865     },
12866
12867     onDblClick : function(e){
12868         e.preventDefault();
12869         if(this.disabled){
12870             return;
12871         }
12872         if(this.checkbox){
12873             this.toggleCheck();
12874         }
12875         if(!this.animating && this.node.hasChildNodes()){
12876             this.node.toggle();
12877         }
12878         this.fireEvent("dblclick", this.node, e);
12879     },
12880
12881     onCheckChange : function(){
12882         var checked = this.checkbox.checked;
12883         this.node.attributes.checked = checked;
12884         this.fireEvent('checkchange', this.node, checked);
12885     },
12886
12887     ecClick : function(e){
12888         if(!this.animating && this.node.hasChildNodes()){
12889             this.node.toggle();
12890         }
12891     },
12892
12893     startDrop : function(){
12894         this.dropping = true;
12895     },
12896
12897     // delayed drop so the click event doesn't get fired on a drop
12898     endDrop : function(){
12899        setTimeout(function(){
12900            this.dropping = false;
12901        }.createDelegate(this), 50);
12902     },
12903
12904     expand : function(){
12905         this.updateExpandIcon();
12906         this.ctNode.style.display = "";
12907     },
12908
12909     focus : function(){
12910         if(!this.node.preventHScroll){
12911             try{this.anchor.focus();
12912             }catch(e){}
12913         }else if(!Roo.isIE){
12914             try{
12915                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12916                 var l = noscroll.scrollLeft;
12917                 this.anchor.focus();
12918                 noscroll.scrollLeft = l;
12919             }catch(e){}
12920         }
12921     },
12922
12923     toggleCheck : function(value){
12924         var cb = this.checkbox;
12925         if(cb){
12926             cb.checked = (value === undefined ? !cb.checked : value);
12927         }
12928     },
12929
12930     blur : function(){
12931         try{
12932             this.anchor.blur();
12933         }catch(e){}
12934     },
12935
12936     animExpand : function(callback){
12937         var ct = Roo.get(this.ctNode);
12938         ct.stopFx();
12939         if(!this.node.hasChildNodes()){
12940             this.updateExpandIcon();
12941             this.ctNode.style.display = "";
12942             Roo.callback(callback);
12943             return;
12944         }
12945         this.animating = true;
12946         this.updateExpandIcon();
12947
12948         ct.slideIn('t', {
12949            callback : function(){
12950                this.animating = false;
12951                Roo.callback(callback);
12952             },
12953             scope: this,
12954             duration: this.node.ownerTree.duration || .25
12955         });
12956     },
12957
12958     highlight : function(){
12959         var tree = this.node.getOwnerTree();
12960         Roo.fly(this.wrap).highlight(
12961             tree.hlColor || "C3DAF9",
12962             {endColor: tree.hlBaseColor}
12963         );
12964     },
12965
12966     collapse : function(){
12967         this.updateExpandIcon();
12968         this.ctNode.style.display = "none";
12969     },
12970
12971     animCollapse : function(callback){
12972         var ct = Roo.get(this.ctNode);
12973         ct.enableDisplayMode('block');
12974         ct.stopFx();
12975
12976         this.animating = true;
12977         this.updateExpandIcon();
12978
12979         ct.slideOut('t', {
12980             callback : function(){
12981                this.animating = false;
12982                Roo.callback(callback);
12983             },
12984             scope: this,
12985             duration: this.node.ownerTree.duration || .25
12986         });
12987     },
12988
12989     getContainer : function(){
12990         return this.ctNode;
12991     },
12992
12993     getEl : function(){
12994         return this.wrap;
12995     },
12996
12997     appendDDGhost : function(ghostNode){
12998         ghostNode.appendChild(this.elNode.cloneNode(true));
12999     },
13000
13001     getDDRepairXY : function(){
13002         return Roo.lib.Dom.getXY(this.iconNode);
13003     },
13004
13005     onRender : function(){
13006         this.render();
13007     },
13008
13009     render : function(bulkRender){
13010         var n = this.node, a = n.attributes;
13011         var targetNode = n.parentNode ?
13012               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13013
13014         if(!this.rendered){
13015             this.rendered = true;
13016
13017             this.renderElements(n, a, targetNode, bulkRender);
13018
13019             if(a.qtip){
13020                if(this.textNode.setAttributeNS){
13021                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13022                    if(a.qtipTitle){
13023                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13024                    }
13025                }else{
13026                    this.textNode.setAttribute("ext:qtip", a.qtip);
13027                    if(a.qtipTitle){
13028                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13029                    }
13030                }
13031             }else if(a.qtipCfg){
13032                 a.qtipCfg.target = Roo.id(this.textNode);
13033                 Roo.QuickTips.register(a.qtipCfg);
13034             }
13035             this.initEvents();
13036             if(!this.node.expanded){
13037                 this.updateExpandIcon();
13038             }
13039         }else{
13040             if(bulkRender === true) {
13041                 targetNode.appendChild(this.wrap);
13042             }
13043         }
13044     },
13045
13046     renderElements : function(n, a, targetNode, bulkRender)
13047     {
13048         // add some indent caching, this helps performance when rendering a large tree
13049         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13050         var t = n.getOwnerTree();
13051         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13052         if (typeof(n.attributes.html) != 'undefined') {
13053             txt = n.attributes.html;
13054         }
13055         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13056         var cb = typeof a.checked == 'boolean';
13057         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13058         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13059             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13060             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13061             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13062             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13063             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13064              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13065                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13066             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13067             "</li>"];
13068
13069         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13070             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13071                                 n.nextSibling.ui.getEl(), buf.join(""));
13072         }else{
13073             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13074         }
13075
13076         this.elNode = this.wrap.childNodes[0];
13077         this.ctNode = this.wrap.childNodes[1];
13078         var cs = this.elNode.childNodes;
13079         this.indentNode = cs[0];
13080         this.ecNode = cs[1];
13081         this.iconNode = cs[2];
13082         var index = 3;
13083         if(cb){
13084             this.checkbox = cs[3];
13085             index++;
13086         }
13087         this.anchor = cs[index];
13088         this.textNode = cs[index].firstChild;
13089     },
13090
13091     getAnchor : function(){
13092         return this.anchor;
13093     },
13094
13095     getTextEl : function(){
13096         return this.textNode;
13097     },
13098
13099     getIconEl : function(){
13100         return this.iconNode;
13101     },
13102
13103     isChecked : function(){
13104         return this.checkbox ? this.checkbox.checked : false;
13105     },
13106
13107     updateExpandIcon : function(){
13108         if(this.rendered){
13109             var n = this.node, c1, c2;
13110             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13111             var hasChild = n.hasChildNodes();
13112             if(hasChild){
13113                 if(n.expanded){
13114                     cls += "-minus";
13115                     c1 = "x-tree-node-collapsed";
13116                     c2 = "x-tree-node-expanded";
13117                 }else{
13118                     cls += "-plus";
13119                     c1 = "x-tree-node-expanded";
13120                     c2 = "x-tree-node-collapsed";
13121                 }
13122                 if(this.wasLeaf){
13123                     this.removeClass("x-tree-node-leaf");
13124                     this.wasLeaf = false;
13125                 }
13126                 if(this.c1 != c1 || this.c2 != c2){
13127                     Roo.fly(this.elNode).replaceClass(c1, c2);
13128                     this.c1 = c1; this.c2 = c2;
13129                 }
13130             }else{
13131                 // this changes non-leafs into leafs if they have no children.
13132                 // it's not very rational behaviour..
13133                 
13134                 if(!this.wasLeaf && this.node.leaf){
13135                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13136                     delete this.c1;
13137                     delete this.c2;
13138                     this.wasLeaf = true;
13139                 }
13140             }
13141             var ecc = "x-tree-ec-icon "+cls;
13142             if(this.ecc != ecc){
13143                 this.ecNode.className = ecc;
13144                 this.ecc = ecc;
13145             }
13146         }
13147     },
13148
13149     getChildIndent : function(){
13150         if(!this.childIndent){
13151             var buf = [];
13152             var p = this.node;
13153             while(p){
13154                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13155                     if(!p.isLast()) {
13156                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13157                     } else {
13158                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13159                     }
13160                 }
13161                 p = p.parentNode;
13162             }
13163             this.childIndent = buf.join("");
13164         }
13165         return this.childIndent;
13166     },
13167
13168     renderIndent : function(){
13169         if(this.rendered){
13170             var indent = "";
13171             var p = this.node.parentNode;
13172             if(p){
13173                 indent = p.ui.getChildIndent();
13174             }
13175             if(this.indentMarkup != indent){ // don't rerender if not required
13176                 this.indentNode.innerHTML = indent;
13177                 this.indentMarkup = indent;
13178             }
13179             this.updateExpandIcon();
13180         }
13181     }
13182 };
13183
13184 Roo.tree.RootTreeNodeUI = function(){
13185     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13186 };
13187 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13188     render : function(){
13189         if(!this.rendered){
13190             var targetNode = this.node.ownerTree.innerCt.dom;
13191             this.node.expanded = true;
13192             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13193             this.wrap = this.ctNode = targetNode.firstChild;
13194         }
13195     },
13196     collapse : function(){
13197     },
13198     expand : function(){
13199     }
13200 });/*
13201  * Based on:
13202  * Ext JS Library 1.1.1
13203  * Copyright(c) 2006-2007, Ext JS, LLC.
13204  *
13205  * Originally Released Under LGPL - original licence link has changed is not relivant.
13206  *
13207  * Fork - LGPL
13208  * <script type="text/javascript">
13209  */
13210 /**
13211  * @class Roo.tree.TreeLoader
13212  * @extends Roo.util.Observable
13213  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13214  * nodes from a specified URL. The response must be a javascript Array definition
13215  * who's elements are node definition objects. eg:
13216  * <pre><code>
13217 {  success : true,
13218    data :      [
13219    
13220     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13221     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13222     ]
13223 }
13224
13225
13226 </code></pre>
13227  * <br><br>
13228  * The old style respose with just an array is still supported, but not recommended.
13229  * <br><br>
13230  *
13231  * A server request is sent, and child nodes are loaded only when a node is expanded.
13232  * The loading node's id is passed to the server under the parameter name "node" to
13233  * enable the server to produce the correct child nodes.
13234  * <br><br>
13235  * To pass extra parameters, an event handler may be attached to the "beforeload"
13236  * event, and the parameters specified in the TreeLoader's baseParams property:
13237  * <pre><code>
13238     myTreeLoader.on("beforeload", function(treeLoader, node) {
13239         this.baseParams.category = node.attributes.category;
13240     }, this);
13241     
13242 </code></pre>
13243  *
13244  * This would pass an HTTP parameter called "category" to the server containing
13245  * the value of the Node's "category" attribute.
13246  * @constructor
13247  * Creates a new Treeloader.
13248  * @param {Object} config A config object containing config properties.
13249  */
13250 Roo.tree.TreeLoader = function(config){
13251     this.baseParams = {};
13252     this.requestMethod = "POST";
13253     Roo.apply(this, config);
13254
13255     this.addEvents({
13256     
13257         /**
13258          * @event beforeload
13259          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13260          * @param {Object} This TreeLoader object.
13261          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13262          * @param {Object} callback The callback function specified in the {@link #load} call.
13263          */
13264         beforeload : true,
13265         /**
13266          * @event load
13267          * Fires when the node has been successfuly loaded.
13268          * @param {Object} This TreeLoader object.
13269          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13270          * @param {Object} response The response object containing the data from the server.
13271          */
13272         load : true,
13273         /**
13274          * @event loadexception
13275          * Fires if the network request failed.
13276          * @param {Object} This TreeLoader object.
13277          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13278          * @param {Object} response The response object containing the data from the server.
13279          */
13280         loadexception : true,
13281         /**
13282          * @event create
13283          * Fires before a node is created, enabling you to return custom Node types 
13284          * @param {Object} This TreeLoader object.
13285          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13286          */
13287         create : true
13288     });
13289
13290     Roo.tree.TreeLoader.superclass.constructor.call(this);
13291 };
13292
13293 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13294     /**
13295     * @cfg {String} dataUrl The URL from which to request a Json string which
13296     * specifies an array of node definition object representing the child nodes
13297     * to be loaded.
13298     */
13299     /**
13300     * @cfg {String} requestMethod either GET or POST
13301     * defaults to POST (due to BC)
13302     * to be loaded.
13303     */
13304     /**
13305     * @cfg {Object} baseParams (optional) An object containing properties which
13306     * specify HTTP parameters to be passed to each request for child nodes.
13307     */
13308     /**
13309     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13310     * created by this loader. If the attributes sent by the server have an attribute in this object,
13311     * they take priority.
13312     */
13313     /**
13314     * @cfg {Object} uiProviders (optional) An object containing properties which
13315     * 
13316     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13317     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13318     * <i>uiProvider</i> attribute of a returned child node is a string rather
13319     * than a reference to a TreeNodeUI implementation, this that string value
13320     * is used as a property name in the uiProviders object. You can define the provider named
13321     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13322     */
13323     uiProviders : {},
13324
13325     /**
13326     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13327     * child nodes before loading.
13328     */
13329     clearOnLoad : true,
13330
13331     /**
13332     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13333     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13334     * Grid query { data : [ .....] }
13335     */
13336     
13337     root : false,
13338      /**
13339     * @cfg {String} queryParam (optional) 
13340     * Name of the query as it will be passed on the querystring (defaults to 'node')
13341     * eg. the request will be ?node=[id]
13342     */
13343     
13344     
13345     queryParam: false,
13346     
13347     /**
13348      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13349      * This is called automatically when a node is expanded, but may be used to reload
13350      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13351      * @param {Roo.tree.TreeNode} node
13352      * @param {Function} callback
13353      */
13354     load : function(node, callback){
13355         if(this.clearOnLoad){
13356             while(node.firstChild){
13357                 node.removeChild(node.firstChild);
13358             }
13359         }
13360         if(node.attributes.children){ // preloaded json children
13361             var cs = node.attributes.children;
13362             for(var i = 0, len = cs.length; i < len; i++){
13363                 node.appendChild(this.createNode(cs[i]));
13364             }
13365             if(typeof callback == "function"){
13366                 callback();
13367             }
13368         }else if(this.dataUrl){
13369             this.requestData(node, callback);
13370         }
13371     },
13372
13373     getParams: function(node){
13374         var buf = [], bp = this.baseParams;
13375         for(var key in bp){
13376             if(typeof bp[key] != "function"){
13377                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13378             }
13379         }
13380         var n = this.queryParam === false ? 'node' : this.queryParam;
13381         buf.push(n + "=", encodeURIComponent(node.id));
13382         return buf.join("");
13383     },
13384
13385     requestData : function(node, callback){
13386         if(this.fireEvent("beforeload", this, node, callback) !== false){
13387             this.transId = Roo.Ajax.request({
13388                 method:this.requestMethod,
13389                 url: this.dataUrl||this.url,
13390                 success: this.handleResponse,
13391                 failure: this.handleFailure,
13392                 scope: this,
13393                 argument: {callback: callback, node: node},
13394                 params: this.getParams(node)
13395             });
13396         }else{
13397             // if the load is cancelled, make sure we notify
13398             // the node that we are done
13399             if(typeof callback == "function"){
13400                 callback();
13401             }
13402         }
13403     },
13404
13405     isLoading : function(){
13406         return this.transId ? true : false;
13407     },
13408
13409     abort : function(){
13410         if(this.isLoading()){
13411             Roo.Ajax.abort(this.transId);
13412         }
13413     },
13414
13415     // private
13416     createNode : function(attr)
13417     {
13418         // apply baseAttrs, nice idea Corey!
13419         if(this.baseAttrs){
13420             Roo.applyIf(attr, this.baseAttrs);
13421         }
13422         if(this.applyLoader !== false){
13423             attr.loader = this;
13424         }
13425         // uiProvider = depreciated..
13426         
13427         if(typeof(attr.uiProvider) == 'string'){
13428            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13429                 /**  eval:var:attr */ eval(attr.uiProvider);
13430         }
13431         if(typeof(this.uiProviders['default']) != 'undefined') {
13432             attr.uiProvider = this.uiProviders['default'];
13433         }
13434         
13435         this.fireEvent('create', this, attr);
13436         
13437         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13438         return(attr.leaf ?
13439                         new Roo.tree.TreeNode(attr) :
13440                         new Roo.tree.AsyncTreeNode(attr));
13441     },
13442
13443     processResponse : function(response, node, callback)
13444     {
13445         var json = response.responseText;
13446         try {
13447             
13448             var o = Roo.decode(json);
13449             
13450             if (this.root === false && typeof(o.success) != undefined) {
13451                 this.root = 'data'; // the default behaviour for list like data..
13452                 }
13453                 
13454             if (this.root !== false &&  !o.success) {
13455                 // it's a failure condition.
13456                 var a = response.argument;
13457                 this.fireEvent("loadexception", this, a.node, response);
13458                 Roo.log("Load failed - should have a handler really");
13459                 return;
13460             }
13461             
13462             
13463             
13464             if (this.root !== false) {
13465                  o = o[this.root];
13466             }
13467             
13468             for(var i = 0, len = o.length; i < len; i++){
13469                 var n = this.createNode(o[i]);
13470                 if(n){
13471                     node.appendChild(n);
13472                 }
13473             }
13474             if(typeof callback == "function"){
13475                 callback(this, node);
13476             }
13477         }catch(e){
13478             this.handleFailure(response);
13479         }
13480     },
13481
13482     handleResponse : function(response){
13483         this.transId = false;
13484         var a = response.argument;
13485         this.processResponse(response, a.node, a.callback);
13486         this.fireEvent("load", this, a.node, response);
13487     },
13488
13489     handleFailure : function(response)
13490     {
13491         // should handle failure better..
13492         this.transId = false;
13493         var a = response.argument;
13494         this.fireEvent("loadexception", this, a.node, response);
13495         if(typeof a.callback == "function"){
13496             a.callback(this, a.node);
13497         }
13498     }
13499 });/*
13500  * Based on:
13501  * Ext JS Library 1.1.1
13502  * Copyright(c) 2006-2007, Ext JS, LLC.
13503  *
13504  * Originally Released Under LGPL - original licence link has changed is not relivant.
13505  *
13506  * Fork - LGPL
13507  * <script type="text/javascript">
13508  */
13509
13510 /**
13511 * @class Roo.tree.TreeFilter
13512 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13513 * @param {TreePanel} tree
13514 * @param {Object} config (optional)
13515  */
13516 Roo.tree.TreeFilter = function(tree, config){
13517     this.tree = tree;
13518     this.filtered = {};
13519     Roo.apply(this, config);
13520 };
13521
13522 Roo.tree.TreeFilter.prototype = {
13523     clearBlank:false,
13524     reverse:false,
13525     autoClear:false,
13526     remove:false,
13527
13528      /**
13529      * Filter the data by a specific attribute.
13530      * @param {String/RegExp} value Either string that the attribute value
13531      * should start with or a RegExp to test against the attribute
13532      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13533      * @param {TreeNode} startNode (optional) The node to start the filter at.
13534      */
13535     filter : function(value, attr, startNode){
13536         attr = attr || "text";
13537         var f;
13538         if(typeof value == "string"){
13539             var vlen = value.length;
13540             // auto clear empty filter
13541             if(vlen == 0 && this.clearBlank){
13542                 this.clear();
13543                 return;
13544             }
13545             value = value.toLowerCase();
13546             f = function(n){
13547                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13548             };
13549         }else if(value.exec){ // regex?
13550             f = function(n){
13551                 return value.test(n.attributes[attr]);
13552             };
13553         }else{
13554             throw 'Illegal filter type, must be string or regex';
13555         }
13556         this.filterBy(f, null, startNode);
13557         },
13558
13559     /**
13560      * Filter by a function. The passed function will be called with each
13561      * node in the tree (or from the startNode). If the function returns true, the node is kept
13562      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13563      * @param {Function} fn The filter function
13564      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13565      */
13566     filterBy : function(fn, scope, startNode){
13567         startNode = startNode || this.tree.root;
13568         if(this.autoClear){
13569             this.clear();
13570         }
13571         var af = this.filtered, rv = this.reverse;
13572         var f = function(n){
13573             if(n == startNode){
13574                 return true;
13575             }
13576             if(af[n.id]){
13577                 return false;
13578             }
13579             var m = fn.call(scope || n, n);
13580             if(!m || rv){
13581                 af[n.id] = n;
13582                 n.ui.hide();
13583                 return false;
13584             }
13585             return true;
13586         };
13587         startNode.cascade(f);
13588         if(this.remove){
13589            for(var id in af){
13590                if(typeof id != "function"){
13591                    var n = af[id];
13592                    if(n && n.parentNode){
13593                        n.parentNode.removeChild(n);
13594                    }
13595                }
13596            }
13597         }
13598     },
13599
13600     /**
13601      * Clears the current filter. Note: with the "remove" option
13602      * set a filter cannot be cleared.
13603      */
13604     clear : function(){
13605         var t = this.tree;
13606         var af = this.filtered;
13607         for(var id in af){
13608             if(typeof id != "function"){
13609                 var n = af[id];
13610                 if(n){
13611                     n.ui.show();
13612                 }
13613             }
13614         }
13615         this.filtered = {};
13616     }
13617 };
13618 /*
13619  * Based on:
13620  * Ext JS Library 1.1.1
13621  * Copyright(c) 2006-2007, Ext JS, LLC.
13622  *
13623  * Originally Released Under LGPL - original licence link has changed is not relivant.
13624  *
13625  * Fork - LGPL
13626  * <script type="text/javascript">
13627  */
13628  
13629
13630 /**
13631  * @class Roo.tree.TreeSorter
13632  * Provides sorting of nodes in a TreePanel
13633  * 
13634  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13635  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13636  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13637  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13638  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13639  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13640  * @constructor
13641  * @param {TreePanel} tree
13642  * @param {Object} config
13643  */
13644 Roo.tree.TreeSorter = function(tree, config){
13645     Roo.apply(this, config);
13646     tree.on("beforechildrenrendered", this.doSort, this);
13647     tree.on("append", this.updateSort, this);
13648     tree.on("insert", this.updateSort, this);
13649     
13650     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13651     var p = this.property || "text";
13652     var sortType = this.sortType;
13653     var fs = this.folderSort;
13654     var cs = this.caseSensitive === true;
13655     var leafAttr = this.leafAttr || 'leaf';
13656
13657     this.sortFn = function(n1, n2){
13658         if(fs){
13659             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13660                 return 1;
13661             }
13662             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13663                 return -1;
13664             }
13665         }
13666         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13667         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13668         if(v1 < v2){
13669                         return dsc ? +1 : -1;
13670                 }else if(v1 > v2){
13671                         return dsc ? -1 : +1;
13672         }else{
13673                 return 0;
13674         }
13675     };
13676 };
13677
13678 Roo.tree.TreeSorter.prototype = {
13679     doSort : function(node){
13680         node.sort(this.sortFn);
13681     },
13682     
13683     compareNodes : function(n1, n2){
13684         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13685     },
13686     
13687     updateSort : function(tree, node){
13688         if(node.childrenRendered){
13689             this.doSort.defer(1, this, [node]);
13690         }
13691     }
13692 };/*
13693  * Based on:
13694  * Ext JS Library 1.1.1
13695  * Copyright(c) 2006-2007, Ext JS, LLC.
13696  *
13697  * Originally Released Under LGPL - original licence link has changed is not relivant.
13698  *
13699  * Fork - LGPL
13700  * <script type="text/javascript">
13701  */
13702
13703 if(Roo.dd.DropZone){
13704     
13705 Roo.tree.TreeDropZone = function(tree, config){
13706     this.allowParentInsert = false;
13707     this.allowContainerDrop = false;
13708     this.appendOnly = false;
13709     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13710     this.tree = tree;
13711     this.lastInsertClass = "x-tree-no-status";
13712     this.dragOverData = {};
13713 };
13714
13715 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13716     ddGroup : "TreeDD",
13717     scroll:  true,
13718     
13719     expandDelay : 1000,
13720     
13721     expandNode : function(node){
13722         if(node.hasChildNodes() && !node.isExpanded()){
13723             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13724         }
13725     },
13726     
13727     queueExpand : function(node){
13728         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13729     },
13730     
13731     cancelExpand : function(){
13732         if(this.expandProcId){
13733             clearTimeout(this.expandProcId);
13734             this.expandProcId = false;
13735         }
13736     },
13737     
13738     isValidDropPoint : function(n, pt, dd, e, data){
13739         if(!n || !data){ return false; }
13740         var targetNode = n.node;
13741         var dropNode = data.node;
13742         // default drop rules
13743         if(!(targetNode && targetNode.isTarget && pt)){
13744             return false;
13745         }
13746         if(pt == "append" && targetNode.allowChildren === false){
13747             return false;
13748         }
13749         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13750             return false;
13751         }
13752         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13753             return false;
13754         }
13755         // reuse the object
13756         var overEvent = this.dragOverData;
13757         overEvent.tree = this.tree;
13758         overEvent.target = targetNode;
13759         overEvent.data = data;
13760         overEvent.point = pt;
13761         overEvent.source = dd;
13762         overEvent.rawEvent = e;
13763         overEvent.dropNode = dropNode;
13764         overEvent.cancel = false;  
13765         var result = this.tree.fireEvent("nodedragover", overEvent);
13766         return overEvent.cancel === false && result !== false;
13767     },
13768     
13769     getDropPoint : function(e, n, dd)
13770     {
13771         var tn = n.node;
13772         if(tn.isRoot){
13773             return tn.allowChildren !== false ? "append" : false; // always append for root
13774         }
13775         var dragEl = n.ddel;
13776         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13777         var y = Roo.lib.Event.getPageY(e);
13778         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13779         
13780         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13781         var noAppend = tn.allowChildren === false;
13782         if(this.appendOnly || tn.parentNode.allowChildren === false){
13783             return noAppend ? false : "append";
13784         }
13785         var noBelow = false;
13786         if(!this.allowParentInsert){
13787             noBelow = tn.hasChildNodes() && tn.isExpanded();
13788         }
13789         var q = (b - t) / (noAppend ? 2 : 3);
13790         if(y >= t && y < (t + q)){
13791             return "above";
13792         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13793             return "below";
13794         }else{
13795             return "append";
13796         }
13797     },
13798     
13799     onNodeEnter : function(n, dd, e, data)
13800     {
13801         this.cancelExpand();
13802     },
13803     
13804     onNodeOver : function(n, dd, e, data)
13805     {
13806        
13807         var pt = this.getDropPoint(e, n, dd);
13808         var node = n.node;
13809         
13810         // auto node expand check
13811         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13812             this.queueExpand(node);
13813         }else if(pt != "append"){
13814             this.cancelExpand();
13815         }
13816         
13817         // set the insert point style on the target node
13818         var returnCls = this.dropNotAllowed;
13819         if(this.isValidDropPoint(n, pt, dd, e, data)){
13820            if(pt){
13821                var el = n.ddel;
13822                var cls;
13823                if(pt == "above"){
13824                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13825                    cls = "x-tree-drag-insert-above";
13826                }else if(pt == "below"){
13827                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13828                    cls = "x-tree-drag-insert-below";
13829                }else{
13830                    returnCls = "x-tree-drop-ok-append";
13831                    cls = "x-tree-drag-append";
13832                }
13833                if(this.lastInsertClass != cls){
13834                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13835                    this.lastInsertClass = cls;
13836                }
13837            }
13838        }
13839        return returnCls;
13840     },
13841     
13842     onNodeOut : function(n, dd, e, data){
13843         
13844         this.cancelExpand();
13845         this.removeDropIndicators(n);
13846     },
13847     
13848     onNodeDrop : function(n, dd, e, data){
13849         var point = this.getDropPoint(e, n, dd);
13850         var targetNode = n.node;
13851         targetNode.ui.startDrop();
13852         if(!this.isValidDropPoint(n, point, dd, e, data)){
13853             targetNode.ui.endDrop();
13854             return false;
13855         }
13856         // first try to find the drop node
13857         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13858         var dropEvent = {
13859             tree : this.tree,
13860             target: targetNode,
13861             data: data,
13862             point: point,
13863             source: dd,
13864             rawEvent: e,
13865             dropNode: dropNode,
13866             cancel: !dropNode   
13867         };
13868         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13869         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13870             targetNode.ui.endDrop();
13871             return false;
13872         }
13873         // allow target changing
13874         targetNode = dropEvent.target;
13875         if(point == "append" && !targetNode.isExpanded()){
13876             targetNode.expand(false, null, function(){
13877                 this.completeDrop(dropEvent);
13878             }.createDelegate(this));
13879         }else{
13880             this.completeDrop(dropEvent);
13881         }
13882         return true;
13883     },
13884     
13885     completeDrop : function(de){
13886         var ns = de.dropNode, p = de.point, t = de.target;
13887         if(!(ns instanceof Array)){
13888             ns = [ns];
13889         }
13890         var n;
13891         for(var i = 0, len = ns.length; i < len; i++){
13892             n = ns[i];
13893             if(p == "above"){
13894                 t.parentNode.insertBefore(n, t);
13895             }else if(p == "below"){
13896                 t.parentNode.insertBefore(n, t.nextSibling);
13897             }else{
13898                 t.appendChild(n);
13899             }
13900         }
13901         n.ui.focus();
13902         if(this.tree.hlDrop){
13903             n.ui.highlight();
13904         }
13905         t.ui.endDrop();
13906         this.tree.fireEvent("nodedrop", de);
13907     },
13908     
13909     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13910         if(this.tree.hlDrop){
13911             dropNode.ui.focus();
13912             dropNode.ui.highlight();
13913         }
13914         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13915     },
13916     
13917     getTree : function(){
13918         return this.tree;
13919     },
13920     
13921     removeDropIndicators : function(n){
13922         if(n && n.ddel){
13923             var el = n.ddel;
13924             Roo.fly(el).removeClass([
13925                     "x-tree-drag-insert-above",
13926                     "x-tree-drag-insert-below",
13927                     "x-tree-drag-append"]);
13928             this.lastInsertClass = "_noclass";
13929         }
13930     },
13931     
13932     beforeDragDrop : function(target, e, id){
13933         this.cancelExpand();
13934         return true;
13935     },
13936     
13937     afterRepair : function(data){
13938         if(data && Roo.enableFx){
13939             data.node.ui.highlight();
13940         }
13941         this.hideProxy();
13942     } 
13943     
13944 });
13945
13946 }
13947 /*
13948  * Based on:
13949  * Ext JS Library 1.1.1
13950  * Copyright(c) 2006-2007, Ext JS, LLC.
13951  *
13952  * Originally Released Under LGPL - original licence link has changed is not relivant.
13953  *
13954  * Fork - LGPL
13955  * <script type="text/javascript">
13956  */
13957  
13958
13959 if(Roo.dd.DragZone){
13960 Roo.tree.TreeDragZone = function(tree, config){
13961     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13962     this.tree = tree;
13963 };
13964
13965 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13966     ddGroup : "TreeDD",
13967    
13968     onBeforeDrag : function(data, e){
13969         var n = data.node;
13970         return n && n.draggable && !n.disabled;
13971     },
13972      
13973     
13974     onInitDrag : function(e){
13975         var data = this.dragData;
13976         this.tree.getSelectionModel().select(data.node);
13977         this.proxy.update("");
13978         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13979         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13980     },
13981     
13982     getRepairXY : function(e, data){
13983         return data.node.ui.getDDRepairXY();
13984     },
13985     
13986     onEndDrag : function(data, e){
13987         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13988         
13989         
13990     },
13991     
13992     onValidDrop : function(dd, e, id){
13993         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13994         this.hideProxy();
13995     },
13996     
13997     beforeInvalidDrop : function(e, id){
13998         // this scrolls the original position back into view
13999         var sm = this.tree.getSelectionModel();
14000         sm.clearSelections();
14001         sm.select(this.dragData.node);
14002     }
14003 });
14004 }/*
14005  * Based on:
14006  * Ext JS Library 1.1.1
14007  * Copyright(c) 2006-2007, Ext JS, LLC.
14008  *
14009  * Originally Released Under LGPL - original licence link has changed is not relivant.
14010  *
14011  * Fork - LGPL
14012  * <script type="text/javascript">
14013  */
14014 /**
14015  * @class Roo.tree.TreeEditor
14016  * @extends Roo.Editor
14017  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14018  * as the editor field.
14019  * @constructor
14020  * @param {Object} config (used to be the tree panel.)
14021  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14022  * 
14023  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14024  * @cfg {Roo.form.TextField} field [required] The field configuration
14025  *
14026  * 
14027  */
14028 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14029     var tree = config;
14030     var field;
14031     if (oldconfig) { // old style..
14032         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14033     } else {
14034         // new style..
14035         tree = config.tree;
14036         config.field = config.field  || {};
14037         config.field.xtype = 'TextField';
14038         field = Roo.factory(config.field, Roo.form);
14039     }
14040     config = config || {};
14041     
14042     
14043     this.addEvents({
14044         /**
14045          * @event beforenodeedit
14046          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14047          * false from the handler of this event.
14048          * @param {Editor} this
14049          * @param {Roo.tree.Node} node 
14050          */
14051         "beforenodeedit" : true
14052     });
14053     
14054     //Roo.log(config);
14055     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14056
14057     this.tree = tree;
14058
14059     tree.on('beforeclick', this.beforeNodeClick, this);
14060     tree.getTreeEl().on('mousedown', this.hide, this);
14061     this.on('complete', this.updateNode, this);
14062     this.on('beforestartedit', this.fitToTree, this);
14063     this.on('startedit', this.bindScroll, this, {delay:10});
14064     this.on('specialkey', this.onSpecialKey, this);
14065 };
14066
14067 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14068     /**
14069      * @cfg {String} alignment
14070      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14071      */
14072     alignment: "l-l",
14073     // inherit
14074     autoSize: false,
14075     /**
14076      * @cfg {Boolean} hideEl
14077      * True to hide the bound element while the editor is displayed (defaults to false)
14078      */
14079     hideEl : false,
14080     /**
14081      * @cfg {String} cls
14082      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14083      */
14084     cls: "x-small-editor x-tree-editor",
14085     /**
14086      * @cfg {Boolean} shim
14087      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14088      */
14089     shim:false,
14090     // inherit
14091     shadow:"frame",
14092     /**
14093      * @cfg {Number} maxWidth
14094      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14095      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14096      * scroll and client offsets into account prior to each edit.
14097      */
14098     maxWidth: 250,
14099
14100     editDelay : 350,
14101
14102     // private
14103     fitToTree : function(ed, el){
14104         var td = this.tree.getTreeEl().dom, nd = el.dom;
14105         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14106             td.scrollLeft = nd.offsetLeft;
14107         }
14108         var w = Math.min(
14109                 this.maxWidth,
14110                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14111         this.setSize(w, '');
14112         
14113         return this.fireEvent('beforenodeedit', this, this.editNode);
14114         
14115     },
14116
14117     // private
14118     triggerEdit : function(node){
14119         this.completeEdit();
14120         this.editNode = node;
14121         this.startEdit(node.ui.textNode, node.text);
14122     },
14123
14124     // private
14125     bindScroll : function(){
14126         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14127     },
14128
14129     // private
14130     beforeNodeClick : function(node, e){
14131         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14132         this.lastClick = new Date();
14133         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14134             e.stopEvent();
14135             this.triggerEdit(node);
14136             return false;
14137         }
14138         return true;
14139     },
14140
14141     // private
14142     updateNode : function(ed, value){
14143         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14144         this.editNode.setText(value);
14145     },
14146
14147     // private
14148     onHide : function(){
14149         Roo.tree.TreeEditor.superclass.onHide.call(this);
14150         if(this.editNode){
14151             this.editNode.ui.focus();
14152         }
14153     },
14154
14155     // private
14156     onSpecialKey : function(field, e){
14157         var k = e.getKey();
14158         if(k == e.ESC){
14159             e.stopEvent();
14160             this.cancelEdit();
14161         }else if(k == e.ENTER && !e.hasModifier()){
14162             e.stopEvent();
14163             this.completeEdit();
14164         }
14165     }
14166 });//<Script type="text/javascript">
14167 /*
14168  * Based on:
14169  * Ext JS Library 1.1.1
14170  * Copyright(c) 2006-2007, Ext JS, LLC.
14171  *
14172  * Originally Released Under LGPL - original licence link has changed is not relivant.
14173  *
14174  * Fork - LGPL
14175  * <script type="text/javascript">
14176  */
14177  
14178 /**
14179  * Not documented??? - probably should be...
14180  */
14181
14182 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14183     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14184     
14185     renderElements : function(n, a, targetNode, bulkRender){
14186         //consel.log("renderElements?");
14187         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14188
14189         var t = n.getOwnerTree();
14190         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14191         
14192         var cols = t.columns;
14193         var bw = t.borderWidth;
14194         var c = cols[0];
14195         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14196          var cb = typeof a.checked == "boolean";
14197         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14198         var colcls = 'x-t-' + tid + '-c0';
14199         var buf = [
14200             '<li class="x-tree-node">',
14201             
14202                 
14203                 '<div class="x-tree-node-el ', a.cls,'">',
14204                     // extran...
14205                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14206                 
14207                 
14208                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14209                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14210                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14211                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14212                            (a.iconCls ? ' '+a.iconCls : ''),
14213                            '" unselectable="on" />',
14214                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14215                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14216                              
14217                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14218                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14219                             '<span unselectable="on" qtip="' + tx + '">',
14220                              tx,
14221                              '</span></a>' ,
14222                     '</div>',
14223                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14224                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14225                  ];
14226         for(var i = 1, len = cols.length; i < len; i++){
14227             c = cols[i];
14228             colcls = 'x-t-' + tid + '-c' +i;
14229             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14230             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14231                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14232                       "</div>");
14233          }
14234          
14235          buf.push(
14236             '</a>',
14237             '<div class="x-clear"></div></div>',
14238             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14239             "</li>");
14240         
14241         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14242             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14243                                 n.nextSibling.ui.getEl(), buf.join(""));
14244         }else{
14245             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14246         }
14247         var el = this.wrap.firstChild;
14248         this.elRow = el;
14249         this.elNode = el.firstChild;
14250         this.ranchor = el.childNodes[1];
14251         this.ctNode = this.wrap.childNodes[1];
14252         var cs = el.firstChild.childNodes;
14253         this.indentNode = cs[0];
14254         this.ecNode = cs[1];
14255         this.iconNode = cs[2];
14256         var index = 3;
14257         if(cb){
14258             this.checkbox = cs[3];
14259             index++;
14260         }
14261         this.anchor = cs[index];
14262         
14263         this.textNode = cs[index].firstChild;
14264         
14265         //el.on("click", this.onClick, this);
14266         //el.on("dblclick", this.onDblClick, this);
14267         
14268         
14269        // console.log(this);
14270     },
14271     initEvents : function(){
14272         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14273         
14274             
14275         var a = this.ranchor;
14276
14277         var el = Roo.get(a);
14278
14279         if(Roo.isOpera){ // opera render bug ignores the CSS
14280             el.setStyle("text-decoration", "none");
14281         }
14282
14283         el.on("click", this.onClick, this);
14284         el.on("dblclick", this.onDblClick, this);
14285         el.on("contextmenu", this.onContextMenu, this);
14286         
14287     },
14288     
14289     /*onSelectedChange : function(state){
14290         if(state){
14291             this.focus();
14292             this.addClass("x-tree-selected");
14293         }else{
14294             //this.blur();
14295             this.removeClass("x-tree-selected");
14296         }
14297     },*/
14298     addClass : function(cls){
14299         if(this.elRow){
14300             Roo.fly(this.elRow).addClass(cls);
14301         }
14302         
14303     },
14304     
14305     
14306     removeClass : function(cls){
14307         if(this.elRow){
14308             Roo.fly(this.elRow).removeClass(cls);
14309         }
14310     }
14311
14312     
14313     
14314 });//<Script type="text/javascript">
14315
14316 /*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326  
14327
14328 /**
14329  * @class Roo.tree.ColumnTree
14330  * @extends Roo.tree.TreePanel
14331  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14332  * @cfg {int} borderWidth  compined right/left border allowance
14333  * @constructor
14334  * @param {String/HTMLElement/Element} el The container element
14335  * @param {Object} config
14336  */
14337 Roo.tree.ColumnTree =  function(el, config)
14338 {
14339    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14340    this.addEvents({
14341         /**
14342         * @event resize
14343         * Fire this event on a container when it resizes
14344         * @param {int} w Width
14345         * @param {int} h Height
14346         */
14347        "resize" : true
14348     });
14349     this.on('resize', this.onResize, this);
14350 };
14351
14352 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14353     //lines:false,
14354     
14355     
14356     borderWidth: Roo.isBorderBox ? 0 : 2, 
14357     headEls : false,
14358     
14359     render : function(){
14360         // add the header.....
14361        
14362         Roo.tree.ColumnTree.superclass.render.apply(this);
14363         
14364         this.el.addClass('x-column-tree');
14365         
14366         this.headers = this.el.createChild(
14367             {cls:'x-tree-headers'},this.innerCt.dom);
14368    
14369         var cols = this.columns, c;
14370         var totalWidth = 0;
14371         this.headEls = [];
14372         var  len = cols.length;
14373         for(var i = 0; i < len; i++){
14374              c = cols[i];
14375              totalWidth += c.width;
14376             this.headEls.push(this.headers.createChild({
14377                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14378                  cn: {
14379                      cls:'x-tree-hd-text',
14380                      html: c.header
14381                  },
14382                  style:'width:'+(c.width-this.borderWidth)+'px;'
14383              }));
14384         }
14385         this.headers.createChild({cls:'x-clear'});
14386         // prevent floats from wrapping when clipped
14387         this.headers.setWidth(totalWidth);
14388         //this.innerCt.setWidth(totalWidth);
14389         this.innerCt.setStyle({ overflow: 'auto' });
14390         this.onResize(this.width, this.height);
14391              
14392         
14393     },
14394     onResize : function(w,h)
14395     {
14396         this.height = h;
14397         this.width = w;
14398         // resize cols..
14399         this.innerCt.setWidth(this.width);
14400         this.innerCt.setHeight(this.height-20);
14401         
14402         // headers...
14403         var cols = this.columns, c;
14404         var totalWidth = 0;
14405         var expEl = false;
14406         var len = cols.length;
14407         for(var i = 0; i < len; i++){
14408             c = cols[i];
14409             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14410                 // it's the expander..
14411                 expEl  = this.headEls[i];
14412                 continue;
14413             }
14414             totalWidth += c.width;
14415             
14416         }
14417         if (expEl) {
14418             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14419         }
14420         this.headers.setWidth(w-20);
14421
14422         
14423         
14424         
14425     }
14426 });
14427 /*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437  
14438 /**
14439  * @class Roo.menu.Menu
14440  * @extends Roo.util.Observable
14441  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14442  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14443  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14444  * @constructor
14445  * Creates a new Menu
14446  * @param {Object} config Configuration options
14447  */
14448 Roo.menu.Menu = function(config){
14449     
14450     Roo.menu.Menu.superclass.constructor.call(this, config);
14451     
14452     this.id = this.id || Roo.id();
14453     this.addEvents({
14454         /**
14455          * @event beforeshow
14456          * Fires before this menu is displayed
14457          * @param {Roo.menu.Menu} this
14458          */
14459         beforeshow : true,
14460         /**
14461          * @event beforehide
14462          * Fires before this menu is hidden
14463          * @param {Roo.menu.Menu} this
14464          */
14465         beforehide : true,
14466         /**
14467          * @event show
14468          * Fires after this menu is displayed
14469          * @param {Roo.menu.Menu} this
14470          */
14471         show : true,
14472         /**
14473          * @event hide
14474          * Fires after this menu is hidden
14475          * @param {Roo.menu.Menu} this
14476          */
14477         hide : true,
14478         /**
14479          * @event click
14480          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14481          * @param {Roo.menu.Menu} this
14482          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14483          * @param {Roo.EventObject} e
14484          */
14485         click : true,
14486         /**
14487          * @event mouseover
14488          * Fires when the mouse is hovering over this menu
14489          * @param {Roo.menu.Menu} this
14490          * @param {Roo.EventObject} e
14491          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14492          */
14493         mouseover : true,
14494         /**
14495          * @event mouseout
14496          * Fires when the mouse exits this menu
14497          * @param {Roo.menu.Menu} this
14498          * @param {Roo.EventObject} e
14499          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14500          */
14501         mouseout : true,
14502         /**
14503          * @event itemclick
14504          * Fires when a menu item contained in this menu is clicked
14505          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14506          * @param {Roo.EventObject} e
14507          */
14508         itemclick: true
14509     });
14510     if (this.registerMenu) {
14511         Roo.menu.MenuMgr.register(this);
14512     }
14513     
14514     var mis = this.items;
14515     this.items = new Roo.util.MixedCollection();
14516     if(mis){
14517         this.add.apply(this, mis);
14518     }
14519 };
14520
14521 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14522     /**
14523      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14524      */
14525     minWidth : 120,
14526     /**
14527      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14528      * for bottom-right shadow (defaults to "sides")
14529      */
14530     shadow : "sides",
14531     /**
14532      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14533      * this menu (defaults to "tl-tr?")
14534      */
14535     subMenuAlign : "tl-tr?",
14536     /**
14537      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14538      * relative to its element of origin (defaults to "tl-bl?")
14539      */
14540     defaultAlign : "tl-bl?",
14541     /**
14542      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14543      */
14544     allowOtherMenus : false,
14545     /**
14546      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14547      */
14548     registerMenu : true,
14549
14550     hidden:true,
14551
14552     // private
14553     render : function(){
14554         if(this.el){
14555             return;
14556         }
14557         var el = this.el = new Roo.Layer({
14558             cls: "x-menu",
14559             shadow:this.shadow,
14560             constrain: false,
14561             parentEl: this.parentEl || document.body,
14562             zindex:15000
14563         });
14564
14565         this.keyNav = new Roo.menu.MenuNav(this);
14566
14567         if(this.plain){
14568             el.addClass("x-menu-plain");
14569         }
14570         if(this.cls){
14571             el.addClass(this.cls);
14572         }
14573         // generic focus element
14574         this.focusEl = el.createChild({
14575             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14576         });
14577         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14578         //disabling touch- as it's causing issues ..
14579         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14580         ul.on('click'   , this.onClick, this);
14581         
14582         
14583         ul.on("mouseover", this.onMouseOver, this);
14584         ul.on("mouseout", this.onMouseOut, this);
14585         this.items.each(function(item){
14586             if (item.hidden) {
14587                 return;
14588             }
14589             
14590             var li = document.createElement("li");
14591             li.className = "x-menu-list-item";
14592             ul.dom.appendChild(li);
14593             item.render(li, this);
14594         }, this);
14595         this.ul = ul;
14596         this.autoWidth();
14597     },
14598
14599     // private
14600     autoWidth : function(){
14601         var el = this.el, ul = this.ul;
14602         if(!el){
14603             return;
14604         }
14605         var w = this.width;
14606         if(w){
14607             el.setWidth(w);
14608         }else if(Roo.isIE){
14609             el.setWidth(this.minWidth);
14610             var t = el.dom.offsetWidth; // force recalc
14611             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14612         }
14613     },
14614
14615     // private
14616     delayAutoWidth : function(){
14617         if(this.rendered){
14618             if(!this.awTask){
14619                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14620             }
14621             this.awTask.delay(20);
14622         }
14623     },
14624
14625     // private
14626     findTargetItem : function(e){
14627         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14628         if(t && t.menuItemId){
14629             return this.items.get(t.menuItemId);
14630         }
14631     },
14632
14633     // private
14634     onClick : function(e){
14635         Roo.log("menu.onClick");
14636         var t = this.findTargetItem(e);
14637         if(!t){
14638             return;
14639         }
14640         Roo.log(e);
14641         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14642             if(t == this.activeItem && t.shouldDeactivate(e)){
14643                 this.activeItem.deactivate();
14644                 delete this.activeItem;
14645                 return;
14646             }
14647             if(t.canActivate){
14648                 this.setActiveItem(t, true);
14649             }
14650             return;
14651             
14652             
14653         }
14654         
14655         t.onClick(e);
14656         this.fireEvent("click", this, t, e);
14657     },
14658
14659     // private
14660     setActiveItem : function(item, autoExpand){
14661         if(item != this.activeItem){
14662             if(this.activeItem){
14663                 this.activeItem.deactivate();
14664             }
14665             this.activeItem = item;
14666             item.activate(autoExpand);
14667         }else if(autoExpand){
14668             item.expandMenu();
14669         }
14670     },
14671
14672     // private
14673     tryActivate : function(start, step){
14674         var items = this.items;
14675         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14676             var item = items.get(i);
14677             if(!item.disabled && item.canActivate){
14678                 this.setActiveItem(item, false);
14679                 return item;
14680             }
14681         }
14682         return false;
14683     },
14684
14685     // private
14686     onMouseOver : function(e){
14687         var t;
14688         if(t = this.findTargetItem(e)){
14689             if(t.canActivate && !t.disabled){
14690                 this.setActiveItem(t, true);
14691             }
14692         }
14693         this.fireEvent("mouseover", this, e, t);
14694     },
14695
14696     // private
14697     onMouseOut : function(e){
14698         var t;
14699         if(t = this.findTargetItem(e)){
14700             if(t == this.activeItem && t.shouldDeactivate(e)){
14701                 this.activeItem.deactivate();
14702                 delete this.activeItem;
14703             }
14704         }
14705         this.fireEvent("mouseout", this, e, t);
14706     },
14707
14708     /**
14709      * Read-only.  Returns true if the menu is currently displayed, else false.
14710      * @type Boolean
14711      */
14712     isVisible : function(){
14713         return this.el && !this.hidden;
14714     },
14715
14716     /**
14717      * Displays this menu relative to another element
14718      * @param {String/HTMLElement/Roo.Element} element The element to align to
14719      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14720      * the element (defaults to this.defaultAlign)
14721      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14722      */
14723     show : function(el, pos, parentMenu){
14724         this.parentMenu = parentMenu;
14725         if(!this.el){
14726             this.render();
14727         }
14728         this.fireEvent("beforeshow", this);
14729         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14730     },
14731
14732     /**
14733      * Displays this menu at a specific xy position
14734      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14735      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14736      */
14737     showAt : function(xy, parentMenu, /* private: */_e){
14738         this.parentMenu = parentMenu;
14739         if(!this.el){
14740             this.render();
14741         }
14742         if(_e !== false){
14743             this.fireEvent("beforeshow", this);
14744             xy = this.el.adjustForConstraints(xy);
14745         }
14746         this.el.setXY(xy);
14747         this.el.show();
14748         this.hidden = false;
14749         this.focus();
14750         this.fireEvent("show", this);
14751     },
14752
14753     focus : function(){
14754         if(!this.hidden){
14755             this.doFocus.defer(50, this);
14756         }
14757     },
14758
14759     doFocus : function(){
14760         if(!this.hidden){
14761             this.focusEl.focus();
14762         }
14763     },
14764
14765     /**
14766      * Hides this menu and optionally all parent menus
14767      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14768      */
14769     hide : function(deep){
14770         if(this.el && this.isVisible()){
14771             this.fireEvent("beforehide", this);
14772             if(this.activeItem){
14773                 this.activeItem.deactivate();
14774                 this.activeItem = null;
14775             }
14776             this.el.hide();
14777             this.hidden = true;
14778             this.fireEvent("hide", this);
14779         }
14780         if(deep === true && this.parentMenu){
14781             this.parentMenu.hide(true);
14782         }
14783     },
14784
14785     /**
14786      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14787      * Any of the following are valid:
14788      * <ul>
14789      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14790      * <li>An HTMLElement object which will be converted to a menu item</li>
14791      * <li>A menu item config object that will be created as a new menu item</li>
14792      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14793      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14794      * </ul>
14795      * Usage:
14796      * <pre><code>
14797 // Create the menu
14798 var menu = new Roo.menu.Menu();
14799
14800 // Create a menu item to add by reference
14801 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14802
14803 // Add a bunch of items at once using different methods.
14804 // Only the last item added will be returned.
14805 var item = menu.add(
14806     menuItem,                // add existing item by ref
14807     'Dynamic Item',          // new TextItem
14808     '-',                     // new separator
14809     { text: 'Config Item' }  // new item by config
14810 );
14811 </code></pre>
14812      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14813      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14814      */
14815     add : function(){
14816         var a = arguments, l = a.length, item;
14817         for(var i = 0; i < l; i++){
14818             var el = a[i];
14819             if ((typeof(el) == "object") && el.xtype && el.xns) {
14820                 el = Roo.factory(el, Roo.menu);
14821             }
14822             
14823             if(el.render){ // some kind of Item
14824                 item = this.addItem(el);
14825             }else if(typeof el == "string"){ // string
14826                 if(el == "separator" || el == "-"){
14827                     item = this.addSeparator();
14828                 }else{
14829                     item = this.addText(el);
14830                 }
14831             }else if(el.tagName || el.el){ // element
14832                 item = this.addElement(el);
14833             }else if(typeof el == "object"){ // must be menu item config?
14834                 item = this.addMenuItem(el);
14835             }
14836         }
14837         return item;
14838     },
14839
14840     /**
14841      * Returns this menu's underlying {@link Roo.Element} object
14842      * @return {Roo.Element} The element
14843      */
14844     getEl : function(){
14845         if(!this.el){
14846             this.render();
14847         }
14848         return this.el;
14849     },
14850
14851     /**
14852      * Adds a separator bar to the menu
14853      * @return {Roo.menu.Item} The menu item that was added
14854      */
14855     addSeparator : function(){
14856         return this.addItem(new Roo.menu.Separator());
14857     },
14858
14859     /**
14860      * Adds an {@link Roo.Element} object to the menu
14861      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14862      * @return {Roo.menu.Item} The menu item that was added
14863      */
14864     addElement : function(el){
14865         return this.addItem(new Roo.menu.BaseItem(el));
14866     },
14867
14868     /**
14869      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14870      * @param {Roo.menu.Item} item The menu item to add
14871      * @return {Roo.menu.Item} The menu item that was added
14872      */
14873     addItem : function(item){
14874         this.items.add(item);
14875         if(this.ul){
14876             var li = document.createElement("li");
14877             li.className = "x-menu-list-item";
14878             this.ul.dom.appendChild(li);
14879             item.render(li, this);
14880             this.delayAutoWidth();
14881         }
14882         return item;
14883     },
14884
14885     /**
14886      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14887      * @param {Object} config A MenuItem config object
14888      * @return {Roo.menu.Item} The menu item that was added
14889      */
14890     addMenuItem : function(config){
14891         if(!(config instanceof Roo.menu.Item)){
14892             if(typeof config.checked == "boolean"){ // must be check menu item config?
14893                 config = new Roo.menu.CheckItem(config);
14894             }else{
14895                 config = new Roo.menu.Item(config);
14896             }
14897         }
14898         return this.addItem(config);
14899     },
14900
14901     /**
14902      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14903      * @param {String} text The text to display in the menu item
14904      * @return {Roo.menu.Item} The menu item that was added
14905      */
14906     addText : function(text){
14907         return this.addItem(new Roo.menu.TextItem({ text : text }));
14908     },
14909
14910     /**
14911      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14912      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14913      * @param {Roo.menu.Item} item The menu item to add
14914      * @return {Roo.menu.Item} The menu item that was added
14915      */
14916     insert : function(index, item){
14917         this.items.insert(index, item);
14918         if(this.ul){
14919             var li = document.createElement("li");
14920             li.className = "x-menu-list-item";
14921             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14922             item.render(li, this);
14923             this.delayAutoWidth();
14924         }
14925         return item;
14926     },
14927
14928     /**
14929      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14930      * @param {Roo.menu.Item} item The menu item to remove
14931      */
14932     remove : function(item){
14933         this.items.removeKey(item.id);
14934         item.destroy();
14935     },
14936
14937     /**
14938      * Removes and destroys all items in the menu
14939      */
14940     removeAll : function(){
14941         var f;
14942         while(f = this.items.first()){
14943             this.remove(f);
14944         }
14945     }
14946 });
14947
14948 // MenuNav is a private utility class used internally by the Menu
14949 Roo.menu.MenuNav = function(menu){
14950     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14951     this.scope = this.menu = menu;
14952 };
14953
14954 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14955     doRelay : function(e, h){
14956         var k = e.getKey();
14957         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14958             this.menu.tryActivate(0, 1);
14959             return false;
14960         }
14961         return h.call(this.scope || this, e, this.menu);
14962     },
14963
14964     up : function(e, m){
14965         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14966             m.tryActivate(m.items.length-1, -1);
14967         }
14968     },
14969
14970     down : function(e, m){
14971         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14972             m.tryActivate(0, 1);
14973         }
14974     },
14975
14976     right : function(e, m){
14977         if(m.activeItem){
14978             m.activeItem.expandMenu(true);
14979         }
14980     },
14981
14982     left : function(e, m){
14983         m.hide();
14984         if(m.parentMenu && m.parentMenu.activeItem){
14985             m.parentMenu.activeItem.activate();
14986         }
14987     },
14988
14989     enter : function(e, m){
14990         if(m.activeItem){
14991             e.stopPropagation();
14992             m.activeItem.onClick(e);
14993             m.fireEvent("click", this, m.activeItem);
14994             return true;
14995         }
14996     }
14997 });/*
14998  * Based on:
14999  * Ext JS Library 1.1.1
15000  * Copyright(c) 2006-2007, Ext JS, LLC.
15001  *
15002  * Originally Released Under LGPL - original licence link has changed is not relivant.
15003  *
15004  * Fork - LGPL
15005  * <script type="text/javascript">
15006  */
15007  
15008 /**
15009  * @class Roo.menu.MenuMgr
15010  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15011  * @static
15012  */
15013 Roo.menu.MenuMgr = function(){
15014    var menus, active, groups = {}, attached = false, lastShow = new Date();
15015
15016    // private - called when first menu is created
15017    function init(){
15018        menus = {};
15019        active = new Roo.util.MixedCollection();
15020        Roo.get(document).addKeyListener(27, function(){
15021            if(active.length > 0){
15022                hideAll();
15023            }
15024        });
15025    }
15026
15027    // private
15028    function hideAll(){
15029        if(active && active.length > 0){
15030            var c = active.clone();
15031            c.each(function(m){
15032                m.hide();
15033            });
15034        }
15035    }
15036
15037    // private
15038    function onHide(m){
15039        active.remove(m);
15040        if(active.length < 1){
15041            Roo.get(document).un("mousedown", onMouseDown);
15042            attached = false;
15043        }
15044    }
15045
15046    // private
15047    function onShow(m){
15048        var last = active.last();
15049        lastShow = new Date();
15050        active.add(m);
15051        if(!attached){
15052            Roo.get(document).on("mousedown", onMouseDown);
15053            attached = true;
15054        }
15055        if(m.parentMenu){
15056           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15057           m.parentMenu.activeChild = m;
15058        }else if(last && last.isVisible()){
15059           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15060        }
15061    }
15062
15063    // private
15064    function onBeforeHide(m){
15065        if(m.activeChild){
15066            m.activeChild.hide();
15067        }
15068        if(m.autoHideTimer){
15069            clearTimeout(m.autoHideTimer);
15070            delete m.autoHideTimer;
15071        }
15072    }
15073
15074    // private
15075    function onBeforeShow(m){
15076        var pm = m.parentMenu;
15077        if(!pm && !m.allowOtherMenus){
15078            hideAll();
15079        }else if(pm && pm.activeChild && active != m){
15080            pm.activeChild.hide();
15081        }
15082    }
15083
15084    // private
15085    function onMouseDown(e){
15086        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15087            hideAll();
15088        }
15089    }
15090
15091    // private
15092    function onBeforeCheck(mi, state){
15093        if(state){
15094            var g = groups[mi.group];
15095            for(var i = 0, l = g.length; i < l; i++){
15096                if(g[i] != mi){
15097                    g[i].setChecked(false);
15098                }
15099            }
15100        }
15101    }
15102
15103    return {
15104
15105        /**
15106         * Hides all menus that are currently visible
15107         */
15108        hideAll : function(){
15109             hideAll();  
15110        },
15111
15112        // private
15113        register : function(menu){
15114            if(!menus){
15115                init();
15116            }
15117            menus[menu.id] = menu;
15118            menu.on("beforehide", onBeforeHide);
15119            menu.on("hide", onHide);
15120            menu.on("beforeshow", onBeforeShow);
15121            menu.on("show", onShow);
15122            var g = menu.group;
15123            if(g && menu.events["checkchange"]){
15124                if(!groups[g]){
15125                    groups[g] = [];
15126                }
15127                groups[g].push(menu);
15128                menu.on("checkchange", onCheck);
15129            }
15130        },
15131
15132         /**
15133          * Returns a {@link Roo.menu.Menu} object
15134          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15135          * be used to generate and return a new Menu instance.
15136          */
15137        get : function(menu){
15138            if(typeof menu == "string"){ // menu id
15139                return menus[menu];
15140            }else if(menu.events){  // menu instance
15141                return menu;
15142            }else if(typeof menu.length == 'number'){ // array of menu items?
15143                return new Roo.menu.Menu({items:menu});
15144            }else{ // otherwise, must be a config
15145                return new Roo.menu.Menu(menu);
15146            }
15147        },
15148
15149        // private
15150        unregister : function(menu){
15151            delete menus[menu.id];
15152            menu.un("beforehide", onBeforeHide);
15153            menu.un("hide", onHide);
15154            menu.un("beforeshow", onBeforeShow);
15155            menu.un("show", onShow);
15156            var g = menu.group;
15157            if(g && menu.events["checkchange"]){
15158                groups[g].remove(menu);
15159                menu.un("checkchange", onCheck);
15160            }
15161        },
15162
15163        // private
15164        registerCheckable : function(menuItem){
15165            var g = menuItem.group;
15166            if(g){
15167                if(!groups[g]){
15168                    groups[g] = [];
15169                }
15170                groups[g].push(menuItem);
15171                menuItem.on("beforecheckchange", onBeforeCheck);
15172            }
15173        },
15174
15175        // private
15176        unregisterCheckable : function(menuItem){
15177            var g = menuItem.group;
15178            if(g){
15179                groups[g].remove(menuItem);
15180                menuItem.un("beforecheckchange", onBeforeCheck);
15181            }
15182        }
15183    };
15184 }();/*
15185  * Based on:
15186  * Ext JS Library 1.1.1
15187  * Copyright(c) 2006-2007, Ext JS, LLC.
15188  *
15189  * Originally Released Under LGPL - original licence link has changed is not relivant.
15190  *
15191  * Fork - LGPL
15192  * <script type="text/javascript">
15193  */
15194  
15195
15196 /**
15197  * @class Roo.menu.BaseItem
15198  * @extends Roo.Component
15199  * @abstract
15200  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15201  * management and base configuration options shared by all menu components.
15202  * @constructor
15203  * Creates a new BaseItem
15204  * @param {Object} config Configuration options
15205  */
15206 Roo.menu.BaseItem = function(config){
15207     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15208
15209     this.addEvents({
15210         /**
15211          * @event click
15212          * Fires when this item is clicked
15213          * @param {Roo.menu.BaseItem} this
15214          * @param {Roo.EventObject} e
15215          */
15216         click: true,
15217         /**
15218          * @event activate
15219          * Fires when this item is activated
15220          * @param {Roo.menu.BaseItem} this
15221          */
15222         activate : true,
15223         /**
15224          * @event deactivate
15225          * Fires when this item is deactivated
15226          * @param {Roo.menu.BaseItem} this
15227          */
15228         deactivate : true
15229     });
15230
15231     if(this.handler){
15232         this.on("click", this.handler, this.scope, true);
15233     }
15234 };
15235
15236 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15237     /**
15238      * @cfg {Function} handler
15239      * A function that will handle the click event of this menu item (defaults to undefined)
15240      */
15241     /**
15242      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15243      */
15244     canActivate : false,
15245     
15246      /**
15247      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15248      */
15249     hidden: false,
15250     
15251     /**
15252      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15253      */
15254     activeClass : "x-menu-item-active",
15255     /**
15256      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15257      */
15258     hideOnClick : true,
15259     /**
15260      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15261      */
15262     hideDelay : 100,
15263
15264     // private
15265     ctype: "Roo.menu.BaseItem",
15266
15267     // private
15268     actionMode : "container",
15269
15270     // private
15271     render : function(container, parentMenu){
15272         this.parentMenu = parentMenu;
15273         Roo.menu.BaseItem.superclass.render.call(this, container);
15274         this.container.menuItemId = this.id;
15275     },
15276
15277     // private
15278     onRender : function(container, position){
15279         this.el = Roo.get(this.el);
15280         container.dom.appendChild(this.el.dom);
15281     },
15282
15283     // private
15284     onClick : function(e){
15285         if(!this.disabled && this.fireEvent("click", this, e) !== false
15286                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15287             this.handleClick(e);
15288         }else{
15289             e.stopEvent();
15290         }
15291     },
15292
15293     // private
15294     activate : function(){
15295         if(this.disabled){
15296             return false;
15297         }
15298         var li = this.container;
15299         li.addClass(this.activeClass);
15300         this.region = li.getRegion().adjust(2, 2, -2, -2);
15301         this.fireEvent("activate", this);
15302         return true;
15303     },
15304
15305     // private
15306     deactivate : function(){
15307         this.container.removeClass(this.activeClass);
15308         this.fireEvent("deactivate", this);
15309     },
15310
15311     // private
15312     shouldDeactivate : function(e){
15313         return !this.region || !this.region.contains(e.getPoint());
15314     },
15315
15316     // private
15317     handleClick : function(e){
15318         if(this.hideOnClick){
15319             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15320         }
15321     },
15322
15323     // private
15324     expandMenu : function(autoActivate){
15325         // do nothing
15326     },
15327
15328     // private
15329     hideMenu : function(){
15330         // do nothing
15331     }
15332 });/*
15333  * Based on:
15334  * Ext JS Library 1.1.1
15335  * Copyright(c) 2006-2007, Ext JS, LLC.
15336  *
15337  * Originally Released Under LGPL - original licence link has changed is not relivant.
15338  *
15339  * Fork - LGPL
15340  * <script type="text/javascript">
15341  */
15342  
15343 /**
15344  * @class Roo.menu.Adapter
15345  * @extends Roo.menu.BaseItem
15346  * @abstract
15347  * 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.
15348  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15349  * @constructor
15350  * Creates a new Adapter
15351  * @param {Object} config Configuration options
15352  */
15353 Roo.menu.Adapter = function(component, config){
15354     Roo.menu.Adapter.superclass.constructor.call(this, config);
15355     this.component = component;
15356 };
15357 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15358     // private
15359     canActivate : true,
15360
15361     // private
15362     onRender : function(container, position){
15363         this.component.render(container);
15364         this.el = this.component.getEl();
15365     },
15366
15367     // private
15368     activate : function(){
15369         if(this.disabled){
15370             return false;
15371         }
15372         this.component.focus();
15373         this.fireEvent("activate", this);
15374         return true;
15375     },
15376
15377     // private
15378     deactivate : function(){
15379         this.fireEvent("deactivate", this);
15380     },
15381
15382     // private
15383     disable : function(){
15384         this.component.disable();
15385         Roo.menu.Adapter.superclass.disable.call(this);
15386     },
15387
15388     // private
15389     enable : function(){
15390         this.component.enable();
15391         Roo.menu.Adapter.superclass.enable.call(this);
15392     }
15393 });/*
15394  * Based on:
15395  * Ext JS Library 1.1.1
15396  * Copyright(c) 2006-2007, Ext JS, LLC.
15397  *
15398  * Originally Released Under LGPL - original licence link has changed is not relivant.
15399  *
15400  * Fork - LGPL
15401  * <script type="text/javascript">
15402  */
15403
15404 /**
15405  * @class Roo.menu.TextItem
15406  * @extends Roo.menu.BaseItem
15407  * Adds a static text string to a menu, usually used as either a heading or group separator.
15408  * Note: old style constructor with text is still supported.
15409  * 
15410  * @constructor
15411  * Creates a new TextItem
15412  * @param {Object} cfg Configuration
15413  */
15414 Roo.menu.TextItem = function(cfg){
15415     if (typeof(cfg) == 'string') {
15416         this.text = cfg;
15417     } else {
15418         Roo.apply(this,cfg);
15419     }
15420     
15421     Roo.menu.TextItem.superclass.constructor.call(this);
15422 };
15423
15424 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15425     /**
15426      * @cfg {String} text Text to show on item.
15427      */
15428     text : '',
15429     
15430     /**
15431      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15432      */
15433     hideOnClick : false,
15434     /**
15435      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15436      */
15437     itemCls : "x-menu-text",
15438
15439     // private
15440     onRender : function(){
15441         var s = document.createElement("span");
15442         s.className = this.itemCls;
15443         s.innerHTML = this.text;
15444         this.el = s;
15445         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15446     }
15447 });/*
15448  * Based on:
15449  * Ext JS Library 1.1.1
15450  * Copyright(c) 2006-2007, Ext JS, LLC.
15451  *
15452  * Originally Released Under LGPL - original licence link has changed is not relivant.
15453  *
15454  * Fork - LGPL
15455  * <script type="text/javascript">
15456  */
15457
15458 /**
15459  * @class Roo.menu.Separator
15460  * @extends Roo.menu.BaseItem
15461  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15462  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15463  * @constructor
15464  * @param {Object} config Configuration options
15465  */
15466 Roo.menu.Separator = function(config){
15467     Roo.menu.Separator.superclass.constructor.call(this, config);
15468 };
15469
15470 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15471     /**
15472      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15473      */
15474     itemCls : "x-menu-sep",
15475     /**
15476      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15477      */
15478     hideOnClick : false,
15479
15480     // private
15481     onRender : function(li){
15482         var s = document.createElement("span");
15483         s.className = this.itemCls;
15484         s.innerHTML = "&#160;";
15485         this.el = s;
15486         li.addClass("x-menu-sep-li");
15487         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15488     }
15489 });/*
15490  * Based on:
15491  * Ext JS Library 1.1.1
15492  * Copyright(c) 2006-2007, Ext JS, LLC.
15493  *
15494  * Originally Released Under LGPL - original licence link has changed is not relivant.
15495  *
15496  * Fork - LGPL
15497  * <script type="text/javascript">
15498  */
15499 /**
15500  * @class Roo.menu.Item
15501  * @extends Roo.menu.BaseItem
15502  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15503  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15504  * activation and click handling.
15505  * @constructor
15506  * Creates a new Item
15507  * @param {Object} config Configuration options
15508  */
15509 Roo.menu.Item = function(config){
15510     Roo.menu.Item.superclass.constructor.call(this, config);
15511     if(this.menu){
15512         this.menu = Roo.menu.MenuMgr.get(this.menu);
15513     }
15514 };
15515 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15516     /**
15517      * @cfg {Roo.menu.Menu} menu
15518      * A Sub menu
15519      */
15520     /**
15521      * @cfg {String} text
15522      * The text to show on the menu item.
15523      */
15524     text: '',
15525      /**
15526      * @cfg {String} html to render in menu
15527      * The text to show on the menu item (HTML version).
15528      */
15529     html: '',
15530     /**
15531      * @cfg {String} icon
15532      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15533      */
15534     icon: undefined,
15535     /**
15536      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15537      */
15538     itemCls : "x-menu-item",
15539     /**
15540      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15541      */
15542     canActivate : true,
15543     /**
15544      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15545      */
15546     showDelay: 200,
15547     // doc'd in BaseItem
15548     hideDelay: 200,
15549
15550     // private
15551     ctype: "Roo.menu.Item",
15552     
15553     // private
15554     onRender : function(container, position){
15555         var el = document.createElement("a");
15556         el.hideFocus = true;
15557         el.unselectable = "on";
15558         el.href = this.href || "#";
15559         if(this.hrefTarget){
15560             el.target = this.hrefTarget;
15561         }
15562         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15563         
15564         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15565         
15566         el.innerHTML = String.format(
15567                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15568                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15569         this.el = el;
15570         Roo.menu.Item.superclass.onRender.call(this, container, position);
15571     },
15572
15573     /**
15574      * Sets the text to display in this menu item
15575      * @param {String} text The text to display
15576      * @param {Boolean} isHTML true to indicate text is pure html.
15577      */
15578     setText : function(text, isHTML){
15579         if (isHTML) {
15580             this.html = text;
15581         } else {
15582             this.text = text;
15583             this.html = '';
15584         }
15585         if(this.rendered){
15586             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15587      
15588             this.el.update(String.format(
15589                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15590                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15591             this.parentMenu.autoWidth();
15592         }
15593     },
15594
15595     // private
15596     handleClick : function(e){
15597         if(!this.href){ // if no link defined, stop the event automatically
15598             e.stopEvent();
15599         }
15600         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15601     },
15602
15603     // private
15604     activate : function(autoExpand){
15605         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15606             this.focus();
15607             if(autoExpand){
15608                 this.expandMenu();
15609             }
15610         }
15611         return true;
15612     },
15613
15614     // private
15615     shouldDeactivate : function(e){
15616         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15617             if(this.menu && this.menu.isVisible()){
15618                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15619             }
15620             return true;
15621         }
15622         return false;
15623     },
15624
15625     // private
15626     deactivate : function(){
15627         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15628         this.hideMenu();
15629     },
15630
15631     // private
15632     expandMenu : function(autoActivate){
15633         if(!this.disabled && this.menu){
15634             clearTimeout(this.hideTimer);
15635             delete this.hideTimer;
15636             if(!this.menu.isVisible() && !this.showTimer){
15637                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15638             }else if (this.menu.isVisible() && autoActivate){
15639                 this.menu.tryActivate(0, 1);
15640             }
15641         }
15642     },
15643
15644     // private
15645     deferExpand : function(autoActivate){
15646         delete this.showTimer;
15647         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15648         if(autoActivate){
15649             this.menu.tryActivate(0, 1);
15650         }
15651     },
15652
15653     // private
15654     hideMenu : function(){
15655         clearTimeout(this.showTimer);
15656         delete this.showTimer;
15657         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15658             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15659         }
15660     },
15661
15662     // private
15663     deferHide : function(){
15664         delete this.hideTimer;
15665         this.menu.hide();
15666     }
15667 });/*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677  
15678 /**
15679  * @class Roo.menu.CheckItem
15680  * @extends Roo.menu.Item
15681  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15682  * @constructor
15683  * Creates a new CheckItem
15684  * @param {Object} config Configuration options
15685  */
15686 Roo.menu.CheckItem = function(config){
15687     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15688     this.addEvents({
15689         /**
15690          * @event beforecheckchange
15691          * Fires before the checked value is set, providing an opportunity to cancel if needed
15692          * @param {Roo.menu.CheckItem} this
15693          * @param {Boolean} checked The new checked value that will be set
15694          */
15695         "beforecheckchange" : true,
15696         /**
15697          * @event checkchange
15698          * Fires after the checked value has been set
15699          * @param {Roo.menu.CheckItem} this
15700          * @param {Boolean} checked The checked value that was set
15701          */
15702         "checkchange" : true
15703     });
15704     if(this.checkHandler){
15705         this.on('checkchange', this.checkHandler, this.scope);
15706     }
15707 };
15708 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15709     /**
15710      * @cfg {String} group
15711      * All check items with the same group name will automatically be grouped into a single-select
15712      * radio button group (defaults to '')
15713      */
15714     /**
15715      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15716      */
15717     itemCls : "x-menu-item x-menu-check-item",
15718     /**
15719      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15720      */
15721     groupClass : "x-menu-group-item",
15722
15723     /**
15724      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15725      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15726      * initialized with checked = true will be rendered as checked.
15727      */
15728     checked: false,
15729
15730     // private
15731     ctype: "Roo.menu.CheckItem",
15732
15733     // private
15734     onRender : function(c){
15735         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15736         if(this.group){
15737             this.el.addClass(this.groupClass);
15738         }
15739         Roo.menu.MenuMgr.registerCheckable(this);
15740         if(this.checked){
15741             this.checked = false;
15742             this.setChecked(true, true);
15743         }
15744     },
15745
15746     // private
15747     destroy : function(){
15748         if(this.rendered){
15749             Roo.menu.MenuMgr.unregisterCheckable(this);
15750         }
15751         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15752     },
15753
15754     /**
15755      * Set the checked state of this item
15756      * @param {Boolean} checked The new checked value
15757      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15758      */
15759     setChecked : function(state, suppressEvent){
15760         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15761             if(this.container){
15762                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15763             }
15764             this.checked = state;
15765             if(suppressEvent !== true){
15766                 this.fireEvent("checkchange", this, state);
15767             }
15768         }
15769     },
15770
15771     // private
15772     handleClick : function(e){
15773        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15774            this.setChecked(!this.checked);
15775        }
15776        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15777     }
15778 });/*
15779  * Based on:
15780  * Ext JS Library 1.1.1
15781  * Copyright(c) 2006-2007, Ext JS, LLC.
15782  *
15783  * Originally Released Under LGPL - original licence link has changed is not relivant.
15784  *
15785  * Fork - LGPL
15786  * <script type="text/javascript">
15787  */
15788  
15789 /**
15790  * @class Roo.menu.DateItem
15791  * @extends Roo.menu.Adapter
15792  * A menu item that wraps the {@link Roo.DatPicker} component.
15793  * @constructor
15794  * Creates a new DateItem
15795  * @param {Object} config Configuration options
15796  */
15797 Roo.menu.DateItem = function(config){
15798     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15799     /** The Roo.DatePicker object @type Roo.DatePicker */
15800     this.picker = this.component;
15801     this.addEvents({select: true});
15802     
15803     this.picker.on("render", function(picker){
15804         picker.getEl().swallowEvent("click");
15805         picker.container.addClass("x-menu-date-item");
15806     });
15807
15808     this.picker.on("select", this.onSelect, this);
15809 };
15810
15811 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15812     // private
15813     onSelect : function(picker, date){
15814         this.fireEvent("select", this, date, picker);
15815         Roo.menu.DateItem.superclass.handleClick.call(this);
15816     }
15817 });/*
15818  * Based on:
15819  * Ext JS Library 1.1.1
15820  * Copyright(c) 2006-2007, Ext JS, LLC.
15821  *
15822  * Originally Released Under LGPL - original licence link has changed is not relivant.
15823  *
15824  * Fork - LGPL
15825  * <script type="text/javascript">
15826  */
15827  
15828 /**
15829  * @class Roo.menu.ColorItem
15830  * @extends Roo.menu.Adapter
15831  * A menu item that wraps the {@link Roo.ColorPalette} component.
15832  * @constructor
15833  * Creates a new ColorItem
15834  * @param {Object} config Configuration options
15835  */
15836 Roo.menu.ColorItem = function(config){
15837     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15838     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15839     this.palette = this.component;
15840     this.relayEvents(this.palette, ["select"]);
15841     if(this.selectHandler){
15842         this.on('select', this.selectHandler, this.scope);
15843     }
15844 };
15845 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15846  * Based on:
15847  * Ext JS Library 1.1.1
15848  * Copyright(c) 2006-2007, Ext JS, LLC.
15849  *
15850  * Originally Released Under LGPL - original licence link has changed is not relivant.
15851  *
15852  * Fork - LGPL
15853  * <script type="text/javascript">
15854  */
15855  
15856
15857 /**
15858  * @class Roo.menu.DateMenu
15859  * @extends Roo.menu.Menu
15860  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15861  * @constructor
15862  * Creates a new DateMenu
15863  * @param {Object} config Configuration options
15864  */
15865 Roo.menu.DateMenu = function(config){
15866     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15867     this.plain = true;
15868     var di = new Roo.menu.DateItem(config);
15869     this.add(di);
15870     /**
15871      * The {@link Roo.DatePicker} instance for this DateMenu
15872      * @type DatePicker
15873      */
15874     this.picker = di.picker;
15875     /**
15876      * @event select
15877      * @param {DatePicker} picker
15878      * @param {Date} date
15879      */
15880     this.relayEvents(di, ["select"]);
15881     this.on('beforeshow', function(){
15882         if(this.picker){
15883             this.picker.hideMonthPicker(false);
15884         }
15885     }, this);
15886 };
15887 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15888     cls:'x-date-menu'
15889 });/*
15890  * Based on:
15891  * Ext JS Library 1.1.1
15892  * Copyright(c) 2006-2007, Ext JS, LLC.
15893  *
15894  * Originally Released Under LGPL - original licence link has changed is not relivant.
15895  *
15896  * Fork - LGPL
15897  * <script type="text/javascript">
15898  */
15899  
15900
15901 /**
15902  * @class Roo.menu.ColorMenu
15903  * @extends Roo.menu.Menu
15904  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15905  * @constructor
15906  * Creates a new ColorMenu
15907  * @param {Object} config Configuration options
15908  */
15909 Roo.menu.ColorMenu = function(config){
15910     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15911     this.plain = true;
15912     var ci = new Roo.menu.ColorItem(config);
15913     this.add(ci);
15914     /**
15915      * The {@link Roo.ColorPalette} instance for this ColorMenu
15916      * @type ColorPalette
15917      */
15918     this.palette = ci.palette;
15919     /**
15920      * @event select
15921      * @param {ColorPalette} palette
15922      * @param {String} color
15923      */
15924     this.relayEvents(ci, ["select"]);
15925 };
15926 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15927  * Based on:
15928  * Ext JS Library 1.1.1
15929  * Copyright(c) 2006-2007, Ext JS, LLC.
15930  *
15931  * Originally Released Under LGPL - original licence link has changed is not relivant.
15932  *
15933  * Fork - LGPL
15934  * <script type="text/javascript">
15935  */
15936  
15937 /**
15938  * @class Roo.form.TextItem
15939  * @extends Roo.BoxComponent
15940  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15941  * @constructor
15942  * Creates a new TextItem
15943  * @param {Object} config Configuration options
15944  */
15945 Roo.form.TextItem = function(config){
15946     Roo.form.TextItem.superclass.constructor.call(this, config);
15947 };
15948
15949 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15950     
15951     /**
15952      * @cfg {String} tag the tag for this item (default div)
15953      */
15954     tag : 'div',
15955     /**
15956      * @cfg {String} html the content for this item
15957      */
15958     html : '',
15959     
15960     getAutoCreate : function()
15961     {
15962         var cfg = {
15963             id: this.id,
15964             tag: this.tag,
15965             html: this.html,
15966             cls: 'x-form-item'
15967         };
15968         
15969         return cfg;
15970         
15971     },
15972     
15973     onRender : function(ct, position)
15974     {
15975         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15976         
15977         if(!this.el){
15978             var cfg = this.getAutoCreate();
15979             if(!cfg.name){
15980                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15981             }
15982             if (!cfg.name.length) {
15983                 delete cfg.name;
15984             }
15985             this.el = ct.createChild(cfg, position);
15986         }
15987     },
15988     /*
15989      * setHTML
15990      * @param {String} html update the Contents of the element.
15991      */
15992     setHTML : function(html)
15993     {
15994         this.fieldEl.dom.innerHTML = html;
15995     }
15996     
15997 });/*
15998  * Based on:
15999  * Ext JS Library 1.1.1
16000  * Copyright(c) 2006-2007, Ext JS, LLC.
16001  *
16002  * Originally Released Under LGPL - original licence link has changed is not relivant.
16003  *
16004  * Fork - LGPL
16005  * <script type="text/javascript">
16006  */
16007  
16008 /**
16009  * @class Roo.form.Field
16010  * @extends Roo.BoxComponent
16011  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16012  * @constructor
16013  * Creates a new Field
16014  * @param {Object} config Configuration options
16015  */
16016 Roo.form.Field = function(config){
16017     Roo.form.Field.superclass.constructor.call(this, config);
16018 };
16019
16020 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16021     /**
16022      * @cfg {String} fieldLabel Label to use when rendering a form.
16023      */
16024        /**
16025      * @cfg {String} qtip Mouse over tip
16026      */
16027      
16028     /**
16029      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16030      */
16031     invalidClass : "x-form-invalid",
16032     /**
16033      * @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")
16034      */
16035     invalidText : "The value in this field is invalid",
16036     /**
16037      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16038      */
16039     focusClass : "x-form-focus",
16040     /**
16041      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16042       automatic validation (defaults to "keyup").
16043      */
16044     validationEvent : "keyup",
16045     /**
16046      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16047      */
16048     validateOnBlur : true,
16049     /**
16050      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16051      */
16052     validationDelay : 250,
16053     /**
16054      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16055      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16056      */
16057     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16058     /**
16059      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16060      */
16061     fieldClass : "x-form-field",
16062     /**
16063      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16064      *<pre>
16065 Value         Description
16066 -----------   ----------------------------------------------------------------------
16067 qtip          Display a quick tip when the user hovers over the field
16068 title         Display a default browser title attribute popup
16069 under         Add a block div beneath the field containing the error text
16070 side          Add an error icon to the right of the field with a popup on hover
16071 [element id]  Add the error text directly to the innerHTML of the specified element
16072 </pre>
16073      */
16074     msgTarget : 'qtip',
16075     /**
16076      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16077      */
16078     msgFx : 'normal',
16079
16080     /**
16081      * @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.
16082      */
16083     readOnly : false,
16084
16085     /**
16086      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16087      */
16088     disabled : false,
16089
16090     /**
16091      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16092      */
16093     inputType : undefined,
16094     
16095     /**
16096      * @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).
16097          */
16098         tabIndex : undefined,
16099         
16100     // private
16101     isFormField : true,
16102
16103     // private
16104     hasFocus : false,
16105     /**
16106      * @property {Roo.Element} fieldEl
16107      * Element Containing the rendered Field (with label etc.)
16108      */
16109     /**
16110      * @cfg {Mixed} value A value to initialize this field with.
16111      */
16112     value : undefined,
16113
16114     /**
16115      * @cfg {String} name The field's HTML name attribute.
16116      */
16117     /**
16118      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16119      */
16120     // private
16121     loadedValue : false,
16122      
16123      
16124         // private ??
16125         initComponent : function(){
16126         Roo.form.Field.superclass.initComponent.call(this);
16127         this.addEvents({
16128             /**
16129              * @event focus
16130              * Fires when this field receives input focus.
16131              * @param {Roo.form.Field} this
16132              */
16133             focus : true,
16134             /**
16135              * @event blur
16136              * Fires when this field loses input focus.
16137              * @param {Roo.form.Field} this
16138              */
16139             blur : true,
16140             /**
16141              * @event specialkey
16142              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16143              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16144              * @param {Roo.form.Field} this
16145              * @param {Roo.EventObject} e The event object
16146              */
16147             specialkey : true,
16148             /**
16149              * @event change
16150              * Fires just before the field blurs if the field value has changed.
16151              * @param {Roo.form.Field} this
16152              * @param {Mixed} newValue The new value
16153              * @param {Mixed} oldValue The original value
16154              */
16155             change : true,
16156             /**
16157              * @event invalid
16158              * Fires after the field has been marked as invalid.
16159              * @param {Roo.form.Field} this
16160              * @param {String} msg The validation message
16161              */
16162             invalid : true,
16163             /**
16164              * @event valid
16165              * Fires after the field has been validated with no errors.
16166              * @param {Roo.form.Field} this
16167              */
16168             valid : true,
16169              /**
16170              * @event keyup
16171              * Fires after the key up
16172              * @param {Roo.form.Field} this
16173              * @param {Roo.EventObject}  e The event Object
16174              */
16175             keyup : true
16176         });
16177     },
16178
16179     /**
16180      * Returns the name attribute of the field if available
16181      * @return {String} name The field name
16182      */
16183     getName: function(){
16184          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16185     },
16186
16187     // private
16188     onRender : function(ct, position){
16189         Roo.form.Field.superclass.onRender.call(this, ct, position);
16190         if(!this.el){
16191             var cfg = this.getAutoCreate();
16192             if(!cfg.name){
16193                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16194             }
16195             if (!cfg.name.length) {
16196                 delete cfg.name;
16197             }
16198             if(this.inputType){
16199                 cfg.type = this.inputType;
16200             }
16201             this.el = ct.createChild(cfg, position);
16202         }
16203         var type = this.el.dom.type;
16204         if(type){
16205             if(type == 'password'){
16206                 type = 'text';
16207             }
16208             this.el.addClass('x-form-'+type);
16209         }
16210         if(this.readOnly){
16211             this.el.dom.readOnly = true;
16212         }
16213         if(this.tabIndex !== undefined){
16214             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16215         }
16216
16217         this.el.addClass([this.fieldClass, this.cls]);
16218         this.initValue();
16219     },
16220
16221     /**
16222      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16223      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16224      * @return {Roo.form.Field} this
16225      */
16226     applyTo : function(target){
16227         this.allowDomMove = false;
16228         this.el = Roo.get(target);
16229         this.render(this.el.dom.parentNode);
16230         return this;
16231     },
16232
16233     // private
16234     initValue : function(){
16235         if(this.value !== undefined){
16236             this.setValue(this.value);
16237         }else if(this.el.dom.value.length > 0){
16238             this.setValue(this.el.dom.value);
16239         }
16240     },
16241
16242     /**
16243      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16244      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16245      */
16246     isDirty : function() {
16247         if(this.disabled) {
16248             return false;
16249         }
16250         return String(this.getValue()) !== String(this.originalValue);
16251     },
16252
16253     /**
16254      * stores the current value in loadedValue
16255      */
16256     resetHasChanged : function()
16257     {
16258         this.loadedValue = String(this.getValue());
16259     },
16260     /**
16261      * checks the current value against the 'loaded' value.
16262      * Note - will return false if 'resetHasChanged' has not been called first.
16263      */
16264     hasChanged : function()
16265     {
16266         if(this.disabled || this.readOnly) {
16267             return false;
16268         }
16269         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16270     },
16271     
16272     
16273     
16274     // private
16275     afterRender : function(){
16276         Roo.form.Field.superclass.afterRender.call(this);
16277         this.initEvents();
16278     },
16279
16280     // private
16281     fireKey : function(e){
16282         //Roo.log('field ' + e.getKey());
16283         if(e.isNavKeyPress()){
16284             this.fireEvent("specialkey", this, e);
16285         }
16286     },
16287
16288     /**
16289      * Resets the current field value to the originally loaded value and clears any validation messages
16290      */
16291     reset : function(){
16292         this.setValue(this.resetValue);
16293         this.originalValue = this.getValue();
16294         this.clearInvalid();
16295     },
16296
16297     // private
16298     initEvents : function(){
16299         // safari killled keypress - so keydown is now used..
16300         this.el.on("keydown" , this.fireKey,  this);
16301         this.el.on("focus", this.onFocus,  this);
16302         this.el.on("blur", this.onBlur,  this);
16303         this.el.relayEvent('keyup', this);
16304
16305         // reference to original value for reset
16306         this.originalValue = this.getValue();
16307         this.resetValue =  this.getValue();
16308     },
16309
16310     // private
16311     onFocus : function(){
16312         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16313             this.el.addClass(this.focusClass);
16314         }
16315         if(!this.hasFocus){
16316             this.hasFocus = true;
16317             this.startValue = this.getValue();
16318             this.fireEvent("focus", this);
16319         }
16320     },
16321
16322     beforeBlur : Roo.emptyFn,
16323
16324     // private
16325     onBlur : function(){
16326         this.beforeBlur();
16327         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16328             this.el.removeClass(this.focusClass);
16329         }
16330         this.hasFocus = false;
16331         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16332             this.validate();
16333         }
16334         var v = this.getValue();
16335         if(String(v) !== String(this.startValue)){
16336             this.fireEvent('change', this, v, this.startValue);
16337         }
16338         this.fireEvent("blur", this);
16339     },
16340
16341     /**
16342      * Returns whether or not the field value is currently valid
16343      * @param {Boolean} preventMark True to disable marking the field invalid
16344      * @return {Boolean} True if the value is valid, else false
16345      */
16346     isValid : function(preventMark){
16347         if(this.disabled){
16348             return true;
16349         }
16350         var restore = this.preventMark;
16351         this.preventMark = preventMark === true;
16352         var v = this.validateValue(this.processValue(this.getRawValue()));
16353         this.preventMark = restore;
16354         return v;
16355     },
16356
16357     /**
16358      * Validates the field value
16359      * @return {Boolean} True if the value is valid, else false
16360      */
16361     validate : function(){
16362         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16363             this.clearInvalid();
16364             return true;
16365         }
16366         return false;
16367     },
16368
16369     processValue : function(value){
16370         return value;
16371     },
16372
16373     // private
16374     // Subclasses should provide the validation implementation by overriding this
16375     validateValue : function(value){
16376         return true;
16377     },
16378
16379     /**
16380      * Mark this field as invalid
16381      * @param {String} msg The validation message
16382      */
16383     markInvalid : function(msg){
16384         if(!this.rendered || this.preventMark){ // not rendered
16385             return;
16386         }
16387         
16388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16389         
16390         obj.el.addClass(this.invalidClass);
16391         msg = msg || this.invalidText;
16392         switch(this.msgTarget){
16393             case 'qtip':
16394                 obj.el.dom.qtip = msg;
16395                 obj.el.dom.qclass = 'x-form-invalid-tip';
16396                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16397                     Roo.QuickTips.enable();
16398                 }
16399                 break;
16400             case 'title':
16401                 this.el.dom.title = msg;
16402                 break;
16403             case 'under':
16404                 if(!this.errorEl){
16405                     var elp = this.el.findParent('.x-form-element', 5, true);
16406                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16407                     this.errorEl.setWidth(elp.getWidth(true)-20);
16408                 }
16409                 this.errorEl.update(msg);
16410                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16411                 break;
16412             case 'side':
16413                 if(!this.errorIcon){
16414                     var elp = this.el.findParent('.x-form-element', 5, true);
16415                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16416                 }
16417                 this.alignErrorIcon();
16418                 this.errorIcon.dom.qtip = msg;
16419                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16420                 this.errorIcon.show();
16421                 this.on('resize', this.alignErrorIcon, this);
16422                 break;
16423             default:
16424                 var t = Roo.getDom(this.msgTarget);
16425                 t.innerHTML = msg;
16426                 t.style.display = this.msgDisplay;
16427                 break;
16428         }
16429         this.fireEvent('invalid', this, msg);
16430     },
16431
16432     // private
16433     alignErrorIcon : function(){
16434         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16435     },
16436
16437     /**
16438      * Clear any invalid styles/messages for this field
16439      */
16440     clearInvalid : function(){
16441         if(!this.rendered || this.preventMark){ // not rendered
16442             return;
16443         }
16444         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16445         
16446         obj.el.removeClass(this.invalidClass);
16447         switch(this.msgTarget){
16448             case 'qtip':
16449                 obj.el.dom.qtip = '';
16450                 break;
16451             case 'title':
16452                 this.el.dom.title = '';
16453                 break;
16454             case 'under':
16455                 if(this.errorEl){
16456                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16457                 }
16458                 break;
16459             case 'side':
16460                 if(this.errorIcon){
16461                     this.errorIcon.dom.qtip = '';
16462                     this.errorIcon.hide();
16463                     this.un('resize', this.alignErrorIcon, this);
16464                 }
16465                 break;
16466             default:
16467                 var t = Roo.getDom(this.msgTarget);
16468                 t.innerHTML = '';
16469                 t.style.display = 'none';
16470                 break;
16471         }
16472         this.fireEvent('valid', this);
16473     },
16474
16475     /**
16476      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16477      * @return {Mixed} value The field value
16478      */
16479     getRawValue : function(){
16480         var v = this.el.getValue();
16481         
16482         return v;
16483     },
16484
16485     /**
16486      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16487      * @return {Mixed} value The field value
16488      */
16489     getValue : function(){
16490         var v = this.el.getValue();
16491          
16492         return v;
16493     },
16494
16495     /**
16496      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16497      * @param {Mixed} value The value to set
16498      */
16499     setRawValue : function(v){
16500         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16501     },
16502
16503     /**
16504      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16505      * @param {Mixed} value The value to set
16506      */
16507     setValue : function(v){
16508         this.value = v;
16509         if(this.rendered){
16510             this.el.dom.value = (v === null || v === undefined ? '' : v);
16511              this.validate();
16512         }
16513     },
16514
16515     adjustSize : function(w, h){
16516         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16517         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16518         return s;
16519     },
16520
16521     adjustWidth : function(tag, w){
16522         tag = tag.toLowerCase();
16523         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16524             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16525                 if(tag == 'input'){
16526                     return w + 2;
16527                 }
16528                 if(tag == 'textarea'){
16529                     return w-2;
16530                 }
16531             }else if(Roo.isOpera){
16532                 if(tag == 'input'){
16533                     return w + 2;
16534                 }
16535                 if(tag == 'textarea'){
16536                     return w-2;
16537                 }
16538             }
16539         }
16540         return w;
16541     }
16542 });
16543
16544
16545 // anything other than normal should be considered experimental
16546 Roo.form.Field.msgFx = {
16547     normal : {
16548         show: function(msgEl, f){
16549             msgEl.setDisplayed('block');
16550         },
16551
16552         hide : function(msgEl, f){
16553             msgEl.setDisplayed(false).update('');
16554         }
16555     },
16556
16557     slide : {
16558         show: function(msgEl, f){
16559             msgEl.slideIn('t', {stopFx:true});
16560         },
16561
16562         hide : function(msgEl, f){
16563             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16564         }
16565     },
16566
16567     slideRight : {
16568         show: function(msgEl, f){
16569             msgEl.fixDisplay();
16570             msgEl.alignTo(f.el, 'tl-tr');
16571             msgEl.slideIn('l', {stopFx:true});
16572         },
16573
16574         hide : function(msgEl, f){
16575             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16576         }
16577     }
16578 };/*
16579  * Based on:
16580  * Ext JS Library 1.1.1
16581  * Copyright(c) 2006-2007, Ext JS, LLC.
16582  *
16583  * Originally Released Under LGPL - original licence link has changed is not relivant.
16584  *
16585  * Fork - LGPL
16586  * <script type="text/javascript">
16587  */
16588  
16589
16590 /**
16591  * @class Roo.form.TextField
16592  * @extends Roo.form.Field
16593  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16594  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16595  * @constructor
16596  * Creates a new TextField
16597  * @param {Object} config Configuration options
16598  */
16599 Roo.form.TextField = function(config){
16600     Roo.form.TextField.superclass.constructor.call(this, config);
16601     this.addEvents({
16602         /**
16603          * @event autosize
16604          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16605          * according to the default logic, but this event provides a hook for the developer to apply additional
16606          * logic at runtime to resize the field if needed.
16607              * @param {Roo.form.Field} this This text field
16608              * @param {Number} width The new field width
16609              */
16610         autosize : true
16611     });
16612 };
16613
16614 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16615     /**
16616      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16617      */
16618     grow : false,
16619     /**
16620      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16621      */
16622     growMin : 30,
16623     /**
16624      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16625      */
16626     growMax : 800,
16627     /**
16628      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16629      */
16630     vtype : null,
16631     /**
16632      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16633      */
16634     maskRe : null,
16635     /**
16636      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16637      */
16638     disableKeyFilter : false,
16639     /**
16640      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16641      */
16642     allowBlank : true,
16643     /**
16644      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16645      */
16646     minLength : 0,
16647     /**
16648      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16649      */
16650     maxLength : Number.MAX_VALUE,
16651     /**
16652      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16653      */
16654     minLengthText : "The minimum length for this field is {0}",
16655     /**
16656      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16657      */
16658     maxLengthText : "The maximum length for this field is {0}",
16659     /**
16660      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16661      */
16662     selectOnFocus : false,
16663     /**
16664      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16665      */    
16666     allowLeadingSpace : false,
16667     /**
16668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16669      */
16670     blankText : "This field is required",
16671     /**
16672      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16673      * If available, this function will be called only after the basic validators all return true, and will be passed the
16674      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16675      */
16676     validator : null,
16677     /**
16678      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16679      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16680      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16681      */
16682     regex : null,
16683     /**
16684      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16685      */
16686     regexText : "",
16687     /**
16688      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16689      */
16690     emptyText : null,
16691    
16692
16693     // private
16694     initEvents : function()
16695     {
16696         if (this.emptyText) {
16697             this.el.attr('placeholder', this.emptyText);
16698         }
16699         
16700         Roo.form.TextField.superclass.initEvents.call(this);
16701         if(this.validationEvent == 'keyup'){
16702             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16703             this.el.on('keyup', this.filterValidation, this);
16704         }
16705         else if(this.validationEvent !== false){
16706             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16707         }
16708         
16709         if(this.selectOnFocus){
16710             this.on("focus", this.preFocus, this);
16711         }
16712         if (!this.allowLeadingSpace) {
16713             this.on('blur', this.cleanLeadingSpace, this);
16714         }
16715         
16716         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16717             this.el.on("keypress", this.filterKeys, this);
16718         }
16719         if(this.grow){
16720             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16721             this.el.on("click", this.autoSize,  this);
16722         }
16723         if(this.el.is('input[type=password]') && Roo.isSafari){
16724             this.el.on('keydown', this.SafariOnKeyDown, this);
16725         }
16726     },
16727
16728     processValue : function(value){
16729         if(this.stripCharsRe){
16730             var newValue = value.replace(this.stripCharsRe, '');
16731             if(newValue !== value){
16732                 this.setRawValue(newValue);
16733                 return newValue;
16734             }
16735         }
16736         return value;
16737     },
16738
16739     filterValidation : function(e){
16740         if(!e.isNavKeyPress()){
16741             this.validationTask.delay(this.validationDelay);
16742         }
16743     },
16744
16745     // private
16746     onKeyUp : function(e){
16747         if(!e.isNavKeyPress()){
16748             this.autoSize();
16749         }
16750     },
16751     // private - clean the leading white space
16752     cleanLeadingSpace : function(e)
16753     {
16754         if ( this.inputType == 'file') {
16755             return;
16756         }
16757         
16758         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16759     },
16760     /**
16761      * Resets the current field value to the originally-loaded value and clears any validation messages.
16762      *  
16763      */
16764     reset : function(){
16765         Roo.form.TextField.superclass.reset.call(this);
16766        
16767     }, 
16768     // private
16769     preFocus : function(){
16770         
16771         if(this.selectOnFocus){
16772             this.el.dom.select();
16773         }
16774     },
16775
16776     
16777     // private
16778     filterKeys : function(e){
16779         var k = e.getKey();
16780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16781             return;
16782         }
16783         var c = e.getCharCode(), cc = String.fromCharCode(c);
16784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16785             return;
16786         }
16787         if(!this.maskRe.test(cc)){
16788             e.stopEvent();
16789         }
16790     },
16791
16792     setValue : function(v){
16793         
16794         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16795         
16796         this.autoSize();
16797     },
16798
16799     /**
16800      * Validates a value according to the field's validation rules and marks the field as invalid
16801      * if the validation fails
16802      * @param {Mixed} value The value to validate
16803      * @return {Boolean} True if the value is valid, else false
16804      */
16805     validateValue : function(value){
16806         if(value.length < 1)  { // if it's blank
16807              if(this.allowBlank){
16808                 this.clearInvalid();
16809                 return true;
16810              }else{
16811                 this.markInvalid(this.blankText);
16812                 return false;
16813              }
16814         }
16815         if(value.length < this.minLength){
16816             this.markInvalid(String.format(this.minLengthText, this.minLength));
16817             return false;
16818         }
16819         if(value.length > this.maxLength){
16820             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16821             return false;
16822         }
16823         if(this.vtype){
16824             var vt = Roo.form.VTypes;
16825             if(!vt[this.vtype](value, this)){
16826                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16827                 return false;
16828             }
16829         }
16830         if(typeof this.validator == "function"){
16831             var msg = this.validator(value);
16832             if(msg !== true){
16833                 this.markInvalid(msg);
16834                 return false;
16835             }
16836         }
16837         if(this.regex && !this.regex.test(value)){
16838             this.markInvalid(this.regexText);
16839             return false;
16840         }
16841         return true;
16842     },
16843
16844     /**
16845      * Selects text in this field
16846      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16847      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16848      */
16849     selectText : function(start, end){
16850         var v = this.getRawValue();
16851         if(v.length > 0){
16852             start = start === undefined ? 0 : start;
16853             end = end === undefined ? v.length : end;
16854             var d = this.el.dom;
16855             if(d.setSelectionRange){
16856                 d.setSelectionRange(start, end);
16857             }else if(d.createTextRange){
16858                 var range = d.createTextRange();
16859                 range.moveStart("character", start);
16860                 range.moveEnd("character", v.length-end);
16861                 range.select();
16862             }
16863         }
16864     },
16865
16866     /**
16867      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16868      * This only takes effect if grow = true, and fires the autosize event.
16869      */
16870     autoSize : function(){
16871         if(!this.grow || !this.rendered){
16872             return;
16873         }
16874         if(!this.metrics){
16875             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16876         }
16877         var el = this.el;
16878         var v = el.dom.value;
16879         var d = document.createElement('div');
16880         d.appendChild(document.createTextNode(v));
16881         v = d.innerHTML;
16882         d = null;
16883         v += "&#160;";
16884         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16885         this.el.setWidth(w);
16886         this.fireEvent("autosize", this, w);
16887     },
16888     
16889     // private
16890     SafariOnKeyDown : function(event)
16891     {
16892         // this is a workaround for a password hang bug on chrome/ webkit.
16893         
16894         var isSelectAll = false;
16895         
16896         if(this.el.dom.selectionEnd > 0){
16897             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16898         }
16899         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16900             event.preventDefault();
16901             this.setValue('');
16902             return;
16903         }
16904         
16905         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16906             
16907             event.preventDefault();
16908             // this is very hacky as keydown always get's upper case.
16909             
16910             var cc = String.fromCharCode(event.getCharCode());
16911             
16912             
16913             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16914             
16915         }
16916         
16917         
16918     }
16919 });/*
16920  * Based on:
16921  * Ext JS Library 1.1.1
16922  * Copyright(c) 2006-2007, Ext JS, LLC.
16923  *
16924  * Originally Released Under LGPL - original licence link has changed is not relivant.
16925  *
16926  * Fork - LGPL
16927  * <script type="text/javascript">
16928  */
16929  
16930 /**
16931  * @class Roo.form.Hidden
16932  * @extends Roo.form.TextField
16933  * Simple Hidden element used on forms 
16934  * 
16935  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16936  * 
16937  * @constructor
16938  * Creates a new Hidden form element.
16939  * @param {Object} config Configuration options
16940  */
16941
16942
16943
16944 // easy hidden field...
16945 Roo.form.Hidden = function(config){
16946     Roo.form.Hidden.superclass.constructor.call(this, config);
16947 };
16948   
16949 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16950     fieldLabel:      '',
16951     inputType:      'hidden',
16952     width:          50,
16953     allowBlank:     true,
16954     labelSeparator: '',
16955     hidden:         true,
16956     itemCls :       'x-form-item-display-none'
16957
16958
16959 });
16960
16961
16962 /*
16963  * Based on:
16964  * Ext JS Library 1.1.1
16965  * Copyright(c) 2006-2007, Ext JS, LLC.
16966  *
16967  * Originally Released Under LGPL - original licence link has changed is not relivant.
16968  *
16969  * Fork - LGPL
16970  * <script type="text/javascript">
16971  */
16972  
16973 /**
16974  * @class Roo.form.TriggerField
16975  * @extends Roo.form.TextField
16976  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16977  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16978  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16979  * for which you can provide a custom implementation.  For example:
16980  * <pre><code>
16981 var trigger = new Roo.form.TriggerField();
16982 trigger.onTriggerClick = myTriggerFn;
16983 trigger.applyTo('my-field');
16984 </code></pre>
16985  *
16986  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16987  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16988  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16989  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16990  * @constructor
16991  * Create a new TriggerField.
16992  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16993  * to the base TextField)
16994  */
16995 Roo.form.TriggerField = function(config){
16996     this.mimicing = false;
16997     Roo.form.TriggerField.superclass.constructor.call(this, config);
16998 };
16999
17000 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17001     /**
17002      * @cfg {String} triggerClass A CSS class to apply to the trigger
17003      */
17004     /**
17005      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17006      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17007      */
17008     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17009     /**
17010      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17011      */
17012     hideTrigger:false,
17013
17014     /** @cfg {Boolean} grow @hide */
17015     /** @cfg {Number} growMin @hide */
17016     /** @cfg {Number} growMax @hide */
17017
17018     /**
17019      * @hide 
17020      * @method
17021      */
17022     autoSize: Roo.emptyFn,
17023     // private
17024     monitorTab : true,
17025     // private
17026     deferHeight : true,
17027
17028     
17029     actionMode : 'wrap',
17030     // private
17031     onResize : function(w, h){
17032         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17033         if(typeof w == 'number'){
17034             var x = w - this.trigger.getWidth();
17035             this.el.setWidth(this.adjustWidth('input', x));
17036             this.trigger.setStyle('left', x+'px');
17037         }
17038     },
17039
17040     // private
17041     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17042
17043     // private
17044     getResizeEl : function(){
17045         return this.wrap;
17046     },
17047
17048     // private
17049     getPositionEl : function(){
17050         return this.wrap;
17051     },
17052
17053     // private
17054     alignErrorIcon : function(){
17055         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17056     },
17057
17058     // private
17059     onRender : function(ct, position){
17060         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17061         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17062         this.trigger = this.wrap.createChild(this.triggerConfig ||
17063                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17064         if(this.hideTrigger){
17065             this.trigger.setDisplayed(false);
17066         }
17067         this.initTrigger();
17068         if(!this.width){
17069             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17070         }
17071     },
17072
17073     // private
17074     initTrigger : function(){
17075         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17076         this.trigger.addClassOnOver('x-form-trigger-over');
17077         this.trigger.addClassOnClick('x-form-trigger-click');
17078     },
17079
17080     // private
17081     onDestroy : function(){
17082         if(this.trigger){
17083             this.trigger.removeAllListeners();
17084             this.trigger.remove();
17085         }
17086         if(this.wrap){
17087             this.wrap.remove();
17088         }
17089         Roo.form.TriggerField.superclass.onDestroy.call(this);
17090     },
17091
17092     // private
17093     onFocus : function(){
17094         Roo.form.TriggerField.superclass.onFocus.call(this);
17095         if(!this.mimicing){
17096             this.wrap.addClass('x-trigger-wrap-focus');
17097             this.mimicing = true;
17098             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17099             if(this.monitorTab){
17100                 this.el.on("keydown", this.checkTab, this);
17101             }
17102         }
17103     },
17104
17105     // private
17106     checkTab : function(e){
17107         if(e.getKey() == e.TAB){
17108             this.triggerBlur();
17109         }
17110     },
17111
17112     // private
17113     onBlur : function(){
17114         // do nothing
17115     },
17116
17117     // private
17118     mimicBlur : function(e, t){
17119         if(!this.wrap.contains(t) && this.validateBlur()){
17120             this.triggerBlur();
17121         }
17122     },
17123
17124     // private
17125     triggerBlur : function(){
17126         this.mimicing = false;
17127         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17128         if(this.monitorTab){
17129             this.el.un("keydown", this.checkTab, this);
17130         }
17131         this.wrap.removeClass('x-trigger-wrap-focus');
17132         Roo.form.TriggerField.superclass.onBlur.call(this);
17133     },
17134
17135     // private
17136     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17137     validateBlur : function(e, t){
17138         return true;
17139     },
17140
17141     // private
17142     onDisable : function(){
17143         Roo.form.TriggerField.superclass.onDisable.call(this);
17144         if(this.wrap){
17145             this.wrap.addClass('x-item-disabled');
17146         }
17147     },
17148
17149     // private
17150     onEnable : function(){
17151         Roo.form.TriggerField.superclass.onEnable.call(this);
17152         if(this.wrap){
17153             this.wrap.removeClass('x-item-disabled');
17154         }
17155     },
17156
17157     // private
17158     onShow : function(){
17159         var ae = this.getActionEl();
17160         
17161         if(ae){
17162             ae.dom.style.display = '';
17163             ae.dom.style.visibility = 'visible';
17164         }
17165     },
17166
17167     // private
17168     
17169     onHide : function(){
17170         var ae = this.getActionEl();
17171         ae.dom.style.display = 'none';
17172     },
17173
17174     /**
17175      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17176      * by an implementing function.
17177      * @method
17178      * @param {EventObject} e
17179      */
17180     onTriggerClick : Roo.emptyFn
17181 });
17182
17183 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17184 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17185 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17186 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17187     initComponent : function(){
17188         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17189
17190         this.triggerConfig = {
17191             tag:'span', cls:'x-form-twin-triggers', cn:[
17192             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17193             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17194         ]};
17195     },
17196
17197     getTrigger : function(index){
17198         return this.triggers[index];
17199     },
17200
17201     initTrigger : function(){
17202         var ts = this.trigger.select('.x-form-trigger', true);
17203         this.wrap.setStyle('overflow', 'hidden');
17204         var triggerField = this;
17205         ts.each(function(t, all, index){
17206             t.hide = function(){
17207                 var w = triggerField.wrap.getWidth();
17208                 this.dom.style.display = 'none';
17209                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17210             };
17211             t.show = function(){
17212                 var w = triggerField.wrap.getWidth();
17213                 this.dom.style.display = '';
17214                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17215             };
17216             var triggerIndex = 'Trigger'+(index+1);
17217
17218             if(this['hide'+triggerIndex]){
17219                 t.dom.style.display = 'none';
17220             }
17221             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17222             t.addClassOnOver('x-form-trigger-over');
17223             t.addClassOnClick('x-form-trigger-click');
17224         }, this);
17225         this.triggers = ts.elements;
17226     },
17227
17228     onTrigger1Click : Roo.emptyFn,
17229     onTrigger2Click : Roo.emptyFn
17230 });/*
17231  * Based on:
17232  * Ext JS Library 1.1.1
17233  * Copyright(c) 2006-2007, Ext JS, LLC.
17234  *
17235  * Originally Released Under LGPL - original licence link has changed is not relivant.
17236  *
17237  * Fork - LGPL
17238  * <script type="text/javascript">
17239  */
17240  
17241 /**
17242  * @class Roo.form.TextArea
17243  * @extends Roo.form.TextField
17244  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17245  * support for auto-sizing.
17246  * @constructor
17247  * Creates a new TextArea
17248  * @param {Object} config Configuration options
17249  */
17250 Roo.form.TextArea = function(config){
17251     Roo.form.TextArea.superclass.constructor.call(this, config);
17252     // these are provided exchanges for backwards compat
17253     // minHeight/maxHeight were replaced by growMin/growMax to be
17254     // compatible with TextField growing config values
17255     if(this.minHeight !== undefined){
17256         this.growMin = this.minHeight;
17257     }
17258     if(this.maxHeight !== undefined){
17259         this.growMax = this.maxHeight;
17260     }
17261 };
17262
17263 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17264     /**
17265      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17266      */
17267     growMin : 60,
17268     /**
17269      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17270      */
17271     growMax: 1000,
17272     /**
17273      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17274      * in the field (equivalent to setting overflow: hidden, defaults to false)
17275      */
17276     preventScrollbars: false,
17277     /**
17278      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17279      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17280      */
17281
17282     // private
17283     onRender : function(ct, position){
17284         if(!this.el){
17285             this.defaultAutoCreate = {
17286                 tag: "textarea",
17287                 style:"width:300px;height:60px;",
17288                 autocomplete: "new-password"
17289             };
17290         }
17291         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17292         if(this.grow){
17293             this.textSizeEl = Roo.DomHelper.append(document.body, {
17294                 tag: "pre", cls: "x-form-grow-sizer"
17295             });
17296             if(this.preventScrollbars){
17297                 this.el.setStyle("overflow", "hidden");
17298             }
17299             this.el.setHeight(this.growMin);
17300         }
17301     },
17302
17303     onDestroy : function(){
17304         if(this.textSizeEl){
17305             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17306         }
17307         Roo.form.TextArea.superclass.onDestroy.call(this);
17308     },
17309
17310     // private
17311     onKeyUp : function(e){
17312         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17313             this.autoSize();
17314         }
17315     },
17316
17317     /**
17318      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17319      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17320      */
17321     autoSize : function(){
17322         if(!this.grow || !this.textSizeEl){
17323             return;
17324         }
17325         var el = this.el;
17326         var v = el.dom.value;
17327         var ts = this.textSizeEl;
17328
17329         ts.innerHTML = '';
17330         ts.appendChild(document.createTextNode(v));
17331         v = ts.innerHTML;
17332
17333         Roo.fly(ts).setWidth(this.el.getWidth());
17334         if(v.length < 1){
17335             v = "&#160;&#160;";
17336         }else{
17337             if(Roo.isIE){
17338                 v = v.replace(/\n/g, '<p>&#160;</p>');
17339             }
17340             v += "&#160;\n&#160;";
17341         }
17342         ts.innerHTML = v;
17343         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17344         if(h != this.lastHeight){
17345             this.lastHeight = h;
17346             this.el.setHeight(h);
17347             this.fireEvent("autosize", this, h);
17348         }
17349     }
17350 });/*
17351  * Based on:
17352  * Ext JS Library 1.1.1
17353  * Copyright(c) 2006-2007, Ext JS, LLC.
17354  *
17355  * Originally Released Under LGPL - original licence link has changed is not relivant.
17356  *
17357  * Fork - LGPL
17358  * <script type="text/javascript">
17359  */
17360  
17361
17362 /**
17363  * @class Roo.form.NumberField
17364  * @extends Roo.form.TextField
17365  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17366  * @constructor
17367  * Creates a new NumberField
17368  * @param {Object} config Configuration options
17369  */
17370 Roo.form.NumberField = function(config){
17371     Roo.form.NumberField.superclass.constructor.call(this, config);
17372 };
17373
17374 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17375     /**
17376      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17377      */
17378     fieldClass: "x-form-field x-form-num-field",
17379     /**
17380      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17381      */
17382     allowDecimals : true,
17383     /**
17384      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17385      */
17386     decimalSeparator : ".",
17387     /**
17388      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17389      */
17390     decimalPrecision : 2,
17391     /**
17392      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17393      */
17394     allowNegative : true,
17395     /**
17396      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17397      */
17398     minValue : Number.NEGATIVE_INFINITY,
17399     /**
17400      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17401      */
17402     maxValue : Number.MAX_VALUE,
17403     /**
17404      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17405      */
17406     minText : "The minimum value for this field is {0}",
17407     /**
17408      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17409      */
17410     maxText : "The maximum value for this field is {0}",
17411     /**
17412      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17413      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17414      */
17415     nanText : "{0} is not a valid number",
17416
17417     // private
17418     initEvents : function(){
17419         Roo.form.NumberField.superclass.initEvents.call(this);
17420         var allowed = "0123456789";
17421         if(this.allowDecimals){
17422             allowed += this.decimalSeparator;
17423         }
17424         if(this.allowNegative){
17425             allowed += "-";
17426         }
17427         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17428         var keyPress = function(e){
17429             var k = e.getKey();
17430             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17431                 return;
17432             }
17433             var c = e.getCharCode();
17434             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17435                 e.stopEvent();
17436             }
17437         };
17438         this.el.on("keypress", keyPress, this);
17439     },
17440
17441     // private
17442     validateValue : function(value){
17443         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17444             return false;
17445         }
17446         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17447              return true;
17448         }
17449         var num = this.parseValue(value);
17450         if(isNaN(num)){
17451             this.markInvalid(String.format(this.nanText, value));
17452             return false;
17453         }
17454         if(num < this.minValue){
17455             this.markInvalid(String.format(this.minText, this.minValue));
17456             return false;
17457         }
17458         if(num > this.maxValue){
17459             this.markInvalid(String.format(this.maxText, this.maxValue));
17460             return false;
17461         }
17462         return true;
17463     },
17464
17465     getValue : function(){
17466         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17467     },
17468
17469     // private
17470     parseValue : function(value){
17471         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17472         return isNaN(value) ? '' : value;
17473     },
17474
17475     // private
17476     fixPrecision : function(value){
17477         var nan = isNaN(value);
17478         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17479             return nan ? '' : value;
17480         }
17481         return parseFloat(value).toFixed(this.decimalPrecision);
17482     },
17483
17484     setValue : function(v){
17485         v = this.fixPrecision(v);
17486         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17487     },
17488
17489     // private
17490     decimalPrecisionFcn : function(v){
17491         return Math.floor(v);
17492     },
17493
17494     beforeBlur : function(){
17495         var v = this.parseValue(this.getRawValue());
17496         if(v){
17497             this.setValue(v);
17498         }
17499     }
17500 });/*
17501  * Based on:
17502  * Ext JS Library 1.1.1
17503  * Copyright(c) 2006-2007, Ext JS, LLC.
17504  *
17505  * Originally Released Under LGPL - original licence link has changed is not relivant.
17506  *
17507  * Fork - LGPL
17508  * <script type="text/javascript">
17509  */
17510  
17511 /**
17512  * @class Roo.form.DateField
17513  * @extends Roo.form.TriggerField
17514  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17515 * @constructor
17516 * Create a new DateField
17517 * @param {Object} config
17518  */
17519 Roo.form.DateField = function(config)
17520 {
17521     Roo.form.DateField.superclass.constructor.call(this, config);
17522     
17523       this.addEvents({
17524          
17525         /**
17526          * @event select
17527          * Fires when a date is selected
17528              * @param {Roo.form.DateField} combo This combo box
17529              * @param {Date} date The date selected
17530              */
17531         'select' : true
17532          
17533     });
17534     
17535     
17536     if(typeof this.minValue == "string") {
17537         this.minValue = this.parseDate(this.minValue);
17538     }
17539     if(typeof this.maxValue == "string") {
17540         this.maxValue = this.parseDate(this.maxValue);
17541     }
17542     this.ddMatch = null;
17543     if(this.disabledDates){
17544         var dd = this.disabledDates;
17545         var re = "(?:";
17546         for(var i = 0; i < dd.length; i++){
17547             re += dd[i];
17548             if(i != dd.length-1) {
17549                 re += "|";
17550             }
17551         }
17552         this.ddMatch = new RegExp(re + ")");
17553     }
17554 };
17555
17556 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17557     /**
17558      * @cfg {String} format
17559      * The default date format string which can be overriden for localization support.  The format must be
17560      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17561      */
17562     format : "m/d/y",
17563     /**
17564      * @cfg {String} altFormats
17565      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17566      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17567      */
17568     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17569     /**
17570      * @cfg {Array} disabledDays
17571      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17572      */
17573     disabledDays : null,
17574     /**
17575      * @cfg {String} disabledDaysText
17576      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17577      */
17578     disabledDaysText : "Disabled",
17579     /**
17580      * @cfg {Array} disabledDates
17581      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17582      * expression so they are very powerful. Some examples:
17583      * <ul>
17584      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17585      * <li>["03/08", "09/16"] would disable those days for every year</li>
17586      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17587      * <li>["03/../2006"] would disable every day in March 2006</li>
17588      * <li>["^03"] would disable every day in every March</li>
17589      * </ul>
17590      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17591      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17592      */
17593     disabledDates : null,
17594     /**
17595      * @cfg {String} disabledDatesText
17596      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17597      */
17598     disabledDatesText : "Disabled",
17599         
17600         
17601         /**
17602      * @cfg {Date/String} zeroValue
17603      * if the date is less that this number, then the field is rendered as empty
17604      * default is 1800
17605      */
17606         zeroValue : '1800-01-01',
17607         
17608         
17609     /**
17610      * @cfg {Date/String} minValue
17611      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17612      * valid format (defaults to null).
17613      */
17614     minValue : null,
17615     /**
17616      * @cfg {Date/String} maxValue
17617      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17618      * valid format (defaults to null).
17619      */
17620     maxValue : null,
17621     /**
17622      * @cfg {String} minText
17623      * The error text to display when the date in the cell is before minValue (defaults to
17624      * 'The date in this field must be after {minValue}').
17625      */
17626     minText : "The date in this field must be equal to or after {0}",
17627     /**
17628      * @cfg {String} maxText
17629      * The error text to display when the date in the cell is after maxValue (defaults to
17630      * 'The date in this field must be before {maxValue}').
17631      */
17632     maxText : "The date in this field must be equal to or before {0}",
17633     /**
17634      * @cfg {String} invalidText
17635      * The error text to display when the date in the field is invalid (defaults to
17636      * '{value} is not a valid date - it must be in the format {format}').
17637      */
17638     invalidText : "{0} is not a valid date - it must be in the format {1}",
17639     /**
17640      * @cfg {String} triggerClass
17641      * An additional CSS class used to style the trigger button.  The trigger will always get the
17642      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17643      * which displays a calendar icon).
17644      */
17645     triggerClass : 'x-form-date-trigger',
17646     
17647
17648     /**
17649      * @cfg {Boolean} useIso
17650      * if enabled, then the date field will use a hidden field to store the 
17651      * real value as iso formated date. default (false)
17652      */ 
17653     useIso : false,
17654     /**
17655      * @cfg {String/Object} autoCreate
17656      * A DomHelper element spec, or true for a default element spec (defaults to
17657      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17658      */ 
17659     // private
17660     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17661     
17662     // private
17663     hiddenField: false,
17664     
17665     onRender : function(ct, position)
17666     {
17667         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17668         if (this.useIso) {
17669             //this.el.dom.removeAttribute('name'); 
17670             Roo.log("Changing name?");
17671             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17672             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17673                     'before', true);
17674             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17675             // prevent input submission
17676             this.hiddenName = this.name;
17677         }
17678             
17679             
17680     },
17681     
17682     // private
17683     validateValue : function(value)
17684     {
17685         value = this.formatDate(value);
17686         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17687             Roo.log('super failed');
17688             return false;
17689         }
17690         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17691              return true;
17692         }
17693         var svalue = value;
17694         value = this.parseDate(value);
17695         if(!value){
17696             Roo.log('parse date failed' + svalue);
17697             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17698             return false;
17699         }
17700         var time = value.getTime();
17701         if(this.minValue && time < this.minValue.getTime()){
17702             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17703             return false;
17704         }
17705         if(this.maxValue && time > this.maxValue.getTime()){
17706             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17707             return false;
17708         }
17709         if(this.disabledDays){
17710             var day = value.getDay();
17711             for(var i = 0; i < this.disabledDays.length; i++) {
17712                 if(day === this.disabledDays[i]){
17713                     this.markInvalid(this.disabledDaysText);
17714                     return false;
17715                 }
17716             }
17717         }
17718         var fvalue = this.formatDate(value);
17719         if(this.ddMatch && this.ddMatch.test(fvalue)){
17720             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17721             return false;
17722         }
17723         return true;
17724     },
17725
17726     // private
17727     // Provides logic to override the default TriggerField.validateBlur which just returns true
17728     validateBlur : function(){
17729         return !this.menu || !this.menu.isVisible();
17730     },
17731     
17732     getName: function()
17733     {
17734         // returns hidden if it's set..
17735         if (!this.rendered) {return ''};
17736         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17737         
17738     },
17739
17740     /**
17741      * Returns the current date value of the date field.
17742      * @return {Date} The date value
17743      */
17744     getValue : function(){
17745         
17746         return  this.hiddenField ?
17747                 this.hiddenField.value :
17748                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17749     },
17750
17751     /**
17752      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17753      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17754      * (the default format used is "m/d/y").
17755      * <br />Usage:
17756      * <pre><code>
17757 //All of these calls set the same date value (May 4, 2006)
17758
17759 //Pass a date object:
17760 var dt = new Date('5/4/06');
17761 dateField.setValue(dt);
17762
17763 //Pass a date string (default format):
17764 dateField.setValue('5/4/06');
17765
17766 //Pass a date string (custom format):
17767 dateField.format = 'Y-m-d';
17768 dateField.setValue('2006-5-4');
17769 </code></pre>
17770      * @param {String/Date} date The date or valid date string
17771      */
17772     setValue : function(date){
17773         if (this.hiddenField) {
17774             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17775         }
17776         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17777         // make sure the value field is always stored as a date..
17778         this.value = this.parseDate(date);
17779         
17780         
17781     },
17782
17783     // private
17784     parseDate : function(value){
17785                 
17786                 if (value instanceof Date) {
17787                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17788                                 return  '';
17789                         }
17790                         return value;
17791                 }
17792                 
17793                 
17794         if(!value || value instanceof Date){
17795             return value;
17796         }
17797         var v = Date.parseDate(value, this.format);
17798          if (!v && this.useIso) {
17799             v = Date.parseDate(value, 'Y-m-d');
17800         }
17801         if(!v && this.altFormats){
17802             if(!this.altFormatsArray){
17803                 this.altFormatsArray = this.altFormats.split("|");
17804             }
17805             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17806                 v = Date.parseDate(value, this.altFormatsArray[i]);
17807             }
17808         }
17809                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17810                         v = '';
17811                 }
17812         return v;
17813     },
17814
17815     // private
17816     formatDate : function(date, fmt){
17817         return (!date || !(date instanceof Date)) ?
17818                date : date.dateFormat(fmt || this.format);
17819     },
17820
17821     // private
17822     menuListeners : {
17823         select: function(m, d){
17824             
17825             this.setValue(d);
17826             this.fireEvent('select', this, d);
17827         },
17828         show : function(){ // retain focus styling
17829             this.onFocus();
17830         },
17831         hide : function(){
17832             this.focus.defer(10, this);
17833             var ml = this.menuListeners;
17834             this.menu.un("select", ml.select,  this);
17835             this.menu.un("show", ml.show,  this);
17836             this.menu.un("hide", ml.hide,  this);
17837         }
17838     },
17839
17840     // private
17841     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17842     onTriggerClick : function(){
17843         if(this.disabled){
17844             return;
17845         }
17846         if(this.menu == null){
17847             this.menu = new Roo.menu.DateMenu();
17848         }
17849         Roo.apply(this.menu.picker,  {
17850             showClear: this.allowBlank,
17851             minDate : this.minValue,
17852             maxDate : this.maxValue,
17853             disabledDatesRE : this.ddMatch,
17854             disabledDatesText : this.disabledDatesText,
17855             disabledDays : this.disabledDays,
17856             disabledDaysText : this.disabledDaysText,
17857             format : this.useIso ? 'Y-m-d' : this.format,
17858             minText : String.format(this.minText, this.formatDate(this.minValue)),
17859             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17860         });
17861         this.menu.on(Roo.apply({}, this.menuListeners, {
17862             scope:this
17863         }));
17864         this.menu.picker.setValue(this.getValue() || new Date());
17865         this.menu.show(this.el, "tl-bl?");
17866     },
17867
17868     beforeBlur : function(){
17869         var v = this.parseDate(this.getRawValue());
17870         if(v){
17871             this.setValue(v);
17872         }
17873     },
17874
17875     /*@
17876      * overide
17877      * 
17878      */
17879     isDirty : function() {
17880         if(this.disabled) {
17881             return false;
17882         }
17883         
17884         if(typeof(this.startValue) === 'undefined'){
17885             return false;
17886         }
17887         
17888         return String(this.getValue()) !== String(this.startValue);
17889         
17890     },
17891     // @overide
17892     cleanLeadingSpace : function(e)
17893     {
17894        return;
17895     }
17896     
17897 });/*
17898  * Based on:
17899  * Ext JS Library 1.1.1
17900  * Copyright(c) 2006-2007, Ext JS, LLC.
17901  *
17902  * Originally Released Under LGPL - original licence link has changed is not relivant.
17903  *
17904  * Fork - LGPL
17905  * <script type="text/javascript">
17906  */
17907  
17908 /**
17909  * @class Roo.form.MonthField
17910  * @extends Roo.form.TriggerField
17911  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17912 * @constructor
17913 * Create a new MonthField
17914 * @param {Object} config
17915  */
17916 Roo.form.MonthField = function(config){
17917     
17918     Roo.form.MonthField.superclass.constructor.call(this, config);
17919     
17920       this.addEvents({
17921          
17922         /**
17923          * @event select
17924          * Fires when a date is selected
17925              * @param {Roo.form.MonthFieeld} combo This combo box
17926              * @param {Date} date The date selected
17927              */
17928         'select' : true
17929          
17930     });
17931     
17932     
17933     if(typeof this.minValue == "string") {
17934         this.minValue = this.parseDate(this.minValue);
17935     }
17936     if(typeof this.maxValue == "string") {
17937         this.maxValue = this.parseDate(this.maxValue);
17938     }
17939     this.ddMatch = null;
17940     if(this.disabledDates){
17941         var dd = this.disabledDates;
17942         var re = "(?:";
17943         for(var i = 0; i < dd.length; i++){
17944             re += dd[i];
17945             if(i != dd.length-1) {
17946                 re += "|";
17947             }
17948         }
17949         this.ddMatch = new RegExp(re + ")");
17950     }
17951 };
17952
17953 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17954     /**
17955      * @cfg {String} format
17956      * The default date format string which can be overriden for localization support.  The format must be
17957      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17958      */
17959     format : "M Y",
17960     /**
17961      * @cfg {String} altFormats
17962      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17963      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17964      */
17965     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17966     /**
17967      * @cfg {Array} disabledDays
17968      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17969      */
17970     disabledDays : [0,1,2,3,4,5,6],
17971     /**
17972      * @cfg {String} disabledDaysText
17973      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17974      */
17975     disabledDaysText : "Disabled",
17976     /**
17977      * @cfg {Array} disabledDates
17978      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17979      * expression so they are very powerful. Some examples:
17980      * <ul>
17981      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17982      * <li>["03/08", "09/16"] would disable those days for every year</li>
17983      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17984      * <li>["03/../2006"] would disable every day in March 2006</li>
17985      * <li>["^03"] would disable every day in every March</li>
17986      * </ul>
17987      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17988      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17989      */
17990     disabledDates : null,
17991     /**
17992      * @cfg {String} disabledDatesText
17993      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17994      */
17995     disabledDatesText : "Disabled",
17996     /**
17997      * @cfg {Date/String} minValue
17998      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17999      * valid format (defaults to null).
18000      */
18001     minValue : null,
18002     /**
18003      * @cfg {Date/String} maxValue
18004      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18005      * valid format (defaults to null).
18006      */
18007     maxValue : null,
18008     /**
18009      * @cfg {String} minText
18010      * The error text to display when the date in the cell is before minValue (defaults to
18011      * 'The date in this field must be after {minValue}').
18012      */
18013     minText : "The date in this field must be equal to or after {0}",
18014     /**
18015      * @cfg {String} maxTextf
18016      * The error text to display when the date in the cell is after maxValue (defaults to
18017      * 'The date in this field must be before {maxValue}').
18018      */
18019     maxText : "The date in this field must be equal to or before {0}",
18020     /**
18021      * @cfg {String} invalidText
18022      * The error text to display when the date in the field is invalid (defaults to
18023      * '{value} is not a valid date - it must be in the format {format}').
18024      */
18025     invalidText : "{0} is not a valid date - it must be in the format {1}",
18026     /**
18027      * @cfg {String} triggerClass
18028      * An additional CSS class used to style the trigger button.  The trigger will always get the
18029      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18030      * which displays a calendar icon).
18031      */
18032     triggerClass : 'x-form-date-trigger',
18033     
18034
18035     /**
18036      * @cfg {Boolean} useIso
18037      * if enabled, then the date field will use a hidden field to store the 
18038      * real value as iso formated date. default (true)
18039      */ 
18040     useIso : true,
18041     /**
18042      * @cfg {String/Object} autoCreate
18043      * A DomHelper element spec, or true for a default element spec (defaults to
18044      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18045      */ 
18046     // private
18047     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18048     
18049     // private
18050     hiddenField: false,
18051     
18052     hideMonthPicker : false,
18053     
18054     onRender : function(ct, position)
18055     {
18056         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18057         if (this.useIso) {
18058             this.el.dom.removeAttribute('name'); 
18059             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18060                     'before', true);
18061             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18062             // prevent input submission
18063             this.hiddenName = this.name;
18064         }
18065             
18066             
18067     },
18068     
18069     // private
18070     validateValue : function(value)
18071     {
18072         value = this.formatDate(value);
18073         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18074             return false;
18075         }
18076         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18077              return true;
18078         }
18079         var svalue = value;
18080         value = this.parseDate(value);
18081         if(!value){
18082             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18083             return false;
18084         }
18085         var time = value.getTime();
18086         if(this.minValue && time < this.minValue.getTime()){
18087             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18088             return false;
18089         }
18090         if(this.maxValue && time > this.maxValue.getTime()){
18091             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18092             return false;
18093         }
18094         /*if(this.disabledDays){
18095             var day = value.getDay();
18096             for(var i = 0; i < this.disabledDays.length; i++) {
18097                 if(day === this.disabledDays[i]){
18098                     this.markInvalid(this.disabledDaysText);
18099                     return false;
18100                 }
18101             }
18102         }
18103         */
18104         var fvalue = this.formatDate(value);
18105         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18106             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18107             return false;
18108         }
18109         */
18110         return true;
18111     },
18112
18113     // private
18114     // Provides logic to override the default TriggerField.validateBlur which just returns true
18115     validateBlur : function(){
18116         return !this.menu || !this.menu.isVisible();
18117     },
18118
18119     /**
18120      * Returns the current date value of the date field.
18121      * @return {Date} The date value
18122      */
18123     getValue : function(){
18124         
18125         
18126         
18127         return  this.hiddenField ?
18128                 this.hiddenField.value :
18129                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18130     },
18131
18132     /**
18133      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18134      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18135      * (the default format used is "m/d/y").
18136      * <br />Usage:
18137      * <pre><code>
18138 //All of these calls set the same date value (May 4, 2006)
18139
18140 //Pass a date object:
18141 var dt = new Date('5/4/06');
18142 monthField.setValue(dt);
18143
18144 //Pass a date string (default format):
18145 monthField.setValue('5/4/06');
18146
18147 //Pass a date string (custom format):
18148 monthField.format = 'Y-m-d';
18149 monthField.setValue('2006-5-4');
18150 </code></pre>
18151      * @param {String/Date} date The date or valid date string
18152      */
18153     setValue : function(date){
18154         Roo.log('month setValue' + date);
18155         // can only be first of month..
18156         
18157         var val = this.parseDate(date);
18158         
18159         if (this.hiddenField) {
18160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18161         }
18162         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18163         this.value = this.parseDate(date);
18164     },
18165
18166     // private
18167     parseDate : function(value){
18168         if(!value || value instanceof Date){
18169             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18170             return value;
18171         }
18172         var v = Date.parseDate(value, this.format);
18173         if (!v && this.useIso) {
18174             v = Date.parseDate(value, 'Y-m-d');
18175         }
18176         if (v) {
18177             // 
18178             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18179         }
18180         
18181         
18182         if(!v && this.altFormats){
18183             if(!this.altFormatsArray){
18184                 this.altFormatsArray = this.altFormats.split("|");
18185             }
18186             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18187                 v = Date.parseDate(value, this.altFormatsArray[i]);
18188             }
18189         }
18190         return v;
18191     },
18192
18193     // private
18194     formatDate : function(date, fmt){
18195         return (!date || !(date instanceof Date)) ?
18196                date : date.dateFormat(fmt || this.format);
18197     },
18198
18199     // private
18200     menuListeners : {
18201         select: function(m, d){
18202             this.setValue(d);
18203             this.fireEvent('select', this, d);
18204         },
18205         show : function(){ // retain focus styling
18206             this.onFocus();
18207         },
18208         hide : function(){
18209             this.focus.defer(10, this);
18210             var ml = this.menuListeners;
18211             this.menu.un("select", ml.select,  this);
18212             this.menu.un("show", ml.show,  this);
18213             this.menu.un("hide", ml.hide,  this);
18214         }
18215     },
18216     // private
18217     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18218     onTriggerClick : function(){
18219         if(this.disabled){
18220             return;
18221         }
18222         if(this.menu == null){
18223             this.menu = new Roo.menu.DateMenu();
18224            
18225         }
18226         
18227         Roo.apply(this.menu.picker,  {
18228             
18229             showClear: this.allowBlank,
18230             minDate : this.minValue,
18231             maxDate : this.maxValue,
18232             disabledDatesRE : this.ddMatch,
18233             disabledDatesText : this.disabledDatesText,
18234             
18235             format : this.useIso ? 'Y-m-d' : this.format,
18236             minText : String.format(this.minText, this.formatDate(this.minValue)),
18237             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18238             
18239         });
18240          this.menu.on(Roo.apply({}, this.menuListeners, {
18241             scope:this
18242         }));
18243        
18244         
18245         var m = this.menu;
18246         var p = m.picker;
18247         
18248         // hide month picker get's called when we called by 'before hide';
18249         
18250         var ignorehide = true;
18251         p.hideMonthPicker  = function(disableAnim){
18252             if (ignorehide) {
18253                 return;
18254             }
18255              if(this.monthPicker){
18256                 Roo.log("hideMonthPicker called");
18257                 if(disableAnim === true){
18258                     this.monthPicker.hide();
18259                 }else{
18260                     this.monthPicker.slideOut('t', {duration:.2});
18261                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18262                     p.fireEvent("select", this, this.value);
18263                     m.hide();
18264                 }
18265             }
18266         }
18267         
18268         Roo.log('picker set value');
18269         Roo.log(this.getValue());
18270         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18271         m.show(this.el, 'tl-bl?');
18272         ignorehide  = false;
18273         // this will trigger hideMonthPicker..
18274         
18275         
18276         // hidden the day picker
18277         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18278         
18279         
18280         
18281       
18282         
18283         p.showMonthPicker.defer(100, p);
18284     
18285         
18286        
18287     },
18288
18289     beforeBlur : function(){
18290         var v = this.parseDate(this.getRawValue());
18291         if(v){
18292             this.setValue(v);
18293         }
18294     }
18295
18296     /** @cfg {Boolean} grow @hide */
18297     /** @cfg {Number} growMin @hide */
18298     /** @cfg {Number} growMax @hide */
18299     /**
18300      * @hide
18301      * @method autoSize
18302      */
18303 });/*
18304  * Based on:
18305  * Ext JS Library 1.1.1
18306  * Copyright(c) 2006-2007, Ext JS, LLC.
18307  *
18308  * Originally Released Under LGPL - original licence link has changed is not relivant.
18309  *
18310  * Fork - LGPL
18311  * <script type="text/javascript">
18312  */
18313  
18314
18315 /**
18316  * @class Roo.form.ComboBox
18317  * @extends Roo.form.TriggerField
18318  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18319  * @constructor
18320  * Create a new ComboBox.
18321  * @param {Object} config Configuration options
18322  */
18323 Roo.form.ComboBox = function(config){
18324     Roo.form.ComboBox.superclass.constructor.call(this, config);
18325     this.addEvents({
18326         /**
18327          * @event expand
18328          * Fires when the dropdown list is expanded
18329              * @param {Roo.form.ComboBox} combo This combo box
18330              */
18331         'expand' : true,
18332         /**
18333          * @event collapse
18334          * Fires when the dropdown list is collapsed
18335              * @param {Roo.form.ComboBox} combo This combo box
18336              */
18337         'collapse' : true,
18338         /**
18339          * @event beforeselect
18340          * Fires before a list item is selected. Return false to cancel the selection.
18341              * @param {Roo.form.ComboBox} combo This combo box
18342              * @param {Roo.data.Record} record The data record returned from the underlying store
18343              * @param {Number} index The index of the selected item in the dropdown list
18344              */
18345         'beforeselect' : true,
18346         /**
18347          * @event select
18348          * Fires when a list item is selected
18349              * @param {Roo.form.ComboBox} combo This combo box
18350              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18351              * @param {Number} index The index of the selected item in the dropdown list
18352              */
18353         'select' : true,
18354         /**
18355          * @event beforequery
18356          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18357          * The event object passed has these properties:
18358              * @param {Roo.form.ComboBox} combo This combo box
18359              * @param {String} query The query
18360              * @param {Boolean} forceAll true to force "all" query
18361              * @param {Boolean} cancel true to cancel the query
18362              * @param {Object} e The query event object
18363              */
18364         'beforequery': true,
18365          /**
18366          * @event add
18367          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18368              * @param {Roo.form.ComboBox} combo This combo box
18369              */
18370         'add' : true,
18371         /**
18372          * @event edit
18373          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18374              * @param {Roo.form.ComboBox} combo This combo box
18375              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18376              */
18377         'edit' : true
18378         
18379         
18380     });
18381     if(this.transform){
18382         this.allowDomMove = false;
18383         var s = Roo.getDom(this.transform);
18384         if(!this.hiddenName){
18385             this.hiddenName = s.name;
18386         }
18387         if(!this.store){
18388             this.mode = 'local';
18389             var d = [], opts = s.options;
18390             for(var i = 0, len = opts.length;i < len; i++){
18391                 var o = opts[i];
18392                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18393                 if(o.selected) {
18394                     this.value = value;
18395                 }
18396                 d.push([value, o.text]);
18397             }
18398             this.store = new Roo.data.SimpleStore({
18399                 'id': 0,
18400                 fields: ['value', 'text'],
18401                 data : d
18402             });
18403             this.valueField = 'value';
18404             this.displayField = 'text';
18405         }
18406         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18407         if(!this.lazyRender){
18408             this.target = true;
18409             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18410             s.parentNode.removeChild(s); // remove it
18411             this.render(this.el.parentNode);
18412         }else{
18413             s.parentNode.removeChild(s); // remove it
18414         }
18415
18416     }
18417     if (this.store) {
18418         this.store = Roo.factory(this.store, Roo.data);
18419     }
18420     
18421     this.selectedIndex = -1;
18422     if(this.mode == 'local'){
18423         if(config.queryDelay === undefined){
18424             this.queryDelay = 10;
18425         }
18426         if(config.minChars === undefined){
18427             this.minChars = 0;
18428         }
18429     }
18430 };
18431
18432 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18433     /**
18434      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18435      */
18436     /**
18437      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18438      * rendering into an Roo.Editor, defaults to false)
18439      */
18440     /**
18441      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18442      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18443      */
18444     /**
18445      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18446      */
18447     /**
18448      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18449      * the dropdown list (defaults to undefined, with no header element)
18450      */
18451
18452      /**
18453      * @cfg {String/Roo.Template} tpl The template to use to render the output
18454      */
18455      
18456     // private
18457     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18458     /**
18459      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18460      */
18461     listWidth: undefined,
18462     /**
18463      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18464      * mode = 'remote' or 'text' if mode = 'local')
18465      */
18466     displayField: undefined,
18467     /**
18468      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18469      * mode = 'remote' or 'value' if mode = 'local'). 
18470      * Note: use of a valueField requires the user make a selection
18471      * in order for a value to be mapped.
18472      */
18473     valueField: undefined,
18474     
18475     
18476     /**
18477      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18478      * field's data value (defaults to the underlying DOM element's name)
18479      */
18480     hiddenName: undefined,
18481     /**
18482      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18483      */
18484     listClass: '',
18485     /**
18486      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18487      */
18488     selectedClass: 'x-combo-selected',
18489     /**
18490      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18491      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18492      * which displays a downward arrow icon).
18493      */
18494     triggerClass : 'x-form-arrow-trigger',
18495     /**
18496      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18497      */
18498     shadow:'sides',
18499     /**
18500      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18501      * anchor positions (defaults to 'tl-bl')
18502      */
18503     listAlign: 'tl-bl?',
18504     /**
18505      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18506      */
18507     maxHeight: 300,
18508     /**
18509      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18510      * query specified by the allQuery config option (defaults to 'query')
18511      */
18512     triggerAction: 'query',
18513     /**
18514      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18515      * (defaults to 4, does not apply if editable = false)
18516      */
18517     minChars : 4,
18518     /**
18519      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18520      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18521      */
18522     typeAhead: false,
18523     /**
18524      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18525      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18526      */
18527     queryDelay: 500,
18528     /**
18529      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18530      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18531      */
18532     pageSize: 0,
18533     /**
18534      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18535      * when editable = true (defaults to false)
18536      */
18537     selectOnFocus:false,
18538     /**
18539      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18540      */
18541     queryParam: 'query',
18542     /**
18543      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18544      * when mode = 'remote' (defaults to 'Loading...')
18545      */
18546     loadingText: 'Loading...',
18547     /**
18548      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18549      */
18550     resizable: false,
18551     /**
18552      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18553      */
18554     handleHeight : 8,
18555     /**
18556      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18557      * traditional select (defaults to true)
18558      */
18559     editable: true,
18560     /**
18561      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18562      */
18563     allQuery: '',
18564     /**
18565      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18566      */
18567     mode: 'remote',
18568     /**
18569      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18570      * listWidth has a higher value)
18571      */
18572     minListWidth : 70,
18573     /**
18574      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18575      * allow the user to set arbitrary text into the field (defaults to false)
18576      */
18577     forceSelection:false,
18578     /**
18579      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18580      * if typeAhead = true (defaults to 250)
18581      */
18582     typeAheadDelay : 250,
18583     /**
18584      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18585      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18586      */
18587     valueNotFoundText : undefined,
18588     /**
18589      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18590      */
18591     blockFocus : false,
18592     
18593     /**
18594      * @cfg {Boolean} disableClear Disable showing of clear button.
18595      */
18596     disableClear : false,
18597     /**
18598      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18599      */
18600     alwaysQuery : false,
18601     
18602     //private
18603     addicon : false,
18604     editicon: false,
18605     
18606     // element that contains real text value.. (when hidden is used..)
18607      
18608     // private
18609     onRender : function(ct, position)
18610     {
18611         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18612         
18613         if(this.hiddenName){
18614             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18615                     'before', true);
18616             this.hiddenField.value =
18617                 this.hiddenValue !== undefined ? this.hiddenValue :
18618                 this.value !== undefined ? this.value : '';
18619
18620             // prevent input submission
18621             this.el.dom.removeAttribute('name');
18622              
18623              
18624         }
18625         
18626         if(Roo.isGecko){
18627             this.el.dom.setAttribute('autocomplete', 'off');
18628         }
18629
18630         var cls = 'x-combo-list';
18631
18632         this.list = new Roo.Layer({
18633             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18634         });
18635
18636         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18637         this.list.setWidth(lw);
18638         this.list.swallowEvent('mousewheel');
18639         this.assetHeight = 0;
18640
18641         if(this.title){
18642             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18643             this.assetHeight += this.header.getHeight();
18644         }
18645
18646         this.innerList = this.list.createChild({cls:cls+'-inner'});
18647         this.innerList.on('mouseover', this.onViewOver, this);
18648         this.innerList.on('mousemove', this.onViewMove, this);
18649         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18650         
18651         if(this.allowBlank && !this.pageSize && !this.disableClear){
18652             this.footer = this.list.createChild({cls:cls+'-ft'});
18653             this.pageTb = new Roo.Toolbar(this.footer);
18654            
18655         }
18656         if(this.pageSize){
18657             this.footer = this.list.createChild({cls:cls+'-ft'});
18658             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18659                     {pageSize: this.pageSize});
18660             
18661         }
18662         
18663         if (this.pageTb && this.allowBlank && !this.disableClear) {
18664             var _this = this;
18665             this.pageTb.add(new Roo.Toolbar.Fill(), {
18666                 cls: 'x-btn-icon x-btn-clear',
18667                 text: '&#160;',
18668                 handler: function()
18669                 {
18670                     _this.collapse();
18671                     _this.clearValue();
18672                     _this.onSelect(false, -1);
18673                 }
18674             });
18675         }
18676         if (this.footer) {
18677             this.assetHeight += this.footer.getHeight();
18678         }
18679         
18680
18681         if(!this.tpl){
18682             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18683         }
18684
18685         this.view = new Roo.View(this.innerList, this.tpl, {
18686             singleSelect:true,
18687             store: this.store,
18688             selectedClass: this.selectedClass
18689         });
18690
18691         this.view.on('click', this.onViewClick, this);
18692
18693         this.store.on('beforeload', this.onBeforeLoad, this);
18694         this.store.on('load', this.onLoad, this);
18695         this.store.on('loadexception', this.onLoadException, this);
18696
18697         if(this.resizable){
18698             this.resizer = new Roo.Resizable(this.list,  {
18699                pinned:true, handles:'se'
18700             });
18701             this.resizer.on('resize', function(r, w, h){
18702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18703                 this.listWidth = w;
18704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18705                 this.restrictHeight();
18706             }, this);
18707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18708         }
18709         if(!this.editable){
18710             this.editable = true;
18711             this.setEditable(false);
18712         }  
18713         
18714         
18715         if (typeof(this.events.add.listeners) != 'undefined') {
18716             
18717             this.addicon = this.wrap.createChild(
18718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18719        
18720             this.addicon.on('click', function(e) {
18721                 this.fireEvent('add', this);
18722             }, this);
18723         }
18724         if (typeof(this.events.edit.listeners) != 'undefined') {
18725             
18726             this.editicon = this.wrap.createChild(
18727                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18728             if (this.addicon) {
18729                 this.editicon.setStyle('margin-left', '40px');
18730             }
18731             this.editicon.on('click', function(e) {
18732                 
18733                 // we fire even  if inothing is selected..
18734                 this.fireEvent('edit', this, this.lastData );
18735                 
18736             }, this);
18737         }
18738         
18739         
18740         
18741     },
18742
18743     // private
18744     initEvents : function(){
18745         Roo.form.ComboBox.superclass.initEvents.call(this);
18746
18747         this.keyNav = new Roo.KeyNav(this.el, {
18748             "up" : function(e){
18749                 this.inKeyMode = true;
18750                 this.selectPrev();
18751             },
18752
18753             "down" : function(e){
18754                 if(!this.isExpanded()){
18755                     this.onTriggerClick();
18756                 }else{
18757                     this.inKeyMode = true;
18758                     this.selectNext();
18759                 }
18760             },
18761
18762             "enter" : function(e){
18763                 this.onViewClick();
18764                 //return true;
18765             },
18766
18767             "esc" : function(e){
18768                 this.collapse();
18769             },
18770
18771             "tab" : function(e){
18772                 this.onViewClick(false);
18773                 this.fireEvent("specialkey", this, e);
18774                 return true;
18775             },
18776
18777             scope : this,
18778
18779             doRelay : function(foo, bar, hname){
18780                 if(hname == 'down' || this.scope.isExpanded()){
18781                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18782                 }
18783                 return true;
18784             },
18785
18786             forceKeyDown: true
18787         });
18788         this.queryDelay = Math.max(this.queryDelay || 10,
18789                 this.mode == 'local' ? 10 : 250);
18790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18791         if(this.typeAhead){
18792             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18793         }
18794         if(this.editable !== false){
18795             this.el.on("keyup", this.onKeyUp, this);
18796         }
18797         if(this.forceSelection){
18798             this.on('blur', this.doForce, this);
18799         }
18800     },
18801
18802     onDestroy : function(){
18803         if(this.view){
18804             this.view.setStore(null);
18805             this.view.el.removeAllListeners();
18806             this.view.el.remove();
18807             this.view.purgeListeners();
18808         }
18809         if(this.list){
18810             this.list.destroy();
18811         }
18812         if(this.store){
18813             this.store.un('beforeload', this.onBeforeLoad, this);
18814             this.store.un('load', this.onLoad, this);
18815             this.store.un('loadexception', this.onLoadException, this);
18816         }
18817         Roo.form.ComboBox.superclass.onDestroy.call(this);
18818     },
18819
18820     // private
18821     fireKey : function(e){
18822         if(e.isNavKeyPress() && !this.list.isVisible()){
18823             this.fireEvent("specialkey", this, e);
18824         }
18825     },
18826
18827     // private
18828     onResize: function(w, h){
18829         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18830         
18831         if(typeof w != 'number'){
18832             // we do not handle it!?!?
18833             return;
18834         }
18835         var tw = this.trigger.getWidth();
18836         tw += this.addicon ? this.addicon.getWidth() : 0;
18837         tw += this.editicon ? this.editicon.getWidth() : 0;
18838         var x = w - tw;
18839         this.el.setWidth( this.adjustWidth('input', x));
18840             
18841         this.trigger.setStyle('left', x+'px');
18842         
18843         if(this.list && this.listWidth === undefined){
18844             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18845             this.list.setWidth(lw);
18846             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18847         }
18848         
18849     
18850         
18851     },
18852
18853     /**
18854      * Allow or prevent the user from directly editing the field text.  If false is passed,
18855      * the user will only be able to select from the items defined in the dropdown list.  This method
18856      * is the runtime equivalent of setting the 'editable' config option at config time.
18857      * @param {Boolean} value True to allow the user to directly edit the field text
18858      */
18859     setEditable : function(value){
18860         if(value == this.editable){
18861             return;
18862         }
18863         this.editable = value;
18864         if(!value){
18865             this.el.dom.setAttribute('readOnly', true);
18866             this.el.on('mousedown', this.onTriggerClick,  this);
18867             this.el.addClass('x-combo-noedit');
18868         }else{
18869             this.el.dom.setAttribute('readOnly', false);
18870             this.el.un('mousedown', this.onTriggerClick,  this);
18871             this.el.removeClass('x-combo-noedit');
18872         }
18873     },
18874
18875     // private
18876     onBeforeLoad : function(){
18877         if(!this.hasFocus){
18878             return;
18879         }
18880         this.innerList.update(this.loadingText ?
18881                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18882         this.restrictHeight();
18883         this.selectedIndex = -1;
18884     },
18885
18886     // private
18887     onLoad : function(){
18888         if(!this.hasFocus){
18889             return;
18890         }
18891         if(this.store.getCount() > 0){
18892             this.expand();
18893             this.restrictHeight();
18894             if(this.lastQuery == this.allQuery){
18895                 if(this.editable){
18896                     this.el.dom.select();
18897                 }
18898                 if(!this.selectByValue(this.value, true)){
18899                     this.select(0, true);
18900                 }
18901             }else{
18902                 this.selectNext();
18903                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18904                     this.taTask.delay(this.typeAheadDelay);
18905                 }
18906             }
18907         }else{
18908             this.onEmptyResults();
18909         }
18910         //this.el.focus();
18911     },
18912     // private
18913     onLoadException : function()
18914     {
18915         this.collapse();
18916         Roo.log(this.store.reader.jsonData);
18917         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18918             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18919         }
18920         
18921         
18922     },
18923     // private
18924     onTypeAhead : function(){
18925         if(this.store.getCount() > 0){
18926             var r = this.store.getAt(0);
18927             var newValue = r.data[this.displayField];
18928             var len = newValue.length;
18929             var selStart = this.getRawValue().length;
18930             if(selStart != len){
18931                 this.setRawValue(newValue);
18932                 this.selectText(selStart, newValue.length);
18933             }
18934         }
18935     },
18936
18937     // private
18938     onSelect : function(record, index){
18939         if(this.fireEvent('beforeselect', this, record, index) !== false){
18940             this.setFromData(index > -1 ? record.data : false);
18941             this.collapse();
18942             this.fireEvent('select', this, record, index);
18943         }
18944     },
18945
18946     /**
18947      * Returns the currently selected field value or empty string if no value is set.
18948      * @return {String} value The selected value
18949      */
18950     getValue : function(){
18951         if(this.valueField){
18952             return typeof this.value != 'undefined' ? this.value : '';
18953         }
18954         return Roo.form.ComboBox.superclass.getValue.call(this);
18955     },
18956
18957     /**
18958      * Clears any text/value currently set in the field
18959      */
18960     clearValue : function(){
18961         if(this.hiddenField){
18962             this.hiddenField.value = '';
18963         }
18964         this.value = '';
18965         this.setRawValue('');
18966         this.lastSelectionText = '';
18967         
18968     },
18969
18970     /**
18971      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18972      * will be displayed in the field.  If the value does not match the data value of an existing item,
18973      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18974      * Otherwise the field will be blank (although the value will still be set).
18975      * @param {String} value The value to match
18976      */
18977     setValue : function(v){
18978         var text = v;
18979         if(this.valueField){
18980             var r = this.findRecord(this.valueField, v);
18981             if(r){
18982                 text = r.data[this.displayField];
18983             }else if(this.valueNotFoundText !== undefined){
18984                 text = this.valueNotFoundText;
18985             }
18986         }
18987         this.lastSelectionText = text;
18988         if(this.hiddenField){
18989             this.hiddenField.value = v;
18990         }
18991         Roo.form.ComboBox.superclass.setValue.call(this, text);
18992         this.value = v;
18993     },
18994     /**
18995      * @property {Object} the last set data for the element
18996      */
18997     
18998     lastData : false,
18999     /**
19000      * Sets the value of the field based on a object which is related to the record format for the store.
19001      * @param {Object} value the value to set as. or false on reset?
19002      */
19003     setFromData : function(o){
19004         var dv = ''; // display value
19005         var vv = ''; // value value..
19006         this.lastData = o;
19007         if (this.displayField) {
19008             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19009         } else {
19010             // this is an error condition!!!
19011             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19012         }
19013         
19014         if(this.valueField){
19015             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19016         }
19017         if(this.hiddenField){
19018             this.hiddenField.value = vv;
19019             
19020             this.lastSelectionText = dv;
19021             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19022             this.value = vv;
19023             return;
19024         }
19025         // no hidden field.. - we store the value in 'value', but still display
19026         // display field!!!!
19027         this.lastSelectionText = dv;
19028         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19029         this.value = vv;
19030         
19031         
19032     },
19033     // private
19034     reset : function(){
19035         // overridden so that last data is reset..
19036         this.setValue(this.resetValue);
19037         this.originalValue = this.getValue();
19038         this.clearInvalid();
19039         this.lastData = false;
19040         if (this.view) {
19041             this.view.clearSelections();
19042         }
19043     },
19044     // private
19045     findRecord : function(prop, value){
19046         var record;
19047         if(this.store.getCount() > 0){
19048             this.store.each(function(r){
19049                 if(r.data[prop] == value){
19050                     record = r;
19051                     return false;
19052                 }
19053                 return true;
19054             });
19055         }
19056         return record;
19057     },
19058     
19059     getName: function()
19060     {
19061         // returns hidden if it's set..
19062         if (!this.rendered) {return ''};
19063         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19064         
19065     },
19066     // private
19067     onViewMove : function(e, t){
19068         this.inKeyMode = false;
19069     },
19070
19071     // private
19072     onViewOver : function(e, t){
19073         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19074             return;
19075         }
19076         var item = this.view.findItemFromChild(t);
19077         if(item){
19078             var index = this.view.indexOf(item);
19079             this.select(index, false);
19080         }
19081     },
19082
19083     // private
19084     onViewClick : function(doFocus)
19085     {
19086         var index = this.view.getSelectedIndexes()[0];
19087         var r = this.store.getAt(index);
19088         if(r){
19089             this.onSelect(r, index);
19090         }
19091         if(doFocus !== false && !this.blockFocus){
19092             this.el.focus();
19093         }
19094     },
19095
19096     // private
19097     restrictHeight : function(){
19098         this.innerList.dom.style.height = '';
19099         var inner = this.innerList.dom;
19100         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19101         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19102         this.list.beginUpdate();
19103         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19104         this.list.alignTo(this.el, this.listAlign);
19105         this.list.endUpdate();
19106     },
19107
19108     // private
19109     onEmptyResults : function(){
19110         this.collapse();
19111     },
19112
19113     /**
19114      * Returns true if the dropdown list is expanded, else false.
19115      */
19116     isExpanded : function(){
19117         return this.list.isVisible();
19118     },
19119
19120     /**
19121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19123      * @param {String} value The data value of the item to select
19124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19125      * selected item if it is not currently in view (defaults to true)
19126      * @return {Boolean} True if the value matched an item in the list, else false
19127      */
19128     selectByValue : function(v, scrollIntoView){
19129         if(v !== undefined && v !== null){
19130             var r = this.findRecord(this.valueField || this.displayField, v);
19131             if(r){
19132                 this.select(this.store.indexOf(r), scrollIntoView);
19133                 return true;
19134             }
19135         }
19136         return false;
19137     },
19138
19139     /**
19140      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19142      * @param {Number} index The zero-based index of the list item to select
19143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19144      * selected item if it is not currently in view (defaults to true)
19145      */
19146     select : function(index, scrollIntoView){
19147         this.selectedIndex = index;
19148         this.view.select(index);
19149         if(scrollIntoView !== false){
19150             var el = this.view.getNode(index);
19151             if(el){
19152                 this.innerList.scrollChildIntoView(el, false);
19153             }
19154         }
19155     },
19156
19157     // private
19158     selectNext : function(){
19159         var ct = this.store.getCount();
19160         if(ct > 0){
19161             if(this.selectedIndex == -1){
19162                 this.select(0);
19163             }else if(this.selectedIndex < ct-1){
19164                 this.select(this.selectedIndex+1);
19165             }
19166         }
19167     },
19168
19169     // private
19170     selectPrev : function(){
19171         var ct = this.store.getCount();
19172         if(ct > 0){
19173             if(this.selectedIndex == -1){
19174                 this.select(0);
19175             }else if(this.selectedIndex != 0){
19176                 this.select(this.selectedIndex-1);
19177             }
19178         }
19179     },
19180
19181     // private
19182     onKeyUp : function(e){
19183         if(this.editable !== false && !e.isSpecialKey()){
19184             this.lastKey = e.getKey();
19185             this.dqTask.delay(this.queryDelay);
19186         }
19187     },
19188
19189     // private
19190     validateBlur : function(){
19191         return !this.list || !this.list.isVisible();   
19192     },
19193
19194     // private
19195     initQuery : function(){
19196         this.doQuery(this.getRawValue());
19197     },
19198
19199     // private
19200     doForce : function(){
19201         if(this.el.dom.value.length > 0){
19202             this.el.dom.value =
19203                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19204              
19205         }
19206     },
19207
19208     /**
19209      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19210      * query allowing the query action to be canceled if needed.
19211      * @param {String} query The SQL query to execute
19212      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19213      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19214      * saved in the current store (defaults to false)
19215      */
19216     doQuery : function(q, forceAll){
19217         if(q === undefined || q === null){
19218             q = '';
19219         }
19220         var qe = {
19221             query: q,
19222             forceAll: forceAll,
19223             combo: this,
19224             cancel:false
19225         };
19226         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19227             return false;
19228         }
19229         q = qe.query;
19230         forceAll = qe.forceAll;
19231         if(forceAll === true || (q.length >= this.minChars)){
19232             if(this.lastQuery != q || this.alwaysQuery){
19233                 this.lastQuery = q;
19234                 if(this.mode == 'local'){
19235                     this.selectedIndex = -1;
19236                     if(forceAll){
19237                         this.store.clearFilter();
19238                     }else{
19239                         this.store.filter(this.displayField, q);
19240                     }
19241                     this.onLoad();
19242                 }else{
19243                     this.store.baseParams[this.queryParam] = q;
19244                     this.store.load({
19245                         params: this.getParams(q)
19246                     });
19247                     this.expand();
19248                 }
19249             }else{
19250                 this.selectedIndex = -1;
19251                 this.onLoad();   
19252             }
19253         }
19254     },
19255
19256     // private
19257     getParams : function(q){
19258         var p = {};
19259         //p[this.queryParam] = q;
19260         if(this.pageSize){
19261             p.start = 0;
19262             p.limit = this.pageSize;
19263         }
19264         return p;
19265     },
19266
19267     /**
19268      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19269      */
19270     collapse : function(){
19271         if(!this.isExpanded()){
19272             return;
19273         }
19274         this.list.hide();
19275         Roo.get(document).un('mousedown', this.collapseIf, this);
19276         Roo.get(document).un('mousewheel', this.collapseIf, this);
19277         if (!this.editable) {
19278             Roo.get(document).un('keydown', this.listKeyPress, this);
19279         }
19280         this.fireEvent('collapse', this);
19281     },
19282
19283     // private
19284     collapseIf : function(e){
19285         if(!e.within(this.wrap) && !e.within(this.list)){
19286             this.collapse();
19287         }
19288     },
19289
19290     /**
19291      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19292      */
19293     expand : function(){
19294         if(this.isExpanded() || !this.hasFocus){
19295             return;
19296         }
19297         this.list.alignTo(this.el, this.listAlign);
19298         this.list.show();
19299         Roo.get(document).on('mousedown', this.collapseIf, this);
19300         Roo.get(document).on('mousewheel', this.collapseIf, this);
19301         if (!this.editable) {
19302             Roo.get(document).on('keydown', this.listKeyPress, this);
19303         }
19304         
19305         this.fireEvent('expand', this);
19306     },
19307
19308     // private
19309     // Implements the default empty TriggerField.onTriggerClick function
19310     onTriggerClick : function(){
19311         if(this.disabled){
19312             return;
19313         }
19314         if(this.isExpanded()){
19315             this.collapse();
19316             if (!this.blockFocus) {
19317                 this.el.focus();
19318             }
19319             
19320         }else {
19321             this.hasFocus = true;
19322             if(this.triggerAction == 'all') {
19323                 this.doQuery(this.allQuery, true);
19324             } else {
19325                 this.doQuery(this.getRawValue());
19326             }
19327             if (!this.blockFocus) {
19328                 this.el.focus();
19329             }
19330         }
19331     },
19332     listKeyPress : function(e)
19333     {
19334         //Roo.log('listkeypress');
19335         // scroll to first matching element based on key pres..
19336         if (e.isSpecialKey()) {
19337             return false;
19338         }
19339         var k = String.fromCharCode(e.getKey()).toUpperCase();
19340         //Roo.log(k);
19341         var match  = false;
19342         var csel = this.view.getSelectedNodes();
19343         var cselitem = false;
19344         if (csel.length) {
19345             var ix = this.view.indexOf(csel[0]);
19346             cselitem  = this.store.getAt(ix);
19347             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19348                 cselitem = false;
19349             }
19350             
19351         }
19352         
19353         this.store.each(function(v) { 
19354             if (cselitem) {
19355                 // start at existing selection.
19356                 if (cselitem.id == v.id) {
19357                     cselitem = false;
19358                 }
19359                 return;
19360             }
19361                 
19362             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19363                 match = this.store.indexOf(v);
19364                 return false;
19365             }
19366         }, this);
19367         
19368         if (match === false) {
19369             return true; // no more action?
19370         }
19371         // scroll to?
19372         this.view.select(match);
19373         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19374         sn.scrollIntoView(sn.dom.parentNode, false);
19375     } 
19376
19377     /** 
19378     * @cfg {Boolean} grow 
19379     * @hide 
19380     */
19381     /** 
19382     * @cfg {Number} growMin 
19383     * @hide 
19384     */
19385     /** 
19386     * @cfg {Number} growMax 
19387     * @hide 
19388     */
19389     /**
19390      * @hide
19391      * @method autoSize
19392      */
19393 });/*
19394  * Copyright(c) 2010-2012, Roo J Solutions Limited
19395  *
19396  * Licence LGPL
19397  *
19398  */
19399
19400 /**
19401  * @class Roo.form.ComboBoxArray
19402  * @extends Roo.form.TextField
19403  * A facebook style adder... for lists of email / people / countries  etc...
19404  * pick multiple items from a combo box, and shows each one.
19405  *
19406  *  Fred [x]  Brian [x]  [Pick another |v]
19407  *
19408  *
19409  *  For this to work: it needs various extra information
19410  *    - normal combo problay has
19411  *      name, hiddenName
19412  *    + displayField, valueField
19413  *
19414  *    For our purpose...
19415  *
19416  *
19417  *   If we change from 'extends' to wrapping...
19418  *   
19419  *  
19420  *
19421  
19422  
19423  * @constructor
19424  * Create a new ComboBoxArray.
19425  * @param {Object} config Configuration options
19426  */
19427  
19428
19429 Roo.form.ComboBoxArray = function(config)
19430 {
19431     this.addEvents({
19432         /**
19433          * @event beforeremove
19434          * Fires before remove the value from the list
19435              * @param {Roo.form.ComboBoxArray} _self This combo box array
19436              * @param {Roo.form.ComboBoxArray.Item} item removed item
19437              */
19438         'beforeremove' : true,
19439         /**
19440          * @event remove
19441          * Fires when remove the value from the list
19442              * @param {Roo.form.ComboBoxArray} _self This combo box array
19443              * @param {Roo.form.ComboBoxArray.Item} item removed item
19444              */
19445         'remove' : true
19446         
19447         
19448     });
19449     
19450     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19451     
19452     this.items = new Roo.util.MixedCollection(false);
19453     
19454     // construct the child combo...
19455     
19456     
19457     
19458     
19459    
19460     
19461 }
19462
19463  
19464 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19465
19466     /**
19467      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19468      */
19469     
19470     lastData : false,
19471     
19472     // behavies liek a hiddne field
19473     inputType:      'hidden',
19474     /**
19475      * @cfg {Number} width The width of the box that displays the selected element
19476      */ 
19477     width:          300,
19478
19479     
19480     
19481     /**
19482      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19483      */
19484     name : false,
19485     /**
19486      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19487      */
19488     hiddenName : false,
19489       /**
19490      * @cfg {String} seperator    The value seperator normally ',' 
19491      */
19492     seperator : ',',
19493     
19494     // private the array of items that are displayed..
19495     items  : false,
19496     // private - the hidden field el.
19497     hiddenEl : false,
19498     // private - the filed el..
19499     el : false,
19500     
19501     //validateValue : function() { return true; }, // all values are ok!
19502     //onAddClick: function() { },
19503     
19504     onRender : function(ct, position) 
19505     {
19506         
19507         // create the standard hidden element
19508         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19509         
19510         
19511         // give fake names to child combo;
19512         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19513         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19514         
19515         this.combo = Roo.factory(this.combo, Roo.form);
19516         this.combo.onRender(ct, position);
19517         if (typeof(this.combo.width) != 'undefined') {
19518             this.combo.onResize(this.combo.width,0);
19519         }
19520         
19521         this.combo.initEvents();
19522         
19523         // assigned so form know we need to do this..
19524         this.store          = this.combo.store;
19525         this.valueField     = this.combo.valueField;
19526         this.displayField   = this.combo.displayField ;
19527         
19528         
19529         this.combo.wrap.addClass('x-cbarray-grp');
19530         
19531         var cbwrap = this.combo.wrap.createChild(
19532             {tag: 'div', cls: 'x-cbarray-cb'},
19533             this.combo.el.dom
19534         );
19535         
19536              
19537         this.hiddenEl = this.combo.wrap.createChild({
19538             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19539         });
19540         this.el = this.combo.wrap.createChild({
19541             tag: 'input',  type:'hidden' , name: this.name, value : ''
19542         });
19543          //   this.el.dom.removeAttribute("name");
19544         
19545         
19546         this.outerWrap = this.combo.wrap;
19547         this.wrap = cbwrap;
19548         
19549         this.outerWrap.setWidth(this.width);
19550         this.outerWrap.dom.removeChild(this.el.dom);
19551         
19552         this.wrap.dom.appendChild(this.el.dom);
19553         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19554         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19555         
19556         this.combo.trigger.setStyle('position','relative');
19557         this.combo.trigger.setStyle('left', '0px');
19558         this.combo.trigger.setStyle('top', '2px');
19559         
19560         this.combo.el.setStyle('vertical-align', 'text-bottom');
19561         
19562         //this.trigger.setStyle('vertical-align', 'top');
19563         
19564         // this should use the code from combo really... on('add' ....)
19565         if (this.adder) {
19566             
19567         
19568             this.adder = this.outerWrap.createChild(
19569                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19570             var _t = this;
19571             this.adder.on('click', function(e) {
19572                 _t.fireEvent('adderclick', this, e);
19573             }, _t);
19574         }
19575         //var _t = this;
19576         //this.adder.on('click', this.onAddClick, _t);
19577         
19578         
19579         this.combo.on('select', function(cb, rec, ix) {
19580             this.addItem(rec.data);
19581             
19582             cb.setValue('');
19583             cb.el.dom.value = '';
19584             //cb.lastData = rec.data;
19585             // add to list
19586             
19587         }, this);
19588         
19589         
19590     },
19591     
19592     
19593     getName: function()
19594     {
19595         // returns hidden if it's set..
19596         if (!this.rendered) {return ''};
19597         return  this.hiddenName ? this.hiddenName : this.name;
19598         
19599     },
19600     
19601     
19602     onResize: function(w, h){
19603         
19604         return;
19605         // not sure if this is needed..
19606         //this.combo.onResize(w,h);
19607         
19608         if(typeof w != 'number'){
19609             // we do not handle it!?!?
19610             return;
19611         }
19612         var tw = this.combo.trigger.getWidth();
19613         tw += this.addicon ? this.addicon.getWidth() : 0;
19614         tw += this.editicon ? this.editicon.getWidth() : 0;
19615         var x = w - tw;
19616         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19617             
19618         this.combo.trigger.setStyle('left', '0px');
19619         
19620         if(this.list && this.listWidth === undefined){
19621             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19622             this.list.setWidth(lw);
19623             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19624         }
19625         
19626     
19627         
19628     },
19629     
19630     addItem: function(rec)
19631     {
19632         var valueField = this.combo.valueField;
19633         var displayField = this.combo.displayField;
19634         
19635         if (this.items.indexOfKey(rec[valueField]) > -1) {
19636             //console.log("GOT " + rec.data.id);
19637             return;
19638         }
19639         
19640         var x = new Roo.form.ComboBoxArray.Item({
19641             //id : rec[this.idField],
19642             data : rec,
19643             displayField : displayField ,
19644             tipField : displayField ,
19645             cb : this
19646         });
19647         // use the 
19648         this.items.add(rec[valueField],x);
19649         // add it before the element..
19650         this.updateHiddenEl();
19651         x.render(this.outerWrap, this.wrap.dom);
19652         // add the image handler..
19653     },
19654     
19655     updateHiddenEl : function()
19656     {
19657         this.validate();
19658         if (!this.hiddenEl) {
19659             return;
19660         }
19661         var ar = [];
19662         var idField = this.combo.valueField;
19663         
19664         this.items.each(function(f) {
19665             ar.push(f.data[idField]);
19666         });
19667         this.hiddenEl.dom.value = ar.join(this.seperator);
19668         this.validate();
19669     },
19670     
19671     reset : function()
19672     {
19673         this.items.clear();
19674         
19675         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19676            el.remove();
19677         });
19678         
19679         this.el.dom.value = '';
19680         if (this.hiddenEl) {
19681             this.hiddenEl.dom.value = '';
19682         }
19683         
19684     },
19685     getValue: function()
19686     {
19687         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19688     },
19689     setValue: function(v) // not a valid action - must use addItems..
19690     {
19691         
19692         this.reset();
19693          
19694         if (this.store.isLocal && (typeof(v) == 'string')) {
19695             // then we can use the store to find the values..
19696             // comma seperated at present.. this needs to allow JSON based encoding..
19697             this.hiddenEl.value  = v;
19698             var v_ar = [];
19699             Roo.each(v.split(this.seperator), function(k) {
19700                 Roo.log("CHECK " + this.valueField + ',' + k);
19701                 var li = this.store.query(this.valueField, k);
19702                 if (!li.length) {
19703                     return;
19704                 }
19705                 var add = {};
19706                 add[this.valueField] = k;
19707                 add[this.displayField] = li.item(0).data[this.displayField];
19708                 
19709                 this.addItem(add);
19710             }, this) 
19711              
19712         }
19713         if (typeof(v) == 'object' ) {
19714             // then let's assume it's an array of objects..
19715             Roo.each(v, function(l) {
19716                 var add = l;
19717                 if (typeof(l) == 'string') {
19718                     add = {};
19719                     add[this.valueField] = l;
19720                     add[this.displayField] = l
19721                 }
19722                 this.addItem(add);
19723             }, this);
19724              
19725         }
19726         
19727         
19728     },
19729     setFromData: function(v)
19730     {
19731         // this recieves an object, if setValues is called.
19732         this.reset();
19733         this.el.dom.value = v[this.displayField];
19734         this.hiddenEl.dom.value = v[this.valueField];
19735         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19736             return;
19737         }
19738         var kv = v[this.valueField];
19739         var dv = v[this.displayField];
19740         kv = typeof(kv) != 'string' ? '' : kv;
19741         dv = typeof(dv) != 'string' ? '' : dv;
19742         
19743         
19744         var keys = kv.split(this.seperator);
19745         var display = dv.split(this.seperator);
19746         for (var i = 0 ; i < keys.length; i++) {
19747             add = {};
19748             add[this.valueField] = keys[i];
19749             add[this.displayField] = display[i];
19750             this.addItem(add);
19751         }
19752       
19753         
19754     },
19755     
19756     /**
19757      * Validates the combox array value
19758      * @return {Boolean} True if the value is valid, else false
19759      */
19760     validate : function(){
19761         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19762             this.clearInvalid();
19763             return true;
19764         }
19765         return false;
19766     },
19767     
19768     validateValue : function(value){
19769         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19770         
19771     },
19772     
19773     /*@
19774      * overide
19775      * 
19776      */
19777     isDirty : function() {
19778         if(this.disabled) {
19779             return false;
19780         }
19781         
19782         try {
19783             var d = Roo.decode(String(this.originalValue));
19784         } catch (e) {
19785             return String(this.getValue()) !== String(this.originalValue);
19786         }
19787         
19788         var originalValue = [];
19789         
19790         for (var i = 0; i < d.length; i++){
19791             originalValue.push(d[i][this.valueField]);
19792         }
19793         
19794         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19795         
19796     }
19797     
19798 });
19799
19800
19801
19802 /**
19803  * @class Roo.form.ComboBoxArray.Item
19804  * @extends Roo.BoxComponent
19805  * A selected item in the list
19806  *  Fred [x]  Brian [x]  [Pick another |v]
19807  * 
19808  * @constructor
19809  * Create a new item.
19810  * @param {Object} config Configuration options
19811  */
19812  
19813 Roo.form.ComboBoxArray.Item = function(config) {
19814     config.id = Roo.id();
19815     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19816 }
19817
19818 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19819     data : {},
19820     cb: false,
19821     displayField : false,
19822     tipField : false,
19823     
19824     
19825     defaultAutoCreate : {
19826         tag: 'div',
19827         cls: 'x-cbarray-item',
19828         cn : [ 
19829             { tag: 'div' },
19830             {
19831                 tag: 'img',
19832                 width:16,
19833                 height : 16,
19834                 src : Roo.BLANK_IMAGE_URL ,
19835                 align: 'center'
19836             }
19837         ]
19838         
19839     },
19840     
19841  
19842     onRender : function(ct, position)
19843     {
19844         Roo.form.Field.superclass.onRender.call(this, ct, position);
19845         
19846         if(!this.el){
19847             var cfg = this.getAutoCreate();
19848             this.el = ct.createChild(cfg, position);
19849         }
19850         
19851         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19852         
19853         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19854             this.cb.renderer(this.data) :
19855             String.format('{0}',this.data[this.displayField]);
19856         
19857             
19858         this.el.child('div').dom.setAttribute('qtip',
19859                         String.format('{0}',this.data[this.tipField])
19860         );
19861         
19862         this.el.child('img').on('click', this.remove, this);
19863         
19864     },
19865    
19866     remove : function()
19867     {
19868         if(this.cb.disabled){
19869             return;
19870         }
19871         
19872         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19873             this.cb.items.remove(this);
19874             this.el.child('img').un('click', this.remove, this);
19875             this.el.remove();
19876             this.cb.updateHiddenEl();
19877
19878             this.cb.fireEvent('remove', this.cb, this);
19879         }
19880         
19881     }
19882 });/*
19883  * RooJS Library 1.1.1
19884  * Copyright(c) 2008-2011  Alan Knowles
19885  *
19886  * License - LGPL
19887  */
19888  
19889
19890 /**
19891  * @class Roo.form.ComboNested
19892  * @extends Roo.form.ComboBox
19893  * A combobox for that allows selection of nested items in a list,
19894  * eg.
19895  *
19896  *  Book
19897  *    -> red
19898  *    -> green
19899  *  Table
19900  *    -> square
19901  *      ->red
19902  *      ->green
19903  *    -> rectangle
19904  *      ->green
19905  *      
19906  * 
19907  * @constructor
19908  * Create a new ComboNested
19909  * @param {Object} config Configuration options
19910  */
19911 Roo.form.ComboNested = function(config){
19912     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19913     // should verify some data...
19914     // like
19915     // hiddenName = required..
19916     // displayField = required
19917     // valudField == required
19918     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19919     var _t = this;
19920     Roo.each(req, function(e) {
19921         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19922             throw "Roo.form.ComboNested : missing value for: " + e;
19923         }
19924     });
19925      
19926     
19927 };
19928
19929 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19930    
19931     /*
19932      * @config {Number} max Number of columns to show
19933      */
19934     
19935     maxColumns : 3,
19936    
19937     list : null, // the outermost div..
19938     innerLists : null, // the
19939     views : null,
19940     stores : null,
19941     // private
19942     loadingChildren : false,
19943     
19944     onRender : function(ct, position)
19945     {
19946         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19947         
19948         if(this.hiddenName){
19949             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19950                     'before', true);
19951             this.hiddenField.value =
19952                 this.hiddenValue !== undefined ? this.hiddenValue :
19953                 this.value !== undefined ? this.value : '';
19954
19955             // prevent input submission
19956             this.el.dom.removeAttribute('name');
19957              
19958              
19959         }
19960         
19961         if(Roo.isGecko){
19962             this.el.dom.setAttribute('autocomplete', 'off');
19963         }
19964
19965         var cls = 'x-combo-list';
19966
19967         this.list = new Roo.Layer({
19968             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19969         });
19970
19971         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19972         this.list.setWidth(lw);
19973         this.list.swallowEvent('mousewheel');
19974         this.assetHeight = 0;
19975
19976         if(this.title){
19977             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19978             this.assetHeight += this.header.getHeight();
19979         }
19980         this.innerLists = [];
19981         this.views = [];
19982         this.stores = [];
19983         for (var i =0 ; i < this.maxColumns; i++) {
19984             this.onRenderList( cls, i);
19985         }
19986         
19987         // always needs footer, as we are going to have an 'OK' button.
19988         this.footer = this.list.createChild({cls:cls+'-ft'});
19989         this.pageTb = new Roo.Toolbar(this.footer);  
19990         var _this = this;
19991         this.pageTb.add(  {
19992             
19993             text: 'Done',
19994             handler: function()
19995             {
19996                 _this.collapse();
19997             }
19998         });
19999         
20000         if ( this.allowBlank && !this.disableClear) {
20001             
20002             this.pageTb.add(new Roo.Toolbar.Fill(), {
20003                 cls: 'x-btn-icon x-btn-clear',
20004                 text: '&#160;',
20005                 handler: function()
20006                 {
20007                     _this.collapse();
20008                     _this.clearValue();
20009                     _this.onSelect(false, -1);
20010                 }
20011             });
20012         }
20013         if (this.footer) {
20014             this.assetHeight += this.footer.getHeight();
20015         }
20016         
20017     },
20018     onRenderList : function (  cls, i)
20019     {
20020         
20021         var lw = Math.floor(
20022                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20023         );
20024         
20025         this.list.setWidth(lw); // default to '1'
20026
20027         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20028         //il.on('mouseover', this.onViewOver, this, { list:  i });
20029         //il.on('mousemove', this.onViewMove, this, { list:  i });
20030         il.setWidth(lw);
20031         il.setStyle({ 'overflow-x' : 'hidden'});
20032
20033         if(!this.tpl){
20034             this.tpl = new Roo.Template({
20035                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20036                 isEmpty: function (value, allValues) {
20037                     //Roo.log(value);
20038                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20039                     return dl ? 'has-children' : 'no-children'
20040                 }
20041             });
20042         }
20043         
20044         var store  = this.store;
20045         if (i > 0) {
20046             store  = new Roo.data.SimpleStore({
20047                 //fields : this.store.reader.meta.fields,
20048                 reader : this.store.reader,
20049                 data : [ ]
20050             });
20051         }
20052         this.stores[i]  = store;
20053                   
20054         var view = this.views[i] = new Roo.View(
20055             il,
20056             this.tpl,
20057             {
20058                 singleSelect:true,
20059                 store: store,
20060                 selectedClass: this.selectedClass
20061             }
20062         );
20063         view.getEl().setWidth(lw);
20064         view.getEl().setStyle({
20065             position: i < 1 ? 'relative' : 'absolute',
20066             top: 0,
20067             left: (i * lw ) + 'px',
20068             display : i > 0 ? 'none' : 'block'
20069         });
20070         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20071         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20072         //view.on('click', this.onViewClick, this, { list : i });
20073
20074         store.on('beforeload', this.onBeforeLoad, this);
20075         store.on('load',  this.onLoad, this, { list  : i});
20076         store.on('loadexception', this.onLoadException, this);
20077
20078         // hide the other vies..
20079         
20080         
20081         
20082     },
20083       
20084     restrictHeight : function()
20085     {
20086         var mh = 0;
20087         Roo.each(this.innerLists, function(il,i) {
20088             var el = this.views[i].getEl();
20089             el.dom.style.height = '';
20090             var inner = el.dom;
20091             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20092             // only adjust heights on other ones..
20093             mh = Math.max(h, mh);
20094             if (i < 1) {
20095                 
20096                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20097                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20098                
20099             }
20100             
20101             
20102         }, this);
20103         
20104         this.list.beginUpdate();
20105         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20106         this.list.alignTo(this.el, this.listAlign);
20107         this.list.endUpdate();
20108         
20109     },
20110      
20111     
20112     // -- store handlers..
20113     // private
20114     onBeforeLoad : function()
20115     {
20116         if(!this.hasFocus){
20117             return;
20118         }
20119         this.innerLists[0].update(this.loadingText ?
20120                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20121         this.restrictHeight();
20122         this.selectedIndex = -1;
20123     },
20124     // private
20125     onLoad : function(a,b,c,d)
20126     {
20127         if (!this.loadingChildren) {
20128             // then we are loading the top level. - hide the children
20129             for (var i = 1;i < this.views.length; i++) {
20130                 this.views[i].getEl().setStyle({ display : 'none' });
20131             }
20132             var lw = Math.floor(
20133                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20134             );
20135         
20136              this.list.setWidth(lw); // default to '1'
20137
20138             
20139         }
20140         if(!this.hasFocus){
20141             return;
20142         }
20143         
20144         if(this.store.getCount() > 0) {
20145             this.expand();
20146             this.restrictHeight();   
20147         } else {
20148             this.onEmptyResults();
20149         }
20150         
20151         if (!this.loadingChildren) {
20152             this.selectActive();
20153         }
20154         /*
20155         this.stores[1].loadData([]);
20156         this.stores[2].loadData([]);
20157         this.views
20158         */    
20159     
20160         //this.el.focus();
20161     },
20162     
20163     
20164     // private
20165     onLoadException : function()
20166     {
20167         this.collapse();
20168         Roo.log(this.store.reader.jsonData);
20169         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20170             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20171         }
20172         
20173         
20174     },
20175     // no cleaning of leading spaces on blur here.
20176     cleanLeadingSpace : function(e) { },
20177     
20178
20179     onSelectChange : function (view, sels, opts )
20180     {
20181         var ix = view.getSelectedIndexes();
20182          
20183         if (opts.list > this.maxColumns - 2) {
20184             if (view.store.getCount()<  1) {
20185                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20186
20187             } else  {
20188                 if (ix.length) {
20189                     // used to clear ?? but if we are loading unselected 
20190                     this.setFromData(view.store.getAt(ix[0]).data);
20191                 }
20192                 
20193             }
20194             
20195             return;
20196         }
20197         
20198         if (!ix.length) {
20199             // this get's fired when trigger opens..
20200            // this.setFromData({});
20201             var str = this.stores[opts.list+1];
20202             str.data.clear(); // removeall wihtout the fire events..
20203             return;
20204         }
20205         
20206         var rec = view.store.getAt(ix[0]);
20207          
20208         this.setFromData(rec.data);
20209         this.fireEvent('select', this, rec, ix[0]);
20210         
20211         var lw = Math.floor(
20212              (
20213                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20214              ) / this.maxColumns
20215         );
20216         this.loadingChildren = true;
20217         this.stores[opts.list+1].loadDataFromChildren( rec );
20218         this.loadingChildren = false;
20219         var dl = this.stores[opts.list+1]. getTotalCount();
20220         
20221         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20222         
20223         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20224         for (var i = opts.list+2; i < this.views.length;i++) {
20225             this.views[i].getEl().setStyle({ display : 'none' });
20226         }
20227         
20228         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20229         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20230         
20231         if (this.isLoading) {
20232            // this.selectActive(opts.list);
20233         }
20234          
20235     },
20236     
20237     
20238     
20239     
20240     onDoubleClick : function()
20241     {
20242         this.collapse(); //??
20243     },
20244     
20245      
20246     
20247     
20248     
20249     // private
20250     recordToStack : function(store, prop, value, stack)
20251     {
20252         var cstore = new Roo.data.SimpleStore({
20253             //fields : this.store.reader.meta.fields, // we need array reader.. for
20254             reader : this.store.reader,
20255             data : [ ]
20256         });
20257         var _this = this;
20258         var record  = false;
20259         var srec = false;
20260         if(store.getCount() < 1){
20261             return false;
20262         }
20263         store.each(function(r){
20264             if(r.data[prop] == value){
20265                 record = r;
20266             srec = r;
20267                 return false;
20268             }
20269             if (r.data.cn && r.data.cn.length) {
20270                 cstore.loadDataFromChildren( r);
20271                 var cret = _this.recordToStack(cstore, prop, value, stack);
20272                 if (cret !== false) {
20273                     record = cret;
20274                     srec = r;
20275                     return false;
20276                 }
20277             }
20278              
20279             return true;
20280         });
20281         if (record == false) {
20282             return false
20283         }
20284         stack.unshift(srec);
20285         return record;
20286     },
20287     
20288     /*
20289      * find the stack of stores that match our value.
20290      *
20291      * 
20292      */
20293     
20294     selectActive : function ()
20295     {
20296         // if store is not loaded, then we will need to wait for that to happen first.
20297         var stack = [];
20298         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20299         for (var i = 0; i < stack.length; i++ ) {
20300             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20301         }
20302         
20303     }
20304         
20305          
20306     
20307     
20308     
20309     
20310 });/*
20311  * Based on:
20312  * Ext JS Library 1.1.1
20313  * Copyright(c) 2006-2007, Ext JS, LLC.
20314  *
20315  * Originally Released Under LGPL - original licence link has changed is not relivant.
20316  *
20317  * Fork - LGPL
20318  * <script type="text/javascript">
20319  */
20320 /**
20321  * @class Roo.form.Checkbox
20322  * @extends Roo.form.Field
20323  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20324  * @constructor
20325  * Creates a new Checkbox
20326  * @param {Object} config Configuration options
20327  */
20328 Roo.form.Checkbox = function(config){
20329     Roo.form.Checkbox.superclass.constructor.call(this, config);
20330     this.addEvents({
20331         /**
20332          * @event check
20333          * Fires when the checkbox is checked or unchecked.
20334              * @param {Roo.form.Checkbox} this This checkbox
20335              * @param {Boolean} checked The new checked value
20336              */
20337         check : true
20338     });
20339 };
20340
20341 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20342     /**
20343      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20344      */
20345     focusClass : undefined,
20346     /**
20347      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20348      */
20349     fieldClass: "x-form-field",
20350     /**
20351      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20352      */
20353     checked: false,
20354     /**
20355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20356      * {tag: "input", type: "checkbox", autocomplete: "off"})
20357      */
20358     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20359     /**
20360      * @cfg {String} boxLabel The text that appears beside the checkbox
20361      */
20362     boxLabel : "",
20363     /**
20364      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20365      */  
20366     inputValue : '1',
20367     /**
20368      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20369      */
20370      valueOff: '0', // value when not checked..
20371
20372     actionMode : 'viewEl', 
20373     //
20374     // private
20375     itemCls : 'x-menu-check-item x-form-item',
20376     groupClass : 'x-menu-group-item',
20377     inputType : 'hidden',
20378     
20379     
20380     inSetChecked: false, // check that we are not calling self...
20381     
20382     inputElement: false, // real input element?
20383     basedOn: false, // ????
20384     
20385     isFormField: true, // not sure where this is needed!!!!
20386
20387     onResize : function(){
20388         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20389         if(!this.boxLabel){
20390             this.el.alignTo(this.wrap, 'c-c');
20391         }
20392     },
20393
20394     initEvents : function(){
20395         Roo.form.Checkbox.superclass.initEvents.call(this);
20396         this.el.on("click", this.onClick,  this);
20397         this.el.on("change", this.onClick,  this);
20398     },
20399
20400
20401     getResizeEl : function(){
20402         return this.wrap;
20403     },
20404
20405     getPositionEl : function(){
20406         return this.wrap;
20407     },
20408
20409     // private
20410     onRender : function(ct, position){
20411         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20412         /*
20413         if(this.inputValue !== undefined){
20414             this.el.dom.value = this.inputValue;
20415         }
20416         */
20417         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20418         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20419         var viewEl = this.wrap.createChild({ 
20420             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20421         this.viewEl = viewEl;   
20422         this.wrap.on('click', this.onClick,  this); 
20423         
20424         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20425         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20426         
20427         
20428         
20429         if(this.boxLabel){
20430             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20431         //    viewEl.on('click', this.onClick,  this); 
20432         }
20433         //if(this.checked){
20434             this.setChecked(this.checked);
20435         //}else{
20436             //this.checked = this.el.dom;
20437         //}
20438
20439     },
20440
20441     // private
20442     initValue : Roo.emptyFn,
20443
20444     /**
20445      * Returns the checked state of the checkbox.
20446      * @return {Boolean} True if checked, else false
20447      */
20448     getValue : function(){
20449         if(this.el){
20450             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20451         }
20452         return this.valueOff;
20453         
20454     },
20455
20456         // private
20457     onClick : function(){ 
20458         if (this.disabled) {
20459             return;
20460         }
20461         this.setChecked(!this.checked);
20462
20463         //if(this.el.dom.checked != this.checked){
20464         //    this.setValue(this.el.dom.checked);
20465        // }
20466     },
20467
20468     /**
20469      * Sets the checked state of the checkbox.
20470      * On is always based on a string comparison between inputValue and the param.
20471      * @param {Boolean/String} value - the value to set 
20472      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20473      */
20474     setValue : function(v,suppressEvent){
20475         
20476         
20477         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20478         //if(this.el && this.el.dom){
20479         //    this.el.dom.checked = this.checked;
20480         //    this.el.dom.defaultChecked = this.checked;
20481         //}
20482         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20483         //this.fireEvent("check", this, this.checked);
20484     },
20485     // private..
20486     setChecked : function(state,suppressEvent)
20487     {
20488         if (this.inSetChecked) {
20489             this.checked = state;
20490             return;
20491         }
20492         
20493     
20494         if(this.wrap){
20495             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20496         }
20497         this.checked = state;
20498         if(suppressEvent !== true){
20499             this.fireEvent('check', this, state);
20500         }
20501         this.inSetChecked = true;
20502         this.el.dom.value = state ? this.inputValue : this.valueOff;
20503         this.inSetChecked = false;
20504         
20505     },
20506     // handle setting of hidden value by some other method!!?!?
20507     setFromHidden: function()
20508     {
20509         if(!this.el){
20510             return;
20511         }
20512         //console.log("SET FROM HIDDEN");
20513         //alert('setFrom hidden');
20514         this.setValue(this.el.dom.value);
20515     },
20516     
20517     onDestroy : function()
20518     {
20519         if(this.viewEl){
20520             Roo.get(this.viewEl).remove();
20521         }
20522          
20523         Roo.form.Checkbox.superclass.onDestroy.call(this);
20524     },
20525     
20526     setBoxLabel : function(str)
20527     {
20528         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20529     }
20530
20531 });/*
20532  * Based on:
20533  * Ext JS Library 1.1.1
20534  * Copyright(c) 2006-2007, Ext JS, LLC.
20535  *
20536  * Originally Released Under LGPL - original licence link has changed is not relivant.
20537  *
20538  * Fork - LGPL
20539  * <script type="text/javascript">
20540  */
20541  
20542 /**
20543  * @class Roo.form.Radio
20544  * @extends Roo.form.Checkbox
20545  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20546  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20547  * @constructor
20548  * Creates a new Radio
20549  * @param {Object} config Configuration options
20550  */
20551 Roo.form.Radio = function(){
20552     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20553 };
20554 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20555     inputType: 'radio',
20556
20557     /**
20558      * If this radio is part of a group, it will return the selected value
20559      * @return {String}
20560      */
20561     getGroupValue : function(){
20562         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20563     },
20564     
20565     
20566     onRender : function(ct, position){
20567         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20568         
20569         if(this.inputValue !== undefined){
20570             this.el.dom.value = this.inputValue;
20571         }
20572          
20573         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20574         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20575         //var viewEl = this.wrap.createChild({ 
20576         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20577         //this.viewEl = viewEl;   
20578         //this.wrap.on('click', this.onClick,  this); 
20579         
20580         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20581         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20582         
20583         
20584         
20585         if(this.boxLabel){
20586             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20587         //    viewEl.on('click', this.onClick,  this); 
20588         }
20589          if(this.checked){
20590             this.el.dom.checked =   'checked' ;
20591         }
20592          
20593     } 
20594     
20595     
20596 });Roo.rtf = {}; // namespace
20597 Roo.rtf.Hex = function(hex)
20598 {
20599     this.hexstr = hex;
20600 };
20601 Roo.rtf.Paragraph = function(opts)
20602 {
20603     this.content = []; ///??? is that used?
20604 };Roo.rtf.Span = function(opts)
20605 {
20606     this.value = opts.value;
20607 };
20608
20609 Roo.rtf.Group = function(parent)
20610 {
20611     // we dont want to acutally store parent - it will make debug a nightmare..
20612     this.content = [];
20613     this.cn  = [];
20614      
20615        
20616     
20617 };
20618
20619 Roo.rtf.Group.prototype = {
20620     ignorable : false,
20621     content: false,
20622     cn: false,
20623     addContent : function(node) {
20624         // could set styles...
20625         this.content.push(node);
20626     },
20627     addChild : function(cn)
20628     {
20629         this.cn.push(cn);
20630     },
20631     // only for images really...
20632     toDataURL : function()
20633     {
20634         var mimetype = false;
20635         switch(true) {
20636             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20637                 mimetype = "image/png";
20638                 break;
20639              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20640                 mimetype = "image/jpeg";
20641                 break;
20642             default :
20643                 return 'about:blank'; // ?? error?
20644         }
20645         
20646         
20647         var hexstring = this.content[this.content.length-1].value;
20648         
20649         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20650             return String.fromCharCode(parseInt(a, 16));
20651         }).join(""));
20652     }
20653     
20654 };
20655 // this looks like it's normally the {rtf{ .... }}
20656 Roo.rtf.Document = function()
20657 {
20658     // we dont want to acutally store parent - it will make debug a nightmare..
20659     this.rtlch  = [];
20660     this.content = [];
20661     this.cn = [];
20662     
20663 };
20664 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20665     addChild : function(cn)
20666     {
20667         this.cn.push(cn);
20668         switch(cn.type) {
20669             case 'rtlch': // most content seems to be inside this??
20670             case 'listtext':
20671             case 'shpinst':
20672                 this.rtlch.push(cn);
20673                 return;
20674             default:
20675                 this[cn.type] = cn;
20676         }
20677         
20678     },
20679     
20680     getElementsByType : function(type)
20681     {
20682         var ret =  [];
20683         this._getElementsByType(type, ret, this.cn, 'rtf');
20684         return ret;
20685     },
20686     _getElementsByType : function (type, ret, search_array, path)
20687     {
20688         search_array.forEach(function(n,i) {
20689             if (n.type == type) {
20690                 n.path = path + '/' + n.type + ':' + i;
20691                 ret.push(n);
20692             }
20693             if (n.cn.length > 0) {
20694                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20695             }
20696         },this);
20697     }
20698     
20699 });
20700  
20701 Roo.rtf.Ctrl = function(opts)
20702 {
20703     this.value = opts.value;
20704     this.param = opts.param;
20705 };
20706 /**
20707  *
20708  *
20709  * based on this https://github.com/iarna/rtf-parser
20710  * it's really only designed to extract pict from pasted RTF 
20711  *
20712  * usage:
20713  *
20714  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20715  *  
20716  *
20717  */
20718
20719  
20720
20721
20722
20723 Roo.rtf.Parser = function(text) {
20724     //super({objectMode: true})
20725     this.text = '';
20726     this.parserState = this.parseText;
20727     
20728     // these are for interpeter...
20729     this.doc = {};
20730     ///this.parserState = this.parseTop
20731     this.groupStack = [];
20732     this.hexStore = [];
20733     this.doc = false;
20734     
20735     this.groups = []; // where we put the return.
20736     
20737     for (var ii = 0; ii < text.length; ++ii) {
20738         ++this.cpos;
20739         
20740         if (text[ii] === '\n') {
20741             ++this.row;
20742             this.col = 1;
20743         } else {
20744             ++this.col;
20745         }
20746         this.parserState(text[ii]);
20747     }
20748     
20749     
20750     
20751 };
20752 Roo.rtf.Parser.prototype = {
20753     text : '', // string being parsed..
20754     controlWord : '',
20755     controlWordParam :  '',
20756     hexChar : '',
20757     doc : false,
20758     group: false,
20759     groupStack : false,
20760     hexStore : false,
20761     
20762     
20763     cpos : 0, 
20764     row : 1, // reportin?
20765     col : 1, //
20766
20767      
20768     push : function (el)
20769     {
20770         var m = 'cmd'+ el.type;
20771         if (typeof(this[m]) == 'undefined') {
20772             Roo.log('invalid cmd:' + el.type);
20773             return;
20774         }
20775         this[m](el);
20776         //Roo.log(el);
20777     },
20778     flushHexStore : function()
20779     {
20780         if (this.hexStore.length < 1) {
20781             return;
20782         }
20783         var hexstr = this.hexStore.map(
20784             function(cmd) {
20785                 return cmd.value;
20786         }).join('');
20787         
20788         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20789               
20790             
20791         this.hexStore.splice(0)
20792         
20793     },
20794     
20795     cmdgroupstart : function()
20796     {
20797         this.flushHexStore();
20798         if (this.group) {
20799             this.groupStack.push(this.group);
20800         }
20801          // parent..
20802         if (this.doc === false) {
20803             this.group = this.doc = new Roo.rtf.Document();
20804             return;
20805             
20806         }
20807         this.group = new Roo.rtf.Group(this.group);
20808     },
20809     cmdignorable : function()
20810     {
20811         this.flushHexStore();
20812         this.group.ignorable = true;
20813     },
20814     cmdendparagraph : function()
20815     {
20816         this.flushHexStore();
20817         this.group.addContent(new Roo.rtf.Paragraph());
20818     },
20819     cmdgroupend : function ()
20820     {
20821         this.flushHexStore();
20822         var endingGroup = this.group;
20823         
20824         
20825         this.group = this.groupStack.pop();
20826         if (this.group) {
20827             this.group.addChild(endingGroup);
20828         }
20829         
20830         
20831         
20832         var doc = this.group || this.doc;
20833         //if (endingGroup instanceof FontTable) {
20834         //  doc.fonts = endingGroup.table
20835         //} else if (endingGroup instanceof ColorTable) {
20836         //  doc.colors = endingGroup.table
20837         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20838         if (endingGroup.ignorable === false) {
20839             //code
20840             this.groups.push(endingGroup);
20841            // Roo.log( endingGroup );
20842         }
20843             //Roo.each(endingGroup.content, function(item)) {
20844             //    doc.addContent(item);
20845             //}
20846             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20847         //}
20848     },
20849     cmdtext : function (cmd)
20850     {
20851         this.flushHexStore();
20852         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20853             //this.group = this.doc
20854         }
20855         this.group.addContent(new Roo.rtf.Span(cmd));
20856     },
20857     cmdcontrolword : function (cmd)
20858     {
20859         this.flushHexStore();
20860         if (!this.group.type) {
20861             this.group.type = cmd.value;
20862             return;
20863         }
20864         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20865         // we actually don't care about ctrl words...
20866         return ;
20867         /*
20868         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20869         if (this[method]) {
20870             this[method](cmd.param)
20871         } else {
20872             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20873         }
20874         */
20875     },
20876     cmdhexchar : function(cmd) {
20877         this.hexStore.push(cmd);
20878     },
20879     cmderror : function(cmd) {
20880         throw new Exception (cmd.value);
20881     },
20882     
20883     /*
20884       _flush (done) {
20885         if (this.text !== '\u0000') this.emitText()
20886         done()
20887       }
20888       */
20889       
20890       
20891     parseText : function(c)
20892     {
20893         if (c === '\\') {
20894             this.parserState = this.parseEscapes;
20895         } else if (c === '{') {
20896             this.emitStartGroup();
20897         } else if (c === '}') {
20898             this.emitEndGroup();
20899         } else if (c === '\x0A' || c === '\x0D') {
20900             // cr/lf are noise chars
20901         } else {
20902             this.text += c;
20903         }
20904     },
20905     
20906     parseEscapes: function (c)
20907     {
20908         if (c === '\\' || c === '{' || c === '}') {
20909             this.text += c;
20910             this.parserState = this.parseText;
20911         } else {
20912             this.parserState = this.parseControlSymbol;
20913             this.parseControlSymbol(c);
20914         }
20915     },
20916     parseControlSymbol: function(c)
20917     {
20918         if (c === '~') {
20919             this.text += '\u00a0'; // nbsp
20920             this.parserState = this.parseText
20921         } else if (c === '-') {
20922              this.text += '\u00ad'; // soft hyphen
20923         } else if (c === '_') {
20924             this.text += '\u2011'; // non-breaking hyphen
20925         } else if (c === '*') {
20926             this.emitIgnorable();
20927             this.parserState = this.parseText;
20928         } else if (c === "'") {
20929             this.parserState = this.parseHexChar;
20930         } else if (c === '|') { // formula cacter
20931             this.emitFormula();
20932             this.parserState = this.parseText;
20933         } else if (c === ':') { // subentry in an index entry
20934             this.emitIndexSubEntry();
20935             this.parserState = this.parseText;
20936         } else if (c === '\x0a') {
20937             this.emitEndParagraph();
20938             this.parserState = this.parseText;
20939         } else if (c === '\x0d') {
20940             this.emitEndParagraph();
20941             this.parserState = this.parseText;
20942         } else {
20943             this.parserState = this.parseControlWord;
20944             this.parseControlWord(c);
20945         }
20946     },
20947     parseHexChar: function (c)
20948     {
20949         if (/^[A-Fa-f0-9]$/.test(c)) {
20950             this.hexChar += c;
20951             if (this.hexChar.length >= 2) {
20952               this.emitHexChar();
20953               this.parserState = this.parseText;
20954             }
20955             return;
20956         }
20957         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20958         this.parserState = this.parseText;
20959         
20960     },
20961     parseControlWord : function(c)
20962     {
20963         if (c === ' ') {
20964             this.emitControlWord();
20965             this.parserState = this.parseText;
20966         } else if (/^[-\d]$/.test(c)) {
20967             this.parserState = this.parseControlWordParam;
20968             this.controlWordParam += c;
20969         } else if (/^[A-Za-z]$/.test(c)) {
20970           this.controlWord += c;
20971         } else {
20972           this.emitControlWord();
20973           this.parserState = this.parseText;
20974           this.parseText(c);
20975         }
20976     },
20977     parseControlWordParam : function (c) {
20978         if (/^\d$/.test(c)) {
20979           this.controlWordParam += c;
20980         } else if (c === ' ') {
20981           this.emitControlWord();
20982           this.parserState = this.parseText;
20983         } else {
20984           this.emitControlWord();
20985           this.parserState = this.parseText;
20986           this.parseText(c);
20987         }
20988     },
20989     
20990     
20991     
20992     
20993     emitText : function () {
20994         if (this.text === '') {
20995             return;
20996         }
20997         this.push({
20998             type: 'text',
20999             value: this.text,
21000             pos: this.cpos,
21001             row: this.row,
21002             col: this.col
21003         });
21004         this.text = ''
21005     },
21006     emitControlWord : function ()
21007     {
21008         this.emitText();
21009         if (this.controlWord === '') {
21010             this.emitError('empty control word');
21011         } else {
21012             this.push({
21013                   type: 'controlword',
21014                   value: this.controlWord,
21015                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21016                   pos: this.cpos,
21017                   row: this.row,
21018                   col: this.col
21019             });
21020         }
21021         this.controlWord = '';
21022         this.controlWordParam = '';
21023     },
21024     emitStartGroup : function ()
21025     {
21026         this.emitText();
21027         this.push({
21028             type: 'groupstart',
21029             pos: this.cpos,
21030             row: this.row,
21031             col: this.col
21032         });
21033     },
21034     emitEndGroup : function ()
21035     {
21036         this.emitText();
21037         this.push({
21038             type: 'groupend',
21039             pos: this.cpos,
21040             row: this.row,
21041             col: this.col
21042         });
21043     },
21044     emitIgnorable : function ()
21045     {
21046         this.emitText();
21047         this.push({
21048             type: 'ignorable',
21049             pos: this.cpos,
21050             row: this.row,
21051             col: this.col
21052         });
21053     },
21054     emitHexChar : function ()
21055     {
21056         this.emitText();
21057         this.push({
21058             type: 'hexchar',
21059             value: this.hexChar,
21060             pos: this.cpos,
21061             row: this.row,
21062             col: this.col
21063         });
21064         this.hexChar = ''
21065     },
21066     emitError : function (message)
21067     {
21068       this.emitText();
21069       this.push({
21070             type: 'error',
21071             value: message,
21072             row: this.row,
21073             col: this.col,
21074             char: this.cpos //,
21075             //stack: new Error().stack
21076         });
21077     },
21078     emitEndParagraph : function () {
21079         this.emitText();
21080         this.push({
21081             type: 'endparagraph',
21082             pos: this.cpos,
21083             row: this.row,
21084             col: this.col
21085         });
21086     }
21087      
21088 } ;
21089 Roo.htmleditor = {};
21090  
21091 /**
21092  * @class Roo.htmleditor.Filter
21093  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21094  * @cfg {DomElement} node The node to iterate and filter
21095  * @cfg {boolean|String|Array} tag Tags to replace 
21096  * @constructor
21097  * Create a new Filter.
21098  * @param {Object} config Configuration options
21099  */
21100
21101
21102
21103 Roo.htmleditor.Filter = function(cfg) {
21104     Roo.apply(this.cfg);
21105     // this does not actually call walk as it's really just a abstract class
21106 }
21107
21108
21109 Roo.htmleditor.Filter.prototype = {
21110     
21111     node: false,
21112     
21113     tag: false,
21114
21115     // overrride to do replace comments.
21116     replaceComment : false,
21117     
21118     // overrride to do replace or do stuff with tags..
21119     replaceTag : false,
21120     
21121     walk : function(dom)
21122     {
21123         Roo.each( Array.from(dom.childNodes), function( e ) {
21124             switch(true) {
21125                 
21126                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21127                     this.replaceComment(e);
21128                     return;
21129                 
21130                 case e.nodeType != 1: //not a node.
21131                     return;
21132                 
21133                 case this.tag === true: // everything
21134                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21135                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21136                     if (this.replaceTag && false === this.replaceTag(e)) {
21137                         return;
21138                     }
21139                     if (e.hasChildNodes()) {
21140                         this.walk(e);
21141                     }
21142                     return;
21143                 
21144                 default:    // tags .. that do not match.
21145                     if (e.hasChildNodes()) {
21146                         this.walk(e);
21147                     }
21148             }
21149             
21150         }, this);
21151         
21152     }
21153 }; 
21154
21155 /**
21156  * @class Roo.htmleditor.FilterAttributes
21157  * clean attributes and  styles including http:// etc.. in attribute
21158  * @constructor
21159 * Run a new Attribute Filter
21160 * @param {Object} config Configuration options
21161  */
21162 Roo.htmleditor.FilterAttributes = function(cfg)
21163 {
21164     Roo.apply(this, cfg);
21165     this.attrib_black = this.attrib_black || [];
21166     this.attrib_white = this.attrib_white || [];
21167
21168     this.attrib_clean = this.attrib_clean || [];
21169     this.style_white = this.style_white || [];
21170     this.style_black = this.style_black || [];
21171     this.walk(cfg.node);
21172 }
21173
21174 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21175 {
21176     tag: true, // all tags
21177     
21178     attrib_black : false, // array
21179     attrib_clean : false,
21180     attrib_white : false,
21181
21182     style_white : false,
21183     style_black : false,
21184      
21185      
21186     replaceTag : function(node)
21187     {
21188         if (!node.attributes || !node.attributes.length) {
21189             return true;
21190         }
21191         
21192         for (var i = node.attributes.length-1; i > -1 ; i--) {
21193             var a = node.attributes[i];
21194             //console.log(a);
21195             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21196                 node.removeAttribute(a.name);
21197                 continue;
21198             }
21199             
21200             
21201             
21202             if (a.name.toLowerCase().substr(0,2)=='on')  {
21203                 node.removeAttribute(a.name);
21204                 continue;
21205             }
21206             
21207             
21208             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21209                 node.removeAttribute(a.name);
21210                 continue;
21211             }
21212             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21213                 this.cleanAttr(node,a.name,a.value); // fixme..
21214                 continue;
21215             }
21216             if (a.name == 'style') {
21217                 this.cleanStyle(node,a.name,a.value);
21218                 continue;
21219             }
21220             /// clean up MS crap..
21221             // tecnically this should be a list of valid class'es..
21222             
21223             
21224             if (a.name == 'class') {
21225                 if (a.value.match(/^Mso/)) {
21226                     node.removeAttribute('class');
21227                 }
21228                 
21229                 if (a.value.match(/^body$/)) {
21230                     node.removeAttribute('class');
21231                 }
21232                 continue;
21233             }
21234             
21235             
21236             // style cleanup!?
21237             // class cleanup?
21238             
21239         }
21240         return true; // clean children
21241     },
21242         
21243     cleanAttr: function(node, n,v)
21244     {
21245         
21246         if (v.match(/^\./) || v.match(/^\//)) {
21247             return;
21248         }
21249         if (v.match(/^(http|https):\/\//)
21250             || v.match(/^mailto:/) 
21251             || v.match(/^ftp:/)
21252             || v.match(/^data:/)
21253             ) {
21254             return;
21255         }
21256         if (v.match(/^#/)) {
21257             return;
21258         }
21259         if (v.match(/^\{/)) { // allow template editing.
21260             return;
21261         }
21262 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21263         node.removeAttribute(n);
21264         
21265     },
21266     cleanStyle : function(node,  n,v)
21267     {
21268         if (v.match(/expression/)) { //XSS?? should we even bother..
21269             node.removeAttribute(n);
21270             return;
21271         }
21272         
21273         var parts = v.split(/;/);
21274         var clean = [];
21275         
21276         Roo.each(parts, function(p) {
21277             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21278             if (!p.length) {
21279                 return true;
21280             }
21281             var l = p.split(':').shift().replace(/\s+/g,'');
21282             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21283             
21284             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21285                 return true;
21286             }
21287             //Roo.log()
21288             // only allow 'c whitelisted system attributes'
21289             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21290                 return true;
21291             }
21292             
21293             
21294             clean.push(p);
21295             return true;
21296         },this);
21297         if (clean.length) { 
21298             node.setAttribute(n, clean.join(';'));
21299         } else {
21300             node.removeAttribute(n);
21301         }
21302         
21303     }
21304         
21305         
21306         
21307     
21308 });/**
21309  * @class Roo.htmleditor.FilterBlack
21310  * remove blacklisted elements.
21311  * @constructor
21312  * Run a new Blacklisted Filter
21313  * @param {Object} config Configuration options
21314  */
21315
21316 Roo.htmleditor.FilterBlack = function(cfg)
21317 {
21318     Roo.apply(this, cfg);
21319     this.walk(cfg.node);
21320 }
21321
21322 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21323 {
21324     tag : true, // all elements.
21325    
21326     replaceTag : function(n)
21327     {
21328         n.parentNode.removeChild(n);
21329     }
21330 });
21331 /**
21332  * @class Roo.htmleditor.FilterComment
21333  * remove comments.
21334  * @constructor
21335 * Run a new Comments Filter
21336 * @param {Object} config Configuration options
21337  */
21338 Roo.htmleditor.FilterComment = function(cfg)
21339 {
21340     this.walk(cfg.node);
21341 }
21342
21343 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21344 {
21345   
21346     replaceComment : function(n)
21347     {
21348         n.parentNode.removeChild(n);
21349     }
21350 });/**
21351  * @class Roo.htmleditor.FilterKeepChildren
21352  * remove tags but keep children
21353  * @constructor
21354  * Run a new Keep Children Filter
21355  * @param {Object} config Configuration options
21356  */
21357
21358 Roo.htmleditor.FilterKeepChildren = function(cfg)
21359 {
21360     Roo.apply(this, cfg);
21361     if (this.tag === false) {
21362         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21363     }
21364     this.walk(cfg.node);
21365 }
21366
21367 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21368 {
21369     
21370   
21371     replaceTag : function(node)
21372     {
21373         // walk children...
21374         //Roo.log(node);
21375         var ar = Array.from(node.childNodes);
21376         //remove first..
21377         for (var i = 0; i < ar.length; i++) {
21378             if (ar[i].nodeType == 1) {
21379                 if (
21380                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21381                     || // array and it matches
21382                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21383                 ) {
21384                     this.replaceTag(ar[i]); // child is blacklisted as well...
21385                     continue;
21386                 }
21387             }
21388         }  
21389         ar = Array.from(node.childNodes);
21390         for (var i = 0; i < ar.length; i++) {
21391          
21392             node.removeChild(ar[i]);
21393             // what if we need to walk these???
21394             node.parentNode.insertBefore(ar[i], node);
21395             if (this.tag !== false) {
21396                 this.walk(ar[i]);
21397                 
21398             }
21399         }
21400         node.parentNode.removeChild(node);
21401         return false; // don't walk children
21402         
21403         
21404     }
21405 });/**
21406  * @class Roo.htmleditor.FilterParagraph
21407  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21408  * like on 'push' to remove the <p> tags and replace them with line breaks.
21409  * @constructor
21410  * Run a new Paragraph Filter
21411  * @param {Object} config Configuration options
21412  */
21413
21414 Roo.htmleditor.FilterParagraph = function(cfg)
21415 {
21416     // no need to apply config.
21417     this.walk(cfg.node);
21418 }
21419
21420 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21421 {
21422     
21423      
21424     tag : 'P',
21425     
21426      
21427     replaceTag : function(node)
21428     {
21429         
21430         if (node.childNodes.length == 1 &&
21431             node.childNodes[0].nodeType == 3 &&
21432             node.childNodes[0].textContent.trim().length < 1
21433             ) {
21434             // remove and replace with '<BR>';
21435             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21436             return false; // no need to walk..
21437         }
21438         var ar = Array.from(node.childNodes);
21439         for (var i = 0; i < ar.length; i++) {
21440             node.removeChild(ar[i]);
21441             // what if we need to walk these???
21442             node.parentNode.insertBefore(ar[i], node);
21443         }
21444         // now what about this?
21445         // <p> &nbsp; </p>
21446         
21447         // double BR.
21448         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21449         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21450         node.parentNode.removeChild(node);
21451         
21452         return false;
21453
21454     }
21455     
21456 });/**
21457  * @class Roo.htmleditor.FilterSpan
21458  * filter span's with no attributes out..
21459  * @constructor
21460  * Run a new Span Filter
21461  * @param {Object} config Configuration options
21462  */
21463
21464 Roo.htmleditor.FilterSpan = function(cfg)
21465 {
21466     // no need to apply config.
21467     this.walk(cfg.node);
21468 }
21469
21470 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21471 {
21472      
21473     tag : 'SPAN',
21474      
21475  
21476     replaceTag : function(node)
21477     {
21478         if (node.attributes && node.attributes.length > 0) {
21479             return true; // walk if there are any.
21480         }
21481         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21482         return false;
21483      
21484     }
21485     
21486 });/**
21487  * @class Roo.htmleditor.FilterTableWidth
21488   try and remove table width data - as that frequently messes up other stuff.
21489  * 
21490  *      was cleanTableWidths.
21491  *
21492  * Quite often pasting from word etc.. results in tables with column and widths.
21493  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21494  *
21495  * @constructor
21496  * Run a new Table Filter
21497  * @param {Object} config Configuration options
21498  */
21499
21500 Roo.htmleditor.FilterTableWidth = function(cfg)
21501 {
21502     // no need to apply config.
21503     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21504     this.walk(cfg.node);
21505 }
21506
21507 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21508 {
21509      
21510      
21511     
21512     replaceTag: function(node) {
21513         
21514         
21515       
21516         if (node.hasAttribute('width')) {
21517             node.removeAttribute('width');
21518         }
21519         
21520          
21521         if (node.hasAttribute("style")) {
21522             // pretty basic...
21523             
21524             var styles = node.getAttribute("style").split(";");
21525             var nstyle = [];
21526             Roo.each(styles, function(s) {
21527                 if (!s.match(/:/)) {
21528                     return;
21529                 }
21530                 var kv = s.split(":");
21531                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21532                     return;
21533                 }
21534                 // what ever is left... we allow.
21535                 nstyle.push(s);
21536             });
21537             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21538             if (!nstyle.length) {
21539                 node.removeAttribute('style');
21540             }
21541         }
21542         
21543         return true; // continue doing children..
21544     }
21545 });/**
21546  * @class Roo.htmleditor.FilterWord
21547  * try and clean up all the mess that Word generates.
21548  * 
21549  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21550  
21551  * @constructor
21552  * Run a new Span Filter
21553  * @param {Object} config Configuration options
21554  */
21555
21556 Roo.htmleditor.FilterWord = function(cfg)
21557 {
21558     // no need to apply config.
21559     this.walk(cfg.node);
21560 }
21561
21562 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21563 {
21564     tag: true,
21565      
21566     
21567     /**
21568      * Clean up MS wordisms...
21569      */
21570     replaceTag : function(node)
21571     {
21572          
21573         // no idea what this does - span with text, replaceds with just text.
21574         if(
21575                 node.nodeName == 'SPAN' &&
21576                 !node.hasAttributes() &&
21577                 node.childNodes.length == 1 &&
21578                 node.firstChild.nodeName == "#text"  
21579         ) {
21580             var textNode = node.firstChild;
21581             node.removeChild(textNode);
21582             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21583                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21584             }
21585             node.parentNode.insertBefore(textNode, node);
21586             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21587                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21588             }
21589             
21590             node.parentNode.removeChild(node);
21591             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21592         }
21593         
21594    
21595         
21596         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21597             node.parentNode.removeChild(node);
21598             return false; // dont do chidlren
21599         }
21600         //Roo.log(node.tagName);
21601         // remove - but keep children..
21602         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21603             //Roo.log('-- removed');
21604             while (node.childNodes.length) {
21605                 var cn = node.childNodes[0];
21606                 node.removeChild(cn);
21607                 node.parentNode.insertBefore(cn, node);
21608                 // move node to parent - and clean it..
21609                 this.replaceTag(cn);
21610             }
21611             node.parentNode.removeChild(node);
21612             /// no need to iterate chidlren = it's got none..
21613             //this.iterateChildren(node, this.cleanWord);
21614             return false; // no need to iterate children.
21615         }
21616         // clean styles
21617         if (node.className.length) {
21618             
21619             var cn = node.className.split(/\W+/);
21620             var cna = [];
21621             Roo.each(cn, function(cls) {
21622                 if (cls.match(/Mso[a-zA-Z]+/)) {
21623                     return;
21624                 }
21625                 cna.push(cls);
21626             });
21627             node.className = cna.length ? cna.join(' ') : '';
21628             if (!cna.length) {
21629                 node.removeAttribute("class");
21630             }
21631         }
21632         
21633         if (node.hasAttribute("lang")) {
21634             node.removeAttribute("lang");
21635         }
21636         
21637         if (node.hasAttribute("style")) {
21638             
21639             var styles = node.getAttribute("style").split(";");
21640             var nstyle = [];
21641             Roo.each(styles, function(s) {
21642                 if (!s.match(/:/)) {
21643                     return;
21644                 }
21645                 var kv = s.split(":");
21646                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21647                     return;
21648                 }
21649                 // what ever is left... we allow.
21650                 nstyle.push(s);
21651             });
21652             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21653             if (!nstyle.length) {
21654                 node.removeAttribute('style');
21655             }
21656         }
21657         return true; // do children
21658         
21659         
21660         
21661     }
21662 });
21663 /**
21664  * @class Roo.htmleditor.FilterStyleToTag
21665  * part of the word stuff... - certain 'styles' should be converted to tags.
21666  * eg.
21667  *   font-weight: bold -> bold
21668  *   ?? super / subscrit etc..
21669  * 
21670  * @constructor
21671 * Run a new style to tag filter.
21672 * @param {Object} config Configuration options
21673  */
21674 Roo.htmleditor.FilterStyleToTag = function(cfg)
21675 {
21676     
21677     this.tags = {
21678         B  : [ 'fontWeight' , 'bold'],
21679         I :  [ 'fontStyle' , 'italic'],
21680         //pre :  [ 'font-style' , 'italic'],
21681         // h1.. h6 ?? font-size?
21682         SUP : [ 'verticalAlign' , 'super' ],
21683         SUB : [ 'verticalAlign' , 'sub' ]
21684         
21685         
21686     };
21687     
21688     Roo.apply(this, cfg);
21689      
21690     
21691     this.walk(cfg.node);
21692     
21693     
21694     
21695 }
21696
21697
21698 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21699 {
21700     tag: true, // all tags
21701     
21702     tags : false,
21703     
21704     
21705     replaceTag : function(node)
21706     {
21707         
21708         
21709         if (node.getAttribute("style") === null) {
21710             return true;
21711         }
21712         var inject = [];
21713         for (var k in this.tags) {
21714             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21715                 inject.push(k);
21716                 node.style.removeProperty(this.tags[k][0]);
21717             }
21718         }
21719         if (!inject.length) {
21720             return true; 
21721         }
21722         var cn = Array.from(node.childNodes);
21723         var nn = node;
21724         Roo.each(inject, function(t) {
21725             var nc = node.ownerDocument.createElement(t);
21726             nn.appendChild(nc);
21727             nn = nc;
21728         });
21729         for(var i = 0;i < cn.length;cn++) {
21730             node.removeChild(cn[i]);
21731             nn.appendChild(cn[i]);
21732         }
21733         return true /// iterate thru
21734     }
21735     
21736 })/**
21737  * @class Roo.htmleditor.FilterLongBr
21738  * BR/BR/BR - keep a maximum of 2...
21739  * @constructor
21740  * Run a new Long BR Filter
21741  * @param {Object} config Configuration options
21742  */
21743
21744 Roo.htmleditor.FilterLongBr = function(cfg)
21745 {
21746     // no need to apply config.
21747     this.walk(cfg.node);
21748 }
21749
21750 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21751 {
21752     
21753      
21754     tag : 'BR',
21755     
21756      
21757     replaceTag : function(node)
21758     {
21759         
21760         var ps = node.nextSibling;
21761         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21762             ps = ps.nextSibling;
21763         }
21764         
21765         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21766             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21767             return false;
21768         }
21769         
21770         if (!ps || ps.nodeType != 1) {
21771             return false;
21772         }
21773         
21774         if (!ps || ps.tagName != 'BR') {
21775            
21776             return false;
21777         }
21778         
21779         
21780         
21781         
21782         
21783         if (!node.previousSibling) {
21784             return false;
21785         }
21786         var ps = node.previousSibling;
21787         
21788         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21789             ps = ps.previousSibling;
21790         }
21791         if (!ps || ps.nodeType != 1) {
21792             return false;
21793         }
21794         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21795         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21796             return false;
21797         }
21798         
21799         node.parentNode.removeChild(node); // remove me...
21800         
21801         return false; // no need to do children
21802
21803     }
21804     
21805 }); 
21806
21807 /**
21808  * @class Roo.htmleditor.FilterBlock
21809  * removes id / data-block and contenteditable that are associated with blocks
21810  * usage should be done on a cloned copy of the dom
21811  * @constructor
21812 * Run a new Attribute Filter { node : xxxx }}
21813 * @param {Object} config Configuration options
21814  */
21815 Roo.htmleditor.FilterBlock = function(cfg)
21816 {
21817     Roo.apply(this, cfg);
21818     var qa = cfg.node.querySelectorAll;
21819     this.removeAttributes('data-block');
21820     this.removeAttributes('contenteditable');
21821     this.removeAttributes('id');
21822     
21823 }
21824
21825 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21826 {
21827     node: true, // all tags
21828      
21829      
21830     removeAttributes : function(attr)
21831     {
21832         var ar = this.node.querySelectorAll('*[' + attr + ']');
21833         for (var i =0;i<ar.length;i++) {
21834             ar[i].removeAttribute(attr);
21835         }
21836     }
21837         
21838         
21839         
21840     
21841 });
21842 /***
21843  * This is based loosely on tinymce 
21844  * @class Roo.htmleditor.TidySerializer
21845  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21846  * @constructor
21847  * @method Serializer
21848  * @param {Object} settings Name/value settings object.
21849  */
21850
21851
21852 Roo.htmleditor.TidySerializer = function(settings)
21853 {
21854     Roo.apply(this, settings);
21855     
21856     this.writer = new Roo.htmleditor.TidyWriter(settings);
21857     
21858     
21859
21860 };
21861 Roo.htmleditor.TidySerializer.prototype = {
21862     
21863     /**
21864      * @param {boolean} inner do the inner of the node.
21865      */
21866     inner : false,
21867     
21868     writer : false,
21869     
21870     /**
21871     * Serializes the specified node into a string.
21872     *
21873     * @example
21874     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
21875     * @method serialize
21876     * @param {DomElement} node Node instance to serialize.
21877     * @return {String} String with HTML based on DOM tree.
21878     */
21879     serialize : function(node) {
21880         
21881         // = settings.validate;
21882         var writer = this.writer;
21883         var self  = this;
21884         this.handlers = {
21885             // #text
21886             3: function(node) {
21887                 
21888                 writer.text(node.nodeValue, node);
21889             },
21890             // #comment
21891             8: function(node) {
21892                 writer.comment(node.nodeValue);
21893             },
21894             // Processing instruction
21895             7: function(node) {
21896                 writer.pi(node.name, node.nodeValue);
21897             },
21898             // Doctype
21899             10: function(node) {
21900                 writer.doctype(node.nodeValue);
21901             },
21902             // CDATA
21903             4: function(node) {
21904                 writer.cdata(node.nodeValue);
21905             },
21906             // Document fragment
21907             11: function(node) {
21908                 node = node.firstChild;
21909                 if (!node) {
21910                     return;
21911                 }
21912                 while(node) {
21913                     self.walk(node);
21914                     node = node.nextSibling
21915                 }
21916             }
21917         };
21918         writer.reset();
21919         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
21920         return writer.getContent();
21921     },
21922
21923     walk: function(node)
21924     {
21925         var attrName, attrValue, sortedAttrs, i, l, elementRule,
21926             handler = this.handlers[node.nodeType];
21927             
21928         if (handler) {
21929             handler(node);
21930             return;
21931         }
21932     
21933         var name = node.nodeName;
21934         var isEmpty = node.childNodes.length < 1;
21935       
21936         var writer = this.writer;
21937         var attrs = node.attributes;
21938         // Sort attributes
21939         
21940         writer.start(node.nodeName, attrs, isEmpty, node);
21941         if (isEmpty) {
21942             return;
21943         }
21944         node = node.firstChild;
21945         if (!node) {
21946             writer.end(name);
21947             return;
21948         }
21949         while (node) {
21950             this.walk(node);
21951             node = node.nextSibling;
21952         }
21953         writer.end(name);
21954         
21955     
21956     }
21957     // Serialize element and treat all non elements as fragments
21958    
21959 }; 
21960
21961 /***
21962  * This is based loosely on tinymce 
21963  * @class Roo.htmleditor.TidyWriter
21964  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21965  *
21966  * Known issues?
21967  * - not tested much with 'PRE' formated elements.
21968  * 
21969  *
21970  *
21971  */
21972
21973 Roo.htmleditor.TidyWriter = function(settings)
21974 {
21975     
21976     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
21977     Roo.apply(this, settings);
21978     this.html = [];
21979     this.state = [];
21980      
21981     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
21982   
21983 }
21984 Roo.htmleditor.TidyWriter.prototype = {
21985
21986  
21987     state : false,
21988     
21989     indent :  '  ',
21990     
21991     // part of state...
21992     indentstr : '',
21993     in_pre: false,
21994     in_inline : false,
21995     last_inline : false,
21996     encode : false,
21997      
21998     
21999             /**
22000     * Writes the a start element such as <p id="a">.
22001     *
22002     * @method start
22003     * @param {String} name Name of the element.
22004     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22005     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22006     */
22007     start: function(name, attrs, empty, node)
22008     {
22009         var i, l, attr, value;
22010         
22011         // there are some situations where adding line break && indentation will not work. will not work.
22012         // <span / b / i ... formating?
22013         
22014         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22015         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22016         
22017         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22018         
22019         var add_lb = name == 'BR' ? false : in_inline;
22020         
22021         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22022             i_inline = false;
22023         }
22024
22025         var indentstr =  this.indentstr;
22026         
22027         // e_inline = elements that can be inline, but still allow \n before and after?
22028         // only 'BR' ??? any others?
22029         
22030         // ADD LINE BEFORE tage
22031         if (!this.in_pre) {
22032             if (in_inline) {
22033                 //code
22034                 if (name == 'BR') {
22035                     this.addLine();
22036                 } else if (this.lastElementEndsWS()) {
22037                     this.addLine();
22038                 } else{
22039                     // otherwise - no new line. (and dont indent.)
22040                     indentstr = '';
22041                 }
22042                 
22043             } else {
22044                 this.addLine();
22045             }
22046         } else {
22047             indentstr = '';
22048         }
22049         
22050         this.html.push(indentstr + '<', name.toLowerCase());
22051         
22052         if (attrs) {
22053             for (i = 0, l = attrs.length; i < l; i++) {
22054                 attr = attrs[i];
22055                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22056             }
22057         }
22058      
22059         if (empty) {
22060             if (is_short) {
22061                 this.html[this.html.length] = '/>';
22062             } else {
22063                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22064             }
22065             var e_inline = name == 'BR' ? false : this.in_inline;
22066             
22067             if (!e_inline && !this.in_pre) {
22068                 this.addLine();
22069             }
22070             return;
22071         
22072         }
22073         // not empty..
22074         this.html[this.html.length] = '>';
22075         
22076         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22077         /*
22078         if (!in_inline && !in_pre) {
22079             var cn = node.firstChild;
22080             while(cn) {
22081                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22082                     in_inline = true
22083                     break;
22084                 }
22085                 cn = cn.nextSibling;
22086             }
22087              
22088         }
22089         */
22090         
22091         
22092         this.pushState({
22093             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22094             in_pre : in_pre,
22095             in_inline :  in_inline
22096         });
22097         // add a line after if we are not in a
22098         
22099         if (!in_inline && !in_pre) {
22100             this.addLine();
22101         }
22102         
22103             
22104          
22105         
22106     },
22107     
22108     lastElementEndsWS : function()
22109     {
22110         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22111         if (value === false) {
22112             return true;
22113         }
22114         return value.match(/\s+$/);
22115         
22116     },
22117     
22118     /**
22119      * Writes the a end element such as </p>.
22120      *
22121      * @method end
22122      * @param {String} name Name of the element.
22123      */
22124     end: function(name) {
22125         var value;
22126         this.popState();
22127         var indentstr = '';
22128         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22129         
22130         if (!this.in_pre && !in_inline) {
22131             this.addLine();
22132             indentstr  = this.indentstr;
22133         }
22134         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22135         this.last_inline = in_inline;
22136         
22137         // pop the indent state..
22138     },
22139     /**
22140      * Writes a text node.
22141      *
22142      * In pre - we should not mess with the contents.
22143      * 
22144      *
22145      * @method text
22146      * @param {String} text String to write out.
22147      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22148      */
22149     text: function(text, node)
22150     {
22151         // if not in whitespace critical
22152         if (text.length < 1) {
22153             return;
22154         }
22155         if (this.in_pre) {
22156             this.html[this.html.length] =  text;
22157             return;   
22158         }
22159         
22160         if (this.in_inline) {
22161             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22162             if (text != ' ') {
22163                 text = text.replace(/\s+/,' ');  // all white space to single white space
22164                 
22165                     
22166                 // if next tag is '<BR>', then we can trim right..
22167                 if (node.nextSibling &&
22168                     node.nextSibling.nodeType == 1 &&
22169                     node.nextSibling.nodeName == 'BR' )
22170                 {
22171                     text = text.replace(/\s+$/g,'');
22172                 }
22173                 // if previous tag was a BR, we can also trim..
22174                 if (node.previousSibling &&
22175                     node.previousSibling.nodeType == 1 &&
22176                     node.previousSibling.nodeName == 'BR' )
22177                 {
22178                     text = this.indentstr +  text.replace(/^\s+/g,'');
22179                 }
22180                 if (text.match(/\n/)) {
22181                     text = text.replace(
22182                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22183                     );
22184                     // remoeve the last whitespace / line break.
22185                     text = text.replace(/\n\s+$/,'');
22186                 }
22187                 // repace long lines
22188                 
22189             }
22190              
22191             this.html[this.html.length] =  text;
22192             return;   
22193         }
22194         // see if previous element was a inline element.
22195         var indentstr = this.indentstr;
22196    
22197         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22198         
22199         // should trim left?
22200         if (node.previousSibling &&
22201             node.previousSibling.nodeType == 1 &&
22202             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22203         {
22204             indentstr = '';
22205             
22206         } else {
22207             this.addLine();
22208             text = text.replace(/^\s+/,''); // trim left
22209           
22210         }
22211         // should trim right?
22212         if (node.nextSibling &&
22213             node.nextSibling.nodeType == 1 &&
22214             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22215         {
22216           // noop
22217             
22218         }  else {
22219             text = text.replace(/\s+$/,''); // trim right
22220         }
22221          
22222               
22223         
22224         
22225         
22226         if (text.length < 1) {
22227             return;
22228         }
22229         if (!text.match(/\n/)) {
22230             this.html.push(indentstr + text);
22231             return;
22232         }
22233         
22234         text = this.indentstr + text.replace(
22235             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22236         );
22237         // remoeve the last whitespace / line break.
22238         text = text.replace(/\s+$/,''); 
22239         
22240         this.html.push(text);
22241         
22242         // split and indent..
22243         
22244         
22245     },
22246     /**
22247      * Writes a cdata node such as <![CDATA[data]]>.
22248      *
22249      * @method cdata
22250      * @param {String} text String to write out inside the cdata.
22251      */
22252     cdata: function(text) {
22253         this.html.push('<![CDATA[', text, ']]>');
22254     },
22255     /**
22256     * Writes a comment node such as <!-- Comment -->.
22257     *
22258     * @method cdata
22259     * @param {String} text String to write out inside the comment.
22260     */
22261    comment: function(text) {
22262        this.html.push('<!--', text, '-->');
22263    },
22264     /**
22265      * Writes a PI node such as <?xml attr="value" ?>.
22266      *
22267      * @method pi
22268      * @param {String} name Name of the pi.
22269      * @param {String} text String to write out inside the pi.
22270      */
22271     pi: function(name, text) {
22272         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22273         this.indent != '' && this.html.push('\n');
22274     },
22275     /**
22276      * Writes a doctype node such as <!DOCTYPE data>.
22277      *
22278      * @method doctype
22279      * @param {String} text String to write out inside the doctype.
22280      */
22281     doctype: function(text) {
22282         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22283     },
22284     /**
22285      * Resets the internal buffer if one wants to reuse the writer.
22286      *
22287      * @method reset
22288      */
22289     reset: function() {
22290         this.html.length = 0;
22291         this.state = [];
22292         this.pushState({
22293             indentstr : '',
22294             in_pre : false, 
22295             in_inline : false
22296         })
22297     },
22298     /**
22299      * Returns the contents that got serialized.
22300      *
22301      * @method getContent
22302      * @return {String} HTML contents that got written down.
22303      */
22304     getContent: function() {
22305         return this.html.join('').replace(/\n$/, '');
22306     },
22307     
22308     pushState : function(cfg)
22309     {
22310         this.state.push(cfg);
22311         Roo.apply(this, cfg);
22312     },
22313     
22314     popState : function()
22315     {
22316         if (this.state.length < 1) {
22317             return; // nothing to push
22318         }
22319         var cfg = {
22320             in_pre: false,
22321             indentstr : ''
22322         };
22323         this.state.pop();
22324         if (this.state.length > 0) {
22325             cfg = this.state[this.state.length-1]; 
22326         }
22327         Roo.apply(this, cfg);
22328     },
22329     
22330     addLine: function()
22331     {
22332         if (this.html.length < 1) {
22333             return;
22334         }
22335         
22336         
22337         var value = this.html[this.html.length - 1];
22338         if (value.length > 0 && '\n' !== value) {
22339             this.html.push('\n');
22340         }
22341     }
22342     
22343     
22344 //'pre script noscript style textarea video audio iframe object code'
22345 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22346 // inline 
22347 };
22348
22349 Roo.htmleditor.TidyWriter.inline_elements = [
22350         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22351         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP'
22352 ];
22353 Roo.htmleditor.TidyWriter.shortend_elements = [
22354     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22355     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22356 ];
22357
22358 Roo.htmleditor.TidyWriter.whitespace_elements = [
22359     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22360 ];/***
22361  * This is based loosely on tinymce 
22362  * @class Roo.htmleditor.TidyEntities
22363  * @static
22364  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22365  *
22366  * Not 100% sure this is actually used or needed.
22367  */
22368
22369 Roo.htmleditor.TidyEntities = {
22370     
22371     /**
22372      * initialize data..
22373      */
22374     init : function (){
22375      
22376         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22377        
22378     },
22379
22380
22381     buildEntitiesLookup: function(items, radix) {
22382         var i, chr, entity, lookup = {};
22383         if (!items) {
22384             return {};
22385         }
22386         items = typeof(items) == 'string' ? items.split(',') : items;
22387         radix = radix || 10;
22388         // Build entities lookup table
22389         for (i = 0; i < items.length; i += 2) {
22390             chr = String.fromCharCode(parseInt(items[i], radix));
22391             // Only add non base entities
22392             if (!this.baseEntities[chr]) {
22393                 entity = '&' + items[i + 1] + ';';
22394                 lookup[chr] = entity;
22395                 lookup[entity] = chr;
22396             }
22397         }
22398         return lookup;
22399         
22400     },
22401     
22402     asciiMap : {
22403             128: '€',
22404             130: '‚',
22405             131: 'ƒ',
22406             132: '„',
22407             133: '…',
22408             134: '†',
22409             135: '‡',
22410             136: 'ˆ',
22411             137: '‰',
22412             138: 'Š',
22413             139: '‹',
22414             140: 'Œ',
22415             142: 'Ž',
22416             145: '‘',
22417             146: '’',
22418             147: '“',
22419             148: '”',
22420             149: '•',
22421             150: '–',
22422             151: '—',
22423             152: '˜',
22424             153: '™',
22425             154: 'š',
22426             155: '›',
22427             156: 'œ',
22428             158: 'ž',
22429             159: 'Ÿ'
22430     },
22431     // Raw entities
22432     baseEntities : {
22433         '"': '&quot;',
22434         // Needs to be escaped since the YUI compressor would otherwise break the code
22435         '\'': '&#39;',
22436         '<': '&lt;',
22437         '>': '&gt;',
22438         '&': '&amp;',
22439         '`': '&#96;'
22440     },
22441     // Reverse lookup table for raw entities
22442     reverseEntities : {
22443         '&lt;': '<',
22444         '&gt;': '>',
22445         '&amp;': '&',
22446         '&quot;': '"',
22447         '&apos;': '\''
22448     },
22449     
22450     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22451     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22452     rawCharsRegExp : /[<>&\"\']/g,
22453     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22454     namedEntities  : false,
22455     namedEntitiesData : [ 
22456         '50',
22457         'nbsp',
22458         '51',
22459         'iexcl',
22460         '52',
22461         'cent',
22462         '53',
22463         'pound',
22464         '54',
22465         'curren',
22466         '55',
22467         'yen',
22468         '56',
22469         'brvbar',
22470         '57',
22471         'sect',
22472         '58',
22473         'uml',
22474         '59',
22475         'copy',
22476         '5a',
22477         'ordf',
22478         '5b',
22479         'laquo',
22480         '5c',
22481         'not',
22482         '5d',
22483         'shy',
22484         '5e',
22485         'reg',
22486         '5f',
22487         'macr',
22488         '5g',
22489         'deg',
22490         '5h',
22491         'plusmn',
22492         '5i',
22493         'sup2',
22494         '5j',
22495         'sup3',
22496         '5k',
22497         'acute',
22498         '5l',
22499         'micro',
22500         '5m',
22501         'para',
22502         '5n',
22503         'middot',
22504         '5o',
22505         'cedil',
22506         '5p',
22507         'sup1',
22508         '5q',
22509         'ordm',
22510         '5r',
22511         'raquo',
22512         '5s',
22513         'frac14',
22514         '5t',
22515         'frac12',
22516         '5u',
22517         'frac34',
22518         '5v',
22519         'iquest',
22520         '60',
22521         'Agrave',
22522         '61',
22523         'Aacute',
22524         '62',
22525         'Acirc',
22526         '63',
22527         'Atilde',
22528         '64',
22529         'Auml',
22530         '65',
22531         'Aring',
22532         '66',
22533         'AElig',
22534         '67',
22535         'Ccedil',
22536         '68',
22537         'Egrave',
22538         '69',
22539         'Eacute',
22540         '6a',
22541         'Ecirc',
22542         '6b',
22543         'Euml',
22544         '6c',
22545         'Igrave',
22546         '6d',
22547         'Iacute',
22548         '6e',
22549         'Icirc',
22550         '6f',
22551         'Iuml',
22552         '6g',
22553         'ETH',
22554         '6h',
22555         'Ntilde',
22556         '6i',
22557         'Ograve',
22558         '6j',
22559         'Oacute',
22560         '6k',
22561         'Ocirc',
22562         '6l',
22563         'Otilde',
22564         '6m',
22565         'Ouml',
22566         '6n',
22567         'times',
22568         '6o',
22569         'Oslash',
22570         '6p',
22571         'Ugrave',
22572         '6q',
22573         'Uacute',
22574         '6r',
22575         'Ucirc',
22576         '6s',
22577         'Uuml',
22578         '6t',
22579         'Yacute',
22580         '6u',
22581         'THORN',
22582         '6v',
22583         'szlig',
22584         '70',
22585         'agrave',
22586         '71',
22587         'aacute',
22588         '72',
22589         'acirc',
22590         '73',
22591         'atilde',
22592         '74',
22593         'auml',
22594         '75',
22595         'aring',
22596         '76',
22597         'aelig',
22598         '77',
22599         'ccedil',
22600         '78',
22601         'egrave',
22602         '79',
22603         'eacute',
22604         '7a',
22605         'ecirc',
22606         '7b',
22607         'euml',
22608         '7c',
22609         'igrave',
22610         '7d',
22611         'iacute',
22612         '7e',
22613         'icirc',
22614         '7f',
22615         'iuml',
22616         '7g',
22617         'eth',
22618         '7h',
22619         'ntilde',
22620         '7i',
22621         'ograve',
22622         '7j',
22623         'oacute',
22624         '7k',
22625         'ocirc',
22626         '7l',
22627         'otilde',
22628         '7m',
22629         'ouml',
22630         '7n',
22631         'divide',
22632         '7o',
22633         'oslash',
22634         '7p',
22635         'ugrave',
22636         '7q',
22637         'uacute',
22638         '7r',
22639         'ucirc',
22640         '7s',
22641         'uuml',
22642         '7t',
22643         'yacute',
22644         '7u',
22645         'thorn',
22646         '7v',
22647         'yuml',
22648         'ci',
22649         'fnof',
22650         'sh',
22651         'Alpha',
22652         'si',
22653         'Beta',
22654         'sj',
22655         'Gamma',
22656         'sk',
22657         'Delta',
22658         'sl',
22659         'Epsilon',
22660         'sm',
22661         'Zeta',
22662         'sn',
22663         'Eta',
22664         'so',
22665         'Theta',
22666         'sp',
22667         'Iota',
22668         'sq',
22669         'Kappa',
22670         'sr',
22671         'Lambda',
22672         'ss',
22673         'Mu',
22674         'st',
22675         'Nu',
22676         'su',
22677         'Xi',
22678         'sv',
22679         'Omicron',
22680         't0',
22681         'Pi',
22682         't1',
22683         'Rho',
22684         't3',
22685         'Sigma',
22686         't4',
22687         'Tau',
22688         't5',
22689         'Upsilon',
22690         't6',
22691         'Phi',
22692         't7',
22693         'Chi',
22694         't8',
22695         'Psi',
22696         't9',
22697         'Omega',
22698         'th',
22699         'alpha',
22700         'ti',
22701         'beta',
22702         'tj',
22703         'gamma',
22704         'tk',
22705         'delta',
22706         'tl',
22707         'epsilon',
22708         'tm',
22709         'zeta',
22710         'tn',
22711         'eta',
22712         'to',
22713         'theta',
22714         'tp',
22715         'iota',
22716         'tq',
22717         'kappa',
22718         'tr',
22719         'lambda',
22720         'ts',
22721         'mu',
22722         'tt',
22723         'nu',
22724         'tu',
22725         'xi',
22726         'tv',
22727         'omicron',
22728         'u0',
22729         'pi',
22730         'u1',
22731         'rho',
22732         'u2',
22733         'sigmaf',
22734         'u3',
22735         'sigma',
22736         'u4',
22737         'tau',
22738         'u5',
22739         'upsilon',
22740         'u6',
22741         'phi',
22742         'u7',
22743         'chi',
22744         'u8',
22745         'psi',
22746         'u9',
22747         'omega',
22748         'uh',
22749         'thetasym',
22750         'ui',
22751         'upsih',
22752         'um',
22753         'piv',
22754         '812',
22755         'bull',
22756         '816',
22757         'hellip',
22758         '81i',
22759         'prime',
22760         '81j',
22761         'Prime',
22762         '81u',
22763         'oline',
22764         '824',
22765         'frasl',
22766         '88o',
22767         'weierp',
22768         '88h',
22769         'image',
22770         '88s',
22771         'real',
22772         '892',
22773         'trade',
22774         '89l',
22775         'alefsym',
22776         '8cg',
22777         'larr',
22778         '8ch',
22779         'uarr',
22780         '8ci',
22781         'rarr',
22782         '8cj',
22783         'darr',
22784         '8ck',
22785         'harr',
22786         '8dl',
22787         'crarr',
22788         '8eg',
22789         'lArr',
22790         '8eh',
22791         'uArr',
22792         '8ei',
22793         'rArr',
22794         '8ej',
22795         'dArr',
22796         '8ek',
22797         'hArr',
22798         '8g0',
22799         'forall',
22800         '8g2',
22801         'part',
22802         '8g3',
22803         'exist',
22804         '8g5',
22805         'empty',
22806         '8g7',
22807         'nabla',
22808         '8g8',
22809         'isin',
22810         '8g9',
22811         'notin',
22812         '8gb',
22813         'ni',
22814         '8gf',
22815         'prod',
22816         '8gh',
22817         'sum',
22818         '8gi',
22819         'minus',
22820         '8gn',
22821         'lowast',
22822         '8gq',
22823         'radic',
22824         '8gt',
22825         'prop',
22826         '8gu',
22827         'infin',
22828         '8h0',
22829         'ang',
22830         '8h7',
22831         'and',
22832         '8h8',
22833         'or',
22834         '8h9',
22835         'cap',
22836         '8ha',
22837         'cup',
22838         '8hb',
22839         'int',
22840         '8hk',
22841         'there4',
22842         '8hs',
22843         'sim',
22844         '8i5',
22845         'cong',
22846         '8i8',
22847         'asymp',
22848         '8j0',
22849         'ne',
22850         '8j1',
22851         'equiv',
22852         '8j4',
22853         'le',
22854         '8j5',
22855         'ge',
22856         '8k2',
22857         'sub',
22858         '8k3',
22859         'sup',
22860         '8k4',
22861         'nsub',
22862         '8k6',
22863         'sube',
22864         '8k7',
22865         'supe',
22866         '8kl',
22867         'oplus',
22868         '8kn',
22869         'otimes',
22870         '8l5',
22871         'perp',
22872         '8m5',
22873         'sdot',
22874         '8o8',
22875         'lceil',
22876         '8o9',
22877         'rceil',
22878         '8oa',
22879         'lfloor',
22880         '8ob',
22881         'rfloor',
22882         '8p9',
22883         'lang',
22884         '8pa',
22885         'rang',
22886         '9ea',
22887         'loz',
22888         '9j0',
22889         'spades',
22890         '9j3',
22891         'clubs',
22892         '9j5',
22893         'hearts',
22894         '9j6',
22895         'diams',
22896         'ai',
22897         'OElig',
22898         'aj',
22899         'oelig',
22900         'b0',
22901         'Scaron',
22902         'b1',
22903         'scaron',
22904         'bo',
22905         'Yuml',
22906         'm6',
22907         'circ',
22908         'ms',
22909         'tilde',
22910         '802',
22911         'ensp',
22912         '803',
22913         'emsp',
22914         '809',
22915         'thinsp',
22916         '80c',
22917         'zwnj',
22918         '80d',
22919         'zwj',
22920         '80e',
22921         'lrm',
22922         '80f',
22923         'rlm',
22924         '80j',
22925         'ndash',
22926         '80k',
22927         'mdash',
22928         '80o',
22929         'lsquo',
22930         '80p',
22931         'rsquo',
22932         '80q',
22933         'sbquo',
22934         '80s',
22935         'ldquo',
22936         '80t',
22937         'rdquo',
22938         '80u',
22939         'bdquo',
22940         '810',
22941         'dagger',
22942         '811',
22943         'Dagger',
22944         '81g',
22945         'permil',
22946         '81p',
22947         'lsaquo',
22948         '81q',
22949         'rsaquo',
22950         '85c',
22951         'euro'
22952     ],
22953
22954          
22955     /**
22956      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
22957      *
22958      * @method encodeRaw
22959      * @param {String} text Text to encode.
22960      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22961      * @return {String} Entity encoded text.
22962      */
22963     encodeRaw: function(text, attr)
22964     {
22965         var t = this;
22966         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22967             return t.baseEntities[chr] || chr;
22968         });
22969     },
22970     /**
22971      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
22972      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
22973      * and is exposed as the DOMUtils.encode function.
22974      *
22975      * @method encodeAllRaw
22976      * @param {String} text Text to encode.
22977      * @return {String} Entity encoded text.
22978      */
22979     encodeAllRaw: function(text) {
22980         var t = this;
22981         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
22982             return t.baseEntities[chr] || chr;
22983         });
22984     },
22985     /**
22986      * Encodes the specified string using numeric entities. The core entities will be
22987      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
22988      *
22989      * @method encodeNumeric
22990      * @param {String} text Text to encode.
22991      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22992      * @return {String} Entity encoded text.
22993      */
22994     encodeNumeric: function(text, attr) {
22995         var t = this;
22996         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22997             // Multi byte sequence convert it to a single entity
22998             if (chr.length > 1) {
22999                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23000             }
23001             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23002         });
23003     },
23004     /**
23005      * Encodes the specified string using named entities. The core entities will be encoded
23006      * as named ones but all non lower ascii characters will be encoded into named entities.
23007      *
23008      * @method encodeNamed
23009      * @param {String} text Text to encode.
23010      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23011      * @param {Object} entities Optional parameter with entities to use.
23012      * @return {String} Entity encoded text.
23013      */
23014     encodeNamed: function(text, attr, entities) {
23015         var t = this;
23016         entities = entities || this.namedEntities;
23017         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23018             return t.baseEntities[chr] || entities[chr] || chr;
23019         });
23020     },
23021     /**
23022      * Returns an encode function based on the name(s) and it's optional entities.
23023      *
23024      * @method getEncodeFunc
23025      * @param {String} name Comma separated list of encoders for example named,numeric.
23026      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23027      * @return {function} Encode function to be used.
23028      */
23029     getEncodeFunc: function(name, entities) {
23030         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23031         var t = this;
23032         function encodeNamedAndNumeric(text, attr) {
23033             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23034                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23035             });
23036         }
23037
23038         function encodeCustomNamed(text, attr) {
23039             return t.encodeNamed(text, attr, entities);
23040         }
23041         // Replace + with , to be compatible with previous TinyMCE versions
23042         name = this.makeMap(name.replace(/\+/g, ','));
23043         // Named and numeric encoder
23044         if (name.named && name.numeric) {
23045             return this.encodeNamedAndNumeric;
23046         }
23047         // Named encoder
23048         if (name.named) {
23049             // Custom names
23050             if (entities) {
23051                 return encodeCustomNamed;
23052             }
23053             return this.encodeNamed;
23054         }
23055         // Numeric
23056         if (name.numeric) {
23057             return this.encodeNumeric;
23058         }
23059         // Raw encoder
23060         return this.encodeRaw;
23061     },
23062     /**
23063      * Decodes the specified string, this will replace entities with raw UTF characters.
23064      *
23065      * @method decode
23066      * @param {String} text Text to entity decode.
23067      * @return {String} Entity decoded string.
23068      */
23069     decode: function(text)
23070     {
23071         var  t = this;
23072         return text.replace(this.entityRegExp, function(all, numeric) {
23073             if (numeric) {
23074                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23075                 // Support upper UTF
23076                 if (numeric > 65535) {
23077                     numeric -= 65536;
23078                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23079                 }
23080                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23081             }
23082             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23083         });
23084     },
23085     nativeDecode : function (text) {
23086         return text;
23087     },
23088     makeMap : function (items, delim, map) {
23089                 var i;
23090                 items = items || [];
23091                 delim = delim || ',';
23092                 if (typeof items == "string") {
23093                         items = items.split(delim);
23094                 }
23095                 map = map || {};
23096                 i = items.length;
23097                 while (i--) {
23098                         map[items[i]] = {};
23099                 }
23100                 return map;
23101         }
23102 };
23103     
23104     
23105     
23106 Roo.htmleditor.TidyEntities.init();
23107 /**
23108  * @class Roo.htmleditor.KeyEnter
23109  * Handle Enter press..
23110  * @cfg {Roo.HtmlEditorCore} core the editor.
23111  * @constructor
23112  * Create a new Filter.
23113  * @param {Object} config Configuration options
23114  */
23115
23116
23117
23118
23119
23120 Roo.htmleditor.KeyEnter = function(cfg) {
23121     Roo.apply(this, cfg);
23122     // this does not actually call walk as it's really just a abstract class
23123  
23124     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23125 }
23126
23127 //Roo.htmleditor.KeyEnter.i = 0;
23128
23129
23130 Roo.htmleditor.KeyEnter.prototype = {
23131     
23132     core : false,
23133     
23134     keypress : function(e)
23135     {
23136         if (e.charCode != 13 && e.charCode != 10) {
23137             Roo.log([e.charCode,e]);
23138             return true;
23139         }
23140         e.preventDefault();
23141         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23142         var doc = this.core.doc;
23143           //add a new line
23144        
23145     
23146         var sel = this.core.getSelection();
23147         var range = sel.getRangeAt(0);
23148         var n = range.commonAncestorContainer;
23149         var pc = range.closest([ 'ol', 'ul']);
23150         var pli = range.closest('li');
23151         if (!pc || e.ctrlKey) {
23152             sel.insertNode('br', 'after'); 
23153          
23154             this.core.undoManager.addEvent();
23155             this.core.fireEditorEvent(e);
23156             return false;
23157         }
23158         
23159         // deal with <li> insetion
23160         if (pli.innerText.trim() == '' &&
23161             pli.previousSibling &&
23162             pli.previousSibling.nodeName == 'LI' &&
23163             pli.previousSibling.innerText.trim() ==  '') {
23164             pli.parentNode.removeChild(pli.previousSibling);
23165             sel.cursorAfter(pc);
23166             this.core.undoManager.addEvent();
23167             this.core.fireEditorEvent(e);
23168             return false;
23169         }
23170     
23171         var li = doc.createElement('LI');
23172         li.innerHTML = '&nbsp;';
23173         if (!pli || !pli.firstSibling) {
23174             pc.appendChild(li);
23175         } else {
23176             pli.parentNode.insertBefore(li, pli.firstSibling);
23177         }
23178         sel.cursorText (li.firstChild);
23179       
23180         this.core.undoManager.addEvent();
23181         this.core.fireEditorEvent(e);
23182
23183         return false;
23184         
23185     
23186         
23187         
23188          
23189     }
23190 };
23191      
23192 /**
23193  * @class Roo.htmleditor.Block
23194  * Base class for html editor blocks - do not use it directly .. extend it..
23195  * @cfg {DomElement} node The node to apply stuff to.
23196  * @cfg {String} friendly_name the name that appears in the context bar about this block
23197  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23198  
23199  * @constructor
23200  * Create a new Filter.
23201  * @param {Object} config Configuration options
23202  */
23203
23204 Roo.htmleditor.Block  = function(cfg)
23205 {
23206     // do nothing .. should not be called really.
23207 }
23208 /**
23209  * factory method to get the block from an element (using cache if necessary)
23210  * @static
23211  * @param {HtmlElement} the dom element
23212  */
23213 Roo.htmleditor.Block.factory = function(node)
23214 {
23215     var cc = Roo.htmleditor.Block.cache;
23216     var id = Roo.get(node).id;
23217     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23218         Roo.htmleditor.Block.cache[id].readElement(node);
23219         return Roo.htmleditor.Block.cache[id];
23220     }
23221     var db  = node.getAttribute('data-block');
23222     if (!db) {
23223         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23224     }
23225     var cls = Roo.htmleditor['Block' + db];
23226     if (typeof(cls) == 'undefined') {
23227         //Roo.log(node.getAttribute('data-block'));
23228         Roo.log("OOps missing block : " + 'Block' + db);
23229         return false;
23230     }
23231     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23232     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23233 };
23234
23235 /**
23236  * initalize all Elements from content that are 'blockable'
23237  * @static
23238  * @param the body element
23239  */
23240 Roo.htmleditor.Block.initAll = function(body, type)
23241 {
23242     if (typeof(type) == 'undefined') {
23243         var ia = Roo.htmleditor.Block.initAll;
23244         ia(body,'table');
23245         ia(body,'td');
23246         ia(body,'figure');
23247         return;
23248     }
23249     Roo.each(Roo.get(body).query(type), function(e) {
23250         Roo.htmleditor.Block.factory(e);    
23251     },this);
23252 };
23253 // question goes here... do we need to clear out this cache sometimes?
23254 // or show we make it relivant to the htmleditor.
23255 Roo.htmleditor.Block.cache = {};
23256
23257 Roo.htmleditor.Block.prototype = {
23258     
23259     node : false,
23260     
23261      // used by context menu
23262     friendly_name : 'Based Block',
23263     
23264     // text for button to delete this element
23265     deleteTitle : false,
23266     
23267     context : false,
23268     /**
23269      * Update a node with values from this object
23270      * @param {DomElement} node
23271      */
23272     updateElement : function(node)
23273     {
23274         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23275     },
23276      /**
23277      * convert to plain HTML for calling insertAtCursor..
23278      */
23279     toHTML : function()
23280     {
23281         return Roo.DomHelper.markup(this.toObject());
23282     },
23283     /**
23284      * used by readEleemnt to extract data from a node
23285      * may need improving as it's pretty basic
23286      
23287      * @param {DomElement} node
23288      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23289      * @param {String} attribute (use html - for contents, or style for using next param as style)
23290      * @param {String} style the style property - eg. text-align
23291      */
23292     getVal : function(node, tag, attr, style)
23293     {
23294         var n = node;
23295         if (tag !== true && n.tagName != tag.toUpperCase()) {
23296             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23297             // but kiss for now.
23298             n = node.getElementsByTagName(tag).item(0);
23299         }
23300         if (!n) {
23301             return '';
23302         }
23303         if (attr == 'html') {
23304             return n.innerHTML;
23305         }
23306         if (attr == 'style') {
23307             return n.style[style]; 
23308         }
23309         
23310         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23311             
23312     },
23313     /**
23314      * create a DomHelper friendly object - for use with 
23315      * Roo.DomHelper.markup / overwrite / etc..
23316      * (override this)
23317      */
23318     toObject : function()
23319     {
23320         return {};
23321     },
23322       /**
23323      * Read a node that has a 'data-block' property - and extract the values from it.
23324      * @param {DomElement} node - the node
23325      */
23326     readElement : function(node)
23327     {
23328         
23329     } 
23330     
23331     
23332 };
23333
23334  
23335
23336 /**
23337  * @class Roo.htmleditor.BlockFigure
23338  * Block that has an image and a figcaption
23339  * @cfg {String} image_src the url for the image
23340  * @cfg {String} align (left|right) alignment for the block default left
23341  * @cfg {String} caption the text to appear below  (and in the alt tag)
23342  * @cfg {String} caption_display (block|none) display or not the caption
23343  * @cfg {String|number} image_width the width of the image number or %?
23344  * @cfg {String|number} image_height the height of the image number or %?
23345  * 
23346  * @constructor
23347  * Create a new Filter.
23348  * @param {Object} config Configuration options
23349  */
23350
23351 Roo.htmleditor.BlockFigure = function(cfg)
23352 {
23353     if (cfg.node) {
23354         this.readElement(cfg.node);
23355         this.updateElement(cfg.node);
23356     }
23357     Roo.apply(this, cfg);
23358 }
23359 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23360  
23361     
23362     // setable values.
23363     image_src: '',
23364     align: 'center',
23365     caption : '',
23366     caption_display : 'block',
23367     width : '100%',
23368     cls : '',
23369     href: '',
23370     video_url : '',
23371     
23372     // margin: '2%', not used
23373     
23374     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23375
23376     
23377     // used by context menu
23378     friendly_name : 'Image with caption',
23379     deleteTitle : "Delete Image and Caption",
23380     
23381     contextMenu : function(toolbar)
23382     {
23383         
23384         var block = function() {
23385             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23386         };
23387         
23388         
23389         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23390         
23391         var syncValue = toolbar.editorcore.syncValue;
23392         
23393         var fields = {};
23394         
23395         return [
23396              {
23397                 xtype : 'TextItem',
23398                 text : "Source: ",
23399                 xns : rooui.Toolbar  //Boostrap?
23400             },
23401             {
23402                 xtype : 'Button',
23403                 text: 'Change Image URL',
23404                  
23405                 listeners : {
23406                     click: function (btn, state)
23407                     {
23408                         var b = block();
23409                         
23410                         Roo.MessageBox.show({
23411                             title : "Image Source URL",
23412                             msg : "Enter the url for the image",
23413                             buttons: Roo.MessageBox.OKCANCEL,
23414                             fn: function(btn, val){
23415                                 if (btn != 'ok') {
23416                                     return;
23417                                 }
23418                                 b.image_src = val;
23419                                 b.updateElement();
23420                                 syncValue();
23421                                 toolbar.editorcore.onEditorEvent();
23422                             },
23423                             minWidth:250,
23424                             prompt:true,
23425                             //multiline: multiline,
23426                             modal : true,
23427                             value : b.image_src
23428                         });
23429                     }
23430                 },
23431                 xns : rooui.Toolbar
23432             },
23433          
23434             {
23435                 xtype : 'Button',
23436                 text: 'Change Link URL',
23437                  
23438                 listeners : {
23439                     click: function (btn, state)
23440                     {
23441                         var b = block();
23442                         
23443                         Roo.MessageBox.show({
23444                             title : "Link URL",
23445                             msg : "Enter the url for the link - leave blank to have no link",
23446                             buttons: Roo.MessageBox.OKCANCEL,
23447                             fn: function(btn, val){
23448                                 if (btn != 'ok') {
23449                                     return;
23450                                 }
23451                                 b.href = val;
23452                                 b.updateElement();
23453                                 syncValue();
23454                                 toolbar.editorcore.onEditorEvent();
23455                             },
23456                             minWidth:250,
23457                             prompt:true,
23458                             //multiline: multiline,
23459                             modal : true,
23460                             value : b.href
23461                         });
23462                     }
23463                 },
23464                 xns : rooui.Toolbar
23465             },
23466             {
23467                 xtype : 'Button',
23468                 text: 'Show Video URL',
23469                  
23470                 listeners : {
23471                     click: function (btn, state)
23472                     {
23473                         Roo.MessageBox.alert("Video URL",
23474                             block().video_url == '' ? 'This image is not linked ot a video' :
23475                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23476                     }
23477                 },
23478                 xns : rooui.Toolbar
23479             },
23480             
23481             
23482             {
23483                 xtype : 'TextItem',
23484                 text : "Width: ",
23485                 xns : rooui.Toolbar  //Boostrap?
23486             },
23487             {
23488                 xtype : 'ComboBox',
23489                 allowBlank : false,
23490                 displayField : 'val',
23491                 editable : true,
23492                 listWidth : 100,
23493                 triggerAction : 'all',
23494                 typeAhead : true,
23495                 valueField : 'val',
23496                 width : 70,
23497                 name : 'width',
23498                 listeners : {
23499                     select : function (combo, r, index)
23500                     {
23501                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23502                         var b = block();
23503                         b.width = r.get('val');
23504                         b.updateElement();
23505                         syncValue();
23506                         toolbar.editorcore.onEditorEvent();
23507                     }
23508                 },
23509                 xns : rooui.form,
23510                 store : {
23511                     xtype : 'SimpleStore',
23512                     data : [
23513                         ['auto'],
23514                         ['50%'],
23515                         ['100%']
23516                     ],
23517                     fields : [ 'val'],
23518                     xns : Roo.data
23519                 }
23520             },
23521             {
23522                 xtype : 'TextItem',
23523                 text : "Align: ",
23524                 xns : rooui.Toolbar  //Boostrap?
23525             },
23526             {
23527                 xtype : 'ComboBox',
23528                 allowBlank : false,
23529                 displayField : 'val',
23530                 editable : true,
23531                 listWidth : 100,
23532                 triggerAction : 'all',
23533                 typeAhead : true,
23534                 valueField : 'val',
23535                 width : 70,
23536                 name : 'align',
23537                 listeners : {
23538                     select : function (combo, r, index)
23539                     {
23540                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23541                         var b = block();
23542                         b.align = r.get('val');
23543                         b.updateElement();
23544                         syncValue();
23545                         toolbar.editorcore.onEditorEvent();
23546                     }
23547                 },
23548                 xns : rooui.form,
23549                 store : {
23550                     xtype : 'SimpleStore',
23551                     data : [
23552                         ['left'],
23553                         ['right'],
23554                         ['center']
23555                     ],
23556                     fields : [ 'val'],
23557                     xns : Roo.data
23558                 }
23559             },
23560             
23561             
23562             {
23563                 xtype : 'Button',
23564                 text: 'Hide Caption',
23565                 name : 'caption_display',
23566                 pressed : false,
23567                 enableToggle : true,
23568                 setValue : function(v) {
23569                     this.toggle(v == 'block' ? false : true);
23570                 },
23571                 listeners : {
23572                     toggle: function (btn, state)
23573                     {
23574                         var b  = block();
23575                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23576                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23577                         b.updateElement();
23578                         syncValue();
23579                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23580                         toolbar.editorcore.onEditorEvent();
23581                     }
23582                 },
23583                 xns : rooui.Toolbar
23584             }
23585         ];
23586         
23587     },
23588     /**
23589      * create a DomHelper friendly object - for use with
23590      * Roo.DomHelper.markup / overwrite / etc..
23591      */
23592     toObject : function()
23593     {
23594         var d = document.createElement('div');
23595         d.innerHTML = this.caption;
23596         
23597         var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
23598         
23599         var img =   {
23600             tag : 'img',
23601             contenteditable : 'false',
23602             src : this.image_src,
23603             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23604             style: {
23605                 width : 'auto',
23606                 'max-width': '100%',
23607                 margin : '0px' 
23608                 
23609                 
23610             }
23611         };
23612         /*
23613         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23614                     '<a href="{2}">' + 
23615                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23616                     '</a>' + 
23617                 '</div>',
23618         */
23619                 
23620         if (this.href.length > 0) {
23621             img = {
23622                 tag : 'a',
23623                 href: this.href,
23624                 contenteditable : 'true',
23625                 cn : [
23626                     img
23627                 ]
23628             };
23629         }
23630         
23631         
23632         if (this.video_url.length > 0) {
23633             img = {
23634                 tag : 'div',
23635                 cls : this.cls,
23636                 frameborder : 0,
23637                 allowfullscreen : true,
23638                 width : 420,  // these are for video tricks - that we replace the outer
23639                 height : 315,
23640                 src : this.video_url,
23641                 cn : [
23642                     img
23643                 ]
23644             };
23645         }
23646         
23647         var captionhtml = this.caption_display == 'hidden' ? this.caption : (this.caption.length ? this.caption : "Caption");
23648         
23649         return  {
23650             tag: 'figure',
23651             'data-block' : 'Figure',
23652             contenteditable : 'false',
23653             style : {
23654                 display: 'block',
23655                 float :  this.align ,
23656                 'max-width':  this.width,
23657                 width : 'auto',
23658                 margin:  m,
23659                 padding: '10px'
23660                 
23661             },
23662            
23663             
23664             align : this.align,
23665             cn : [
23666                 img,
23667               
23668                 {
23669                     tag: 'figcaption',
23670                     
23671                     style : {
23672                         'text-align': 'left',
23673                         'margin-top' : '16px',
23674                         'font-size' : '16px',
23675                         'line-height' : '24px',
23676                          display : this.caption_display
23677                     },
23678                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23679                     cn : [
23680                         {
23681                             // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23682                             tag : 'i',
23683                             contenteditable : true,
23684                             html : captionhtml
23685                         }
23686                     ]
23687                     
23688                 }
23689             ]
23690         };
23691          
23692     },
23693     
23694     readElement : function(node)
23695     {
23696         // this should not really come from the link...
23697         this.video_url = this.getVal(node, 'div', 'src');
23698         this.cls = this.getVal(node, 'div', 'class');
23699         this.href = this.getVal(node, 'a', 'href');
23700         
23701         this.image_src = this.getVal(node, 'img', 'src');
23702          
23703         this.align = this.getVal(node, 'figure', 'align');
23704         this.caption = this.getVal(node, 'figcaption', 'html');
23705         // remove '<i>
23706         if (this.caption.trim().match(/^<i[^>]*>/i)) {
23707             this.caption = this.caption.trim().replace(/^<i[^>]*>/i, '').replace(/^<\/i>$/i, '');
23708         }
23709         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23710         this.width = this.getVal(node, 'figure', 'style', 'max-width');
23711         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23712         
23713     },
23714     removeNode : function()
23715     {
23716         return this.node;
23717     }
23718     
23719   
23720    
23721      
23722     
23723     
23724     
23725     
23726 })
23727
23728  
23729
23730 /**
23731  * @class Roo.htmleditor.BlockTable
23732  * Block that manages a table
23733  * 
23734  * @constructor
23735  * Create a new Filter.
23736  * @param {Object} config Configuration options
23737  */
23738
23739 Roo.htmleditor.BlockTable = function(cfg)
23740 {
23741     if (cfg.node) {
23742         this.readElement(cfg.node);
23743         this.updateElement(cfg.node);
23744     }
23745     Roo.apply(this, cfg);
23746     if (!cfg.node) {
23747         this.rows = [];
23748         for(var r = 0; r < this.no_row; r++) {
23749             this.rows[r] = [];
23750             for(var c = 0; c < this.no_col; c++) {
23751                 this.rows[r][c] = this.emptyCell();
23752             }
23753         }
23754     }
23755     
23756     
23757 }
23758 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23759  
23760     rows : false,
23761     no_col : 1,
23762     no_row : 1,
23763     
23764     
23765     width: '100%',
23766     
23767     // used by context menu
23768     friendly_name : 'Table',
23769     deleteTitle : 'Delete Table',
23770     // context menu is drawn once..
23771     
23772     contextMenu : function(toolbar)
23773     {
23774         
23775         var block = function() {
23776             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23777         };
23778         
23779         
23780         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23781         
23782         var syncValue = toolbar.editorcore.syncValue;
23783         
23784         var fields = {};
23785         
23786         return [
23787             {
23788                 xtype : 'TextItem',
23789                 text : "Width: ",
23790                 xns : rooui.Toolbar  //Boostrap?
23791             },
23792             {
23793                 xtype : 'ComboBox',
23794                 allowBlank : false,
23795                 displayField : 'val',
23796                 editable : true,
23797                 listWidth : 100,
23798                 triggerAction : 'all',
23799                 typeAhead : true,
23800                 valueField : 'val',
23801                 width : 100,
23802                 name : 'width',
23803                 listeners : {
23804                     select : function (combo, r, index)
23805                     {
23806                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23807                         var b = block();
23808                         b.width = r.get('val');
23809                         b.updateElement();
23810                         syncValue();
23811                         toolbar.editorcore.onEditorEvent();
23812                     }
23813                 },
23814                 xns : rooui.form,
23815                 store : {
23816                     xtype : 'SimpleStore',
23817                     data : [
23818                         ['100%'],
23819                         ['auto']
23820                     ],
23821                     fields : [ 'val'],
23822                     xns : Roo.data
23823                 }
23824             },
23825             // -------- Cols
23826             
23827             {
23828                 xtype : 'TextItem',
23829                 text : "Columns: ",
23830                 xns : rooui.Toolbar  //Boostrap?
23831             },
23832          
23833             {
23834                 xtype : 'Button',
23835                 text: '-',
23836                 listeners : {
23837                     click : function (_self, e)
23838                     {
23839                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23840                         block().removeColumn();
23841                         syncValue();
23842                         toolbar.editorcore.onEditorEvent();
23843                     }
23844                 },
23845                 xns : rooui.Toolbar
23846             },
23847             {
23848                 xtype : 'Button',
23849                 text: '+',
23850                 listeners : {
23851                     click : function (_self, e)
23852                     {
23853                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23854                         block().addColumn();
23855                         syncValue();
23856                         toolbar.editorcore.onEditorEvent();
23857                     }
23858                 },
23859                 xns : rooui.Toolbar
23860             },
23861             // -------- ROWS
23862             {
23863                 xtype : 'TextItem',
23864                 text : "Rows: ",
23865                 xns : rooui.Toolbar  //Boostrap?
23866             },
23867          
23868             {
23869                 xtype : 'Button',
23870                 text: '-',
23871                 listeners : {
23872                     click : function (_self, e)
23873                     {
23874                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23875                         block().removeRow();
23876                         syncValue();
23877                         toolbar.editorcore.onEditorEvent();
23878                     }
23879                 },
23880                 xns : rooui.Toolbar
23881             },
23882             {
23883                 xtype : 'Button',
23884                 text: '+',
23885                 listeners : {
23886                     click : function (_self, e)
23887                     {
23888                         block().addRow();
23889                         syncValue();
23890                         toolbar.editorcore.onEditorEvent();
23891                     }
23892                 },
23893                 xns : rooui.Toolbar
23894             },
23895             // -------- ROWS
23896             {
23897                 xtype : 'Button',
23898                 text: 'Reset Column Widths',
23899                 listeners : {
23900                     
23901                     click : function (_self, e)
23902                     {
23903                         block().resetWidths();
23904                         syncValue();
23905                         toolbar.editorcore.onEditorEvent();
23906                     }
23907                 },
23908                 xns : rooui.Toolbar
23909             } 
23910             
23911             
23912             
23913         ];
23914         
23915     },
23916     
23917     
23918   /**
23919      * create a DomHelper friendly object - for use with
23920      * Roo.DomHelper.markup / overwrite / etc..
23921      * ?? should it be called with option to hide all editing features?
23922      */
23923     toObject : function()
23924     {
23925         
23926         var ret = {
23927             tag : 'table',
23928             contenteditable : 'false', // this stops cell selection from picking the table.
23929             'data-block' : 'Table',
23930             style : {
23931                 width:  this.width,
23932                 border : 'solid 1px #000', // ??? hard coded?
23933                 'border-collapse' : 'collapse' 
23934             },
23935             cn : [
23936                 { tag : 'tbody' , cn : [] }
23937             ]
23938         };
23939         
23940         // do we have a head = not really 
23941         var ncols = 0;
23942         Roo.each(this.rows, function( row ) {
23943             var tr = {
23944                 tag: 'tr',
23945                 style : {
23946                     margin: '6px',
23947                     border : 'solid 1px #000',
23948                     textAlign : 'left' 
23949                 },
23950                 cn : [ ]
23951             };
23952             
23953             ret.cn[0].cn.push(tr);
23954             // does the row have any properties? ?? height?
23955             var nc = 0;
23956             Roo.each(row, function( cell ) {
23957                 
23958                 var td = {
23959                     tag : 'td',
23960                     contenteditable :  'true',
23961                     'data-block' : 'Td',
23962                     html : cell.html,
23963                     style : cell.style
23964                 };
23965                 if (cell.colspan > 1) {
23966                     td.colspan = cell.colspan ;
23967                     nc += cell.colspan;
23968                 } else {
23969                     nc++;
23970                 }
23971                 if (cell.rowspan > 1) {
23972                     td.rowspan = cell.rowspan ;
23973                 }
23974                 
23975                 
23976                 // widths ?
23977                 tr.cn.push(td);
23978                     
23979                 
23980             }, this);
23981             ncols = Math.max(nc, ncols);
23982             
23983             
23984         }, this);
23985         // add the header row..
23986         
23987         ncols++;
23988          
23989         
23990         return ret;
23991          
23992     },
23993     
23994     readElement : function(node)
23995     {
23996         node  = node ? node : this.node ;
23997         this.width = this.getVal(node, true, 'style', 'width') || '100%';
23998         
23999         this.rows = [];
24000         this.no_row = 0;
24001         var trs = Array.from(node.rows);
24002         trs.forEach(function(tr) {
24003             var row =  [];
24004             this.rows.push(row);
24005             
24006             this.no_row++;
24007             var no_column = 0;
24008             Array.from(tr.cells).forEach(function(td) {
24009                 
24010                 var add = {
24011                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24012                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24013                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24014                     html : td.innerHTML
24015                 };
24016                 no_column += add.colspan;
24017                      
24018                 
24019                 row.push(add);
24020                 
24021                 
24022             },this);
24023             this.no_col = Math.max(this.no_col, no_column);
24024             
24025             
24026         },this);
24027         
24028         
24029     },
24030     normalizeRows: function()
24031     {
24032         var ret= [];
24033         var rid = -1;
24034         this.rows.forEach(function(row) {
24035             rid++;
24036             ret[rid] = [];
24037             row = this.normalizeRow(row);
24038             var cid = 0;
24039             row.forEach(function(c) {
24040                 while (typeof(ret[rid][cid]) != 'undefined') {
24041                     cid++;
24042                 }
24043                 if (typeof(ret[rid]) == 'undefined') {
24044                     ret[rid] = [];
24045                 }
24046                 ret[rid][cid] = c;
24047                 c.row = rid;
24048                 c.col = cid;
24049                 if (c.rowspan < 2) {
24050                     return;
24051                 }
24052                 
24053                 for(var i = 1 ;i < c.rowspan; i++) {
24054                     if (typeof(ret[rid+i]) == 'undefined') {
24055                         ret[rid+i] = [];
24056                     }
24057                     ret[rid+i][cid] = c;
24058                 }
24059             });
24060         }, this);
24061         return ret;
24062     
24063     },
24064     
24065     normalizeRow: function(row)
24066     {
24067         var ret= [];
24068         row.forEach(function(c) {
24069             if (c.colspan < 2) {
24070                 ret.push(c);
24071                 return;
24072             }
24073             for(var i =0 ;i < c.colspan; i++) {
24074                 ret.push(c);
24075             }
24076         });
24077         return ret;
24078     
24079     },
24080     
24081     deleteColumn : function(sel)
24082     {
24083         if (!sel || sel.type != 'col') {
24084             return;
24085         }
24086         if (this.no_col < 2) {
24087             return;
24088         }
24089         
24090         this.rows.forEach(function(row) {
24091             var cols = this.normalizeRow(row);
24092             var col = cols[sel.col];
24093             if (col.colspan > 1) {
24094                 col.colspan --;
24095             } else {
24096                 row.remove(col);
24097             }
24098             
24099         }, this);
24100         this.no_col--;
24101         
24102     },
24103     removeColumn : function()
24104     {
24105         this.deleteColumn({
24106             type: 'col',
24107             col : this.no_col-1
24108         });
24109         this.updateElement();
24110     },
24111     
24112      
24113     addColumn : function()
24114     {
24115         
24116         this.rows.forEach(function(row) {
24117             row.push(this.emptyCell());
24118            
24119         }, this);
24120         this.updateElement();
24121     },
24122     
24123     deleteRow : function(sel)
24124     {
24125         if (!sel || sel.type != 'row') {
24126             return;
24127         }
24128         
24129         if (this.no_row < 2) {
24130             return;
24131         }
24132         
24133         var rows = this.normalizeRows();
24134         
24135         
24136         rows[sel.row].forEach(function(col) {
24137             if (col.rowspan > 1) {
24138                 col.rowspan--;
24139             } else {
24140                 col.remove = 1; // flage it as removed.
24141             }
24142             
24143         }, this);
24144         var newrows = [];
24145         this.rows.forEach(function(row) {
24146             newrow = [];
24147             row.forEach(function(c) {
24148                 if (typeof(c.remove) == 'undefined') {
24149                     newrow.push(c);
24150                 }
24151                 
24152             });
24153             if (newrow.length > 0) {
24154                 newrows.push(row);
24155             }
24156         });
24157         this.rows =  newrows;
24158         
24159         
24160         
24161         this.no_row--;
24162         this.updateElement();
24163         
24164     },
24165     removeRow : function()
24166     {
24167         this.deleteRow({
24168             type: 'row',
24169             row : this.no_row-1
24170         });
24171         
24172     },
24173     
24174      
24175     addRow : function()
24176     {
24177         
24178         var row = [];
24179         for (var i = 0; i < this.no_col; i++ ) {
24180             
24181             row.push(this.emptyCell());
24182            
24183         }
24184         this.rows.push(row);
24185         this.updateElement();
24186         
24187     },
24188      
24189     // the default cell object... at present...
24190     emptyCell : function() {
24191         return (new Roo.htmleditor.BlockTd({})).toObject();
24192         
24193      
24194     },
24195     
24196     removeNode : function()
24197     {
24198         return this.node;
24199     },
24200     
24201     
24202     
24203     resetWidths : function()
24204     {
24205         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24206             var nn = Roo.htmleditor.Block.factory(n);
24207             nn.width = '';
24208             nn.updateElement(n);
24209         });
24210     }
24211     
24212     
24213     
24214     
24215 })
24216
24217 /**
24218  *
24219  * editing a TD?
24220  *
24221  * since selections really work on the table cell, then editing really should work from there
24222  *
24223  * The original plan was to support merging etc... - but that may not be needed yet..
24224  *
24225  * So this simple version will support:
24226  *   add/remove cols
24227  *   adjust the width +/-
24228  *   reset the width...
24229  *   
24230  *
24231  */
24232
24233
24234  
24235
24236 /**
24237  * @class Roo.htmleditor.BlockTable
24238  * Block that manages a table
24239  * 
24240  * @constructor
24241  * Create a new Filter.
24242  * @param {Object} config Configuration options
24243  */
24244
24245 Roo.htmleditor.BlockTd = function(cfg)
24246 {
24247     if (cfg.node) {
24248         this.readElement(cfg.node);
24249         this.updateElement(cfg.node);
24250     }
24251     Roo.apply(this, cfg);
24252      
24253     
24254     
24255 }
24256 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24257  
24258     node : false,
24259     
24260     width: '',
24261     textAlign : 'left',
24262     valign : 'top',
24263     
24264     colspan : 1,
24265     rowspan : 1,
24266     
24267     
24268     // used by context menu
24269     friendly_name : 'Table Cell',
24270     deleteTitle : false, // use our customer delete
24271     
24272     // context menu is drawn once..
24273     
24274     contextMenu : function(toolbar)
24275     {
24276         
24277         var cell = function() {
24278             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24279         };
24280         
24281         var table = function() {
24282             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24283         };
24284         
24285         var lr = false;
24286         var saveSel = function()
24287         {
24288             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24289         }
24290         var restoreSel = function()
24291         {
24292             if (lr) {
24293                 (function() {
24294                     toolbar.editorcore.focus();
24295                     var cr = toolbar.editorcore.getSelection();
24296                     cr.removeAllRanges();
24297                     cr.addRange(lr);
24298                     toolbar.editorcore.onEditorEvent();
24299                 }).defer(10, this);
24300                 
24301                 
24302             }
24303         }
24304         
24305         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24306         
24307         var syncValue = toolbar.editorcore.syncValue;
24308         
24309         var fields = {};
24310         
24311         return [
24312             {
24313                 xtype : 'Button',
24314                 text : 'Edit Table',
24315                 listeners : {
24316                     click : function() {
24317                         var t = toolbar.tb.selectedNode.closest('table');
24318                         toolbar.editorcore.selectNode(t);
24319                         toolbar.editorcore.onEditorEvent();                        
24320                     }
24321                 }
24322                 
24323             },
24324               
24325            
24326              
24327             {
24328                 xtype : 'TextItem',
24329                 text : "Column Width: ",
24330                  xns : rooui.Toolbar 
24331                
24332             },
24333             {
24334                 xtype : 'Button',
24335                 text: '-',
24336                 listeners : {
24337                     click : function (_self, e)
24338                     {
24339                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24340                         cell().shrinkColumn();
24341                         syncValue();
24342                          toolbar.editorcore.onEditorEvent();
24343                     }
24344                 },
24345                 xns : rooui.Toolbar
24346             },
24347             {
24348                 xtype : 'Button',
24349                 text: '+',
24350                 listeners : {
24351                     click : function (_self, e)
24352                     {
24353                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24354                         cell().growColumn();
24355                         syncValue();
24356                         toolbar.editorcore.onEditorEvent();
24357                     }
24358                 },
24359                 xns : rooui.Toolbar
24360             },
24361             
24362             {
24363                 xtype : 'TextItem',
24364                 text : "Vertical Align: ",
24365                 xns : rooui.Toolbar  //Boostrap?
24366             },
24367             {
24368                 xtype : 'ComboBox',
24369                 allowBlank : false,
24370                 displayField : 'val',
24371                 editable : true,
24372                 listWidth : 100,
24373                 triggerAction : 'all',
24374                 typeAhead : true,
24375                 valueField : 'val',
24376                 width : 100,
24377                 name : 'valign',
24378                 listeners : {
24379                     select : function (combo, r, index)
24380                     {
24381                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24382                         var b = cell();
24383                         b.valign = r.get('val');
24384                         b.updateElement();
24385                         syncValue();
24386                         toolbar.editorcore.onEditorEvent();
24387                     }
24388                 },
24389                 xns : rooui.form,
24390                 store : {
24391                     xtype : 'SimpleStore',
24392                     data : [
24393                         ['top'],
24394                         ['middle'],
24395                         ['bottom'] // there are afew more... 
24396                     ],
24397                     fields : [ 'val'],
24398                     xns : Roo.data
24399                 }
24400             },
24401             
24402             {
24403                 xtype : 'TextItem',
24404                 text : "Merge Cells: ",
24405                  xns : rooui.Toolbar 
24406                
24407             },
24408             
24409             
24410             {
24411                 xtype : 'Button',
24412                 text: 'Right',
24413                 listeners : {
24414                     click : function (_self, e)
24415                     {
24416                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24417                         cell().mergeRight();
24418                         //block().growColumn();
24419                         syncValue();
24420                         toolbar.editorcore.onEditorEvent();
24421                     }
24422                 },
24423                 xns : rooui.Toolbar
24424             },
24425              
24426             {
24427                 xtype : 'Button',
24428                 text: 'Below',
24429                 listeners : {
24430                     click : function (_self, e)
24431                     {
24432                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24433                         cell().mergeBelow();
24434                         //block().growColumn();
24435                         syncValue();
24436                         toolbar.editorcore.onEditorEvent();
24437                     }
24438                 },
24439                 xns : rooui.Toolbar
24440             },
24441             {
24442                 xtype : 'TextItem',
24443                 text : "| ",
24444                  xns : rooui.Toolbar 
24445                
24446             },
24447             
24448             {
24449                 xtype : 'Button',
24450                 text: 'Split',
24451                 listeners : {
24452                     click : function (_self, e)
24453                     {
24454                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24455                         cell().split();
24456                         syncValue();
24457                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24458                         toolbar.editorcore.onEditorEvent();
24459                                              
24460                     }
24461                 },
24462                 xns : rooui.Toolbar
24463             },
24464             {
24465                 xtype : 'Fill',
24466                 xns : rooui.Toolbar 
24467                
24468             },
24469         
24470           
24471             {
24472                 xtype : 'Button',
24473                 text: 'Delete',
24474                  
24475                 xns : rooui.Toolbar,
24476                 menu : {
24477                     xtype : 'Menu',
24478                     xns : rooui.menu,
24479                     items : [
24480                         {
24481                             xtype : 'Item',
24482                             html: 'Column',
24483                             listeners : {
24484                                 click : function (_self, e)
24485                                 {
24486                                     var t = table();
24487                                     
24488                                     cell().deleteColumn();
24489                                     syncValue();
24490                                     toolbar.editorcore.selectNode(t.node);
24491                                     toolbar.editorcore.onEditorEvent();   
24492                                 }
24493                             },
24494                             xns : rooui.menu
24495                         },
24496                         {
24497                             xtype : 'Item',
24498                             html: 'Row',
24499                             listeners : {
24500                                 click : function (_self, e)
24501                                 {
24502                                     var t = table();
24503                                     cell().deleteRow();
24504                                     syncValue();
24505                                     
24506                                     toolbar.editorcore.selectNode(t.node);
24507                                     toolbar.editorcore.onEditorEvent();   
24508                                                          
24509                                 }
24510                             },
24511                             xns : rooui.menu
24512                         },
24513                        {
24514                             xtype : 'Separator',
24515                             xns : rooui.menu
24516                         },
24517                         {
24518                             xtype : 'Item',
24519                             html: 'Table',
24520                             listeners : {
24521                                 click : function (_self, e)
24522                                 {
24523                                     var t = table();
24524                                     var nn = t.node.nextSibling || t.node.previousSibling;
24525                                     t.node.parentNode.removeChild(t.node);
24526                                     if (nn) { 
24527                                         toolbar.editorcore.selectNode(nn, true);
24528                                     }
24529                                     toolbar.editorcore.onEditorEvent();   
24530                                                          
24531                                 }
24532                             },
24533                             xns : rooui.menu
24534                         }
24535                     ]
24536                 }
24537             }
24538             
24539             // align... << fixme
24540             
24541         ];
24542         
24543     },
24544     
24545     
24546   /**
24547      * create a DomHelper friendly object - for use with
24548      * Roo.DomHelper.markup / overwrite / etc..
24549      * ?? should it be called with option to hide all editing features?
24550      */
24551  /**
24552      * create a DomHelper friendly object - for use with
24553      * Roo.DomHelper.markup / overwrite / etc..
24554      * ?? should it be called with option to hide all editing features?
24555      */
24556     toObject : function()
24557     {
24558         
24559         var ret = {
24560             tag : 'td',
24561             contenteditable : 'true', // this stops cell selection from picking the table.
24562             'data-block' : 'Td',
24563             valign : this.valign,
24564             style : {  
24565                 'text-align' :  this.textAlign,
24566                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24567                 'border-collapse' : 'collapse',
24568                 padding : '6px', // 8 for desktop / 4 for mobile
24569                 'vertical-align': this.valign
24570             },
24571             html : this.html
24572         };
24573         if (this.width != '') {
24574             ret.width = this.width;
24575             ret.style.width = this.width;
24576         }
24577         
24578         
24579         if (this.colspan > 1) {
24580             ret.colspan = this.colspan ;
24581         } 
24582         if (this.rowspan > 1) {
24583             ret.rowspan = this.rowspan ;
24584         }
24585         
24586            
24587         
24588         return ret;
24589          
24590     },
24591     
24592     readElement : function(node)
24593     {
24594         node  = node ? node : this.node ;
24595         this.width = node.style.width;
24596         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24597         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24598         this.html = node.innerHTML;
24599         
24600         
24601     },
24602      
24603     // the default cell object... at present...
24604     emptyCell : function() {
24605         return {
24606             colspan :  1,
24607             rowspan :  1,
24608             textAlign : 'left',
24609             html : "&nbsp;" // is this going to be editable now?
24610         };
24611      
24612     },
24613     
24614     removeNode : function()
24615     {
24616         return this.node.closest('table');
24617          
24618     },
24619     
24620     cellData : false,
24621     
24622     colWidths : false,
24623     
24624     toTableArray  : function()
24625     {
24626         var ret = [];
24627         var tab = this.node.closest('tr').closest('table');
24628         Array.from(tab.rows).forEach(function(r, ri){
24629             ret[ri] = [];
24630         });
24631         var rn = 0;
24632         this.colWidths = [];
24633         var all_auto = true;
24634         Array.from(tab.rows).forEach(function(r, ri){
24635             
24636             var cn = 0;
24637             Array.from(r.cells).forEach(function(ce, ci){
24638                 var c =  {
24639                     cell : ce,
24640                     row : rn,
24641                     col: cn,
24642                     colspan : ce.colSpan,
24643                     rowspan : ce.rowSpan
24644                 };
24645                 if (ce.isEqualNode(this.node)) {
24646                     this.cellData = c;
24647                 }
24648                 // if we have been filled up by a row?
24649                 if (typeof(ret[rn][cn]) != 'undefined') {
24650                     while(typeof(ret[rn][cn]) != 'undefined') {
24651                         cn++;
24652                     }
24653                     c.col = cn;
24654                 }
24655                 
24656                 if (typeof(this.colWidths[cn]) == 'undefined') {
24657                     this.colWidths[cn] =   ce.style.width;
24658                     if (this.colWidths[cn] != '') {
24659                         all_auto = false;
24660                     }
24661                 }
24662                 
24663                 
24664                 if (c.colspan < 2 && c.rowspan < 2 ) {
24665                     ret[rn][cn] = c;
24666                     cn++;
24667                     return;
24668                 }
24669                 for(var j = 0; j < c.rowspan; j++) {
24670                     if (typeof(ret[rn+j]) == 'undefined') {
24671                         continue; // we have a problem..
24672                     }
24673                     ret[rn+j][cn] = c;
24674                     for(var i = 0; i < c.colspan; i++) {
24675                         ret[rn+j][cn+i] = c;
24676                     }
24677                 }
24678                 
24679                 cn += c.colspan;
24680             }, this);
24681             rn++;
24682         }, this);
24683         
24684         // initalize widths.?
24685         // either all widths or no widths..
24686         if (all_auto) {
24687             this.colWidths[0] = false; // no widths flag.
24688         }
24689         
24690         
24691         return ret;
24692         
24693     },
24694     
24695     
24696     
24697     
24698     mergeRight: function()
24699     {
24700          
24701         // get the contents of the next cell along..
24702         var tr = this.node.closest('tr');
24703         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24704         if (i >= tr.childNodes.length - 1) {
24705             return; // no cells on right to merge with.
24706         }
24707         var table = this.toTableArray();
24708         
24709         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24710             return; // nothing right?
24711         }
24712         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24713         // right cell - must be same rowspan and on the same row.
24714         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24715             return; // right hand side is not same rowspan.
24716         }
24717         
24718         
24719         
24720         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24721         tr.removeChild(rc.cell);
24722         this.colspan += rc.colspan;
24723         this.node.setAttribute('colspan', this.colspan);
24724
24725     },
24726     
24727     
24728     mergeBelow : function()
24729     {
24730         var table = this.toTableArray();
24731         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24732             return; // no row below
24733         }
24734         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24735             return; // nothing right?
24736         }
24737         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24738         
24739         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24740             return; // right hand side is not same rowspan.
24741         }
24742         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24743         rc.cell.parentNode.removeChild(rc.cell);
24744         this.rowspan += rc.rowspan;
24745         this.node.setAttribute('rowspan', this.rowspan);
24746     },
24747     
24748     split: function()
24749     {
24750         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24751             return;
24752         }
24753         var table = this.toTableArray();
24754         var cd = this.cellData;
24755         this.rowspan = 1;
24756         this.colspan = 1;
24757         
24758         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24759             
24760             
24761             
24762             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24763                 if (r == cd.row && c == cd.col) {
24764                     this.node.removeAttribute('rowspan');
24765                     this.node.removeAttribute('colspan');
24766                     continue;
24767                 }
24768                  
24769                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24770                 ntd.removeAttribute('id'); //
24771                 //ntd.style.width  = '';
24772                 ntd.innerHTML = '';
24773                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24774             }
24775             
24776         }
24777         this.redrawAllCells(table);
24778         
24779          
24780         
24781     },
24782     
24783     
24784     
24785     redrawAllCells: function(table)
24786     {
24787         
24788          
24789         var tab = this.node.closest('tr').closest('table');
24790         var ctr = tab.rows[0].parentNode;
24791         Array.from(tab.rows).forEach(function(r, ri){
24792             
24793             Array.from(r.cells).forEach(function(ce, ci){
24794                 ce.parentNode.removeChild(ce);
24795             });
24796             r.parentNode.removeChild(r);
24797         });
24798         for(var r = 0 ; r < table.length; r++) {
24799             var re = tab.rows[r];
24800             
24801             var re = tab.ownerDocument.createElement('tr');
24802             ctr.appendChild(re);
24803             for(var c = 0 ; c < table[r].length; c++) {
24804                 if (table[r][c].cell === false) {
24805                     continue;
24806                 }
24807                 
24808                 re.appendChild(table[r][c].cell);
24809                  
24810                 table[r][c].cell = false;
24811             }
24812         }
24813         
24814     },
24815     updateWidths : function(table)
24816     {
24817         for(var r = 0 ; r < table.length; r++) {
24818            
24819             for(var c = 0 ; c < table[r].length; c++) {
24820                 if (table[r][c].cell === false) {
24821                     continue;
24822                 }
24823                 
24824                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
24825                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
24826                     el.width = Math.floor(this.colWidths[c])  +'%';
24827                     el.updateElement(el.node);
24828                 }
24829                 table[r][c].cell = false; // done
24830             }
24831         }
24832     },
24833     normalizeWidths : function(table)
24834     {
24835     
24836         if (this.colWidths[0] === false) {
24837             var nw = 100.0 / this.colWidths.length;
24838             this.colWidths.forEach(function(w,i) {
24839                 this.colWidths[i] = nw;
24840             },this);
24841             return;
24842         }
24843     
24844         var t = 0, missing = [];
24845         
24846         this.colWidths.forEach(function(w,i) {
24847             //if you mix % and
24848             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
24849             var add =  this.colWidths[i];
24850             if (add > 0) {
24851                 t+=add;
24852                 return;
24853             }
24854             missing.push(i);
24855             
24856             
24857         },this);
24858         var nc = this.colWidths.length;
24859         if (missing.length) {
24860             var mult = (nc - missing.length) / (1.0 * nc);
24861             var t = mult * t;
24862             var ew = (100 -t) / (1.0 * missing.length);
24863             this.colWidths.forEach(function(w,i) {
24864                 if (w > 0) {
24865                     this.colWidths[i] = w * mult;
24866                     return;
24867                 }
24868                 
24869                 this.colWidths[i] = ew;
24870             }, this);
24871             // have to make up numbers..
24872              
24873         }
24874         // now we should have all the widths..
24875         
24876     
24877     },
24878     
24879     shrinkColumn : function()
24880     {
24881         var table = this.toTableArray();
24882         this.normalizeWidths(table);
24883         var col = this.cellData.col;
24884         var nw = this.colWidths[col] * 0.8;
24885         if (nw < 5) {
24886             return;
24887         }
24888         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24889         this.colWidths.forEach(function(w,i) {
24890             if (i == col) {
24891                  this.colWidths[i] = nw;
24892                 return;
24893             }
24894             this.colWidths[i] += otherAdd
24895         }, this);
24896         this.updateWidths(table);
24897          
24898     },
24899     growColumn : function()
24900     {
24901         var table = this.toTableArray();
24902         this.normalizeWidths(table);
24903         var col = this.cellData.col;
24904         var nw = this.colWidths[col] * 1.2;
24905         if (nw > 90) {
24906             return;
24907         }
24908         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24909         this.colWidths.forEach(function(w,i) {
24910             if (i == col) {
24911                 this.colWidths[i] = nw;
24912                 return;
24913             }
24914             this.colWidths[i] -= otherSub
24915         }, this);
24916         this.updateWidths(table);
24917          
24918     },
24919     deleteRow : function()
24920     {
24921         // delete this rows 'tr'
24922         // if any of the cells in this row have a rowspan > 1 && row!= this row..
24923         // then reduce the rowspan.
24924         var table = this.toTableArray();
24925         // this.cellData.row;
24926         for (var i =0;i< table[this.cellData.row].length ; i++) {
24927             var c = table[this.cellData.row][i];
24928             if (c.row != this.cellData.row) {
24929                 
24930                 c.rowspan--;
24931                 c.cell.setAttribute('rowspan', c.rowspan);
24932                 continue;
24933             }
24934             if (c.rowspan > 1) {
24935                 c.rowspan--;
24936                 c.cell.setAttribute('rowspan', c.rowspan);
24937             }
24938         }
24939         table.splice(this.cellData.row,1);
24940         this.redrawAllCells(table);
24941         
24942     },
24943     deleteColumn : function()
24944     {
24945         var table = this.toTableArray();
24946         
24947         for (var i =0;i< table.length ; i++) {
24948             var c = table[i][this.cellData.col];
24949             if (c.col != this.cellData.col) {
24950                 table[i][this.cellData.col].colspan--;
24951             } else if (c.colspan > 1) {
24952                 c.colspan--;
24953                 c.cell.setAttribute('colspan', c.colspan);
24954             }
24955             table[i].splice(this.cellData.col,1);
24956         }
24957         
24958         this.redrawAllCells(table);
24959     }
24960     
24961     
24962     
24963     
24964 })
24965
24966 //<script type="text/javascript">
24967
24968 /*
24969  * Based  Ext JS Library 1.1.1
24970  * Copyright(c) 2006-2007, Ext JS, LLC.
24971  * LGPL
24972  *
24973  */
24974  
24975 /**
24976  * @class Roo.HtmlEditorCore
24977  * @extends Roo.Component
24978  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24979  *
24980  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24981  */
24982
24983 Roo.HtmlEditorCore = function(config){
24984     
24985     
24986     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24987     
24988     
24989     this.addEvents({
24990         /**
24991          * @event initialize
24992          * Fires when the editor is fully initialized (including the iframe)
24993          * @param {Roo.HtmlEditorCore} this
24994          */
24995         initialize: true,
24996         /**
24997          * @event activate
24998          * Fires when the editor is first receives the focus. Any insertion must wait
24999          * until after this event.
25000          * @param {Roo.HtmlEditorCore} this
25001          */
25002         activate: true,
25003          /**
25004          * @event beforesync
25005          * Fires before the textarea is updated with content from the editor iframe. Return false
25006          * to cancel the sync.
25007          * @param {Roo.HtmlEditorCore} this
25008          * @param {String} html
25009          */
25010         beforesync: true,
25011          /**
25012          * @event beforepush
25013          * Fires before the iframe editor is updated with content from the textarea. Return false
25014          * to cancel the push.
25015          * @param {Roo.HtmlEditorCore} this
25016          * @param {String} html
25017          */
25018         beforepush: true,
25019          /**
25020          * @event sync
25021          * Fires when the textarea is updated with content from the editor iframe.
25022          * @param {Roo.HtmlEditorCore} this
25023          * @param {String} html
25024          */
25025         sync: true,
25026          /**
25027          * @event push
25028          * Fires when the iframe editor is updated with content from the textarea.
25029          * @param {Roo.HtmlEditorCore} this
25030          * @param {String} html
25031          */
25032         push: true,
25033         
25034         /**
25035          * @event editorevent
25036          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25037          * @param {Roo.HtmlEditorCore} this
25038          */
25039         editorevent: true 
25040          
25041         
25042     });
25043     
25044     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25045     
25046     // defaults : white / black...
25047     this.applyBlacklists();
25048     
25049     
25050     
25051 };
25052
25053
25054 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25055
25056
25057      /**
25058      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25059      */
25060     
25061     owner : false,
25062     
25063      /**
25064      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25065      *                        Roo.resizable.
25066      */
25067     resizable : false,
25068      /**
25069      * @cfg {Number} height (in pixels)
25070      */   
25071     height: 300,
25072    /**
25073      * @cfg {Number} width (in pixels)
25074      */   
25075     width: 500,
25076      /**
25077      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25078      *         if you are doing an email editor, this probably needs disabling, it's designed
25079      */
25080     autoClean: true,
25081     
25082     /**
25083      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25084      */
25085     enableBlocks : true,
25086     /**
25087      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25088      * 
25089      */
25090     stylesheets: false,
25091      /**
25092      * @cfg {String} language default en - language of text (usefull for rtl languages)
25093      * 
25094      */
25095     language: 'en',
25096     
25097     /**
25098      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25099      *          - by default they are stripped - if you are editing email you may need this.
25100      */
25101     allowComments: false,
25102     // id of frame..
25103     frameId: false,
25104     
25105     // private properties
25106     validationEvent : false,
25107     deferHeight: true,
25108     initialized : false,
25109     activated : false,
25110     sourceEditMode : false,
25111     onFocus : Roo.emptyFn,
25112     iframePad:3,
25113     hideMode:'offsets',
25114     
25115     clearUp: true,
25116     
25117     // blacklist + whitelisted elements..
25118     black: false,
25119     white: false,
25120      
25121     bodyCls : '',
25122
25123     
25124     undoManager : false,
25125     /**
25126      * Protected method that will not generally be called directly. It
25127      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25128      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25129      */
25130     getDocMarkup : function(){
25131         // body styles..
25132         var st = '';
25133         
25134         // inherit styels from page...?? 
25135         if (this.stylesheets === false) {
25136             
25137             Roo.get(document.head).select('style').each(function(node) {
25138                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25139             });
25140             
25141             Roo.get(document.head).select('link').each(function(node) { 
25142                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25143             });
25144             
25145         } else if (!this.stylesheets.length) {
25146                 // simple..
25147                 st = '<style type="text/css">' +
25148                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25149                    '</style>';
25150         } else {
25151             for (var i in this.stylesheets) {
25152                 if (typeof(this.stylesheets[i]) != 'string') {
25153                     continue;
25154                 }
25155                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25156             }
25157             
25158         }
25159         
25160         st +=  '<style type="text/css">' +
25161             'IMG { cursor: pointer } ' +
25162         '</style>';
25163         
25164         st += '<meta name="google" content="notranslate">';
25165         
25166         var cls = 'notranslate roo-htmleditor-body';
25167         
25168         if(this.bodyCls.length){
25169             cls += ' ' + this.bodyCls;
25170         }
25171         
25172         return '<html  class="notranslate" translate="no"><head>' + st  +
25173             //<style type="text/css">' +
25174             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25175             //'</style>' +
25176             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25177     },
25178
25179     // private
25180     onRender : function(ct, position)
25181     {
25182         var _t = this;
25183         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25184         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25185         
25186         
25187         this.el.dom.style.border = '0 none';
25188         this.el.dom.setAttribute('tabIndex', -1);
25189         this.el.addClass('x-hidden hide');
25190         
25191         
25192         
25193         if(Roo.isIE){ // fix IE 1px bogus margin
25194             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25195         }
25196        
25197         
25198         this.frameId = Roo.id();
25199         
25200          
25201         
25202         var iframe = this.owner.wrap.createChild({
25203             tag: 'iframe',
25204             cls: 'form-control', // bootstrap..
25205             id: this.frameId,
25206             name: this.frameId,
25207             frameBorder : 'no',
25208             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25209         }, this.el
25210         );
25211         
25212         
25213         this.iframe = iframe.dom;
25214
25215         this.assignDocWin();
25216         
25217         this.doc.designMode = 'on';
25218        
25219         this.doc.open();
25220         this.doc.write(this.getDocMarkup());
25221         this.doc.close();
25222
25223         
25224         var task = { // must defer to wait for browser to be ready
25225             run : function(){
25226                 //console.log("run task?" + this.doc.readyState);
25227                 this.assignDocWin();
25228                 if(this.doc.body || this.doc.readyState == 'complete'){
25229                     try {
25230                         this.doc.designMode="on";
25231                         
25232                     } catch (e) {
25233                         return;
25234                     }
25235                     Roo.TaskMgr.stop(task);
25236                     this.initEditor.defer(10, this);
25237                 }
25238             },
25239             interval : 10,
25240             duration: 10000,
25241             scope: this
25242         };
25243         Roo.TaskMgr.start(task);
25244
25245     },
25246
25247     // private
25248     onResize : function(w, h)
25249     {
25250          Roo.log('resize: ' +w + ',' + h );
25251         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25252         if(!this.iframe){
25253             return;
25254         }
25255         if(typeof w == 'number'){
25256             
25257             this.iframe.style.width = w + 'px';
25258         }
25259         if(typeof h == 'number'){
25260             
25261             this.iframe.style.height = h + 'px';
25262             if(this.doc){
25263                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25264             }
25265         }
25266         
25267     },
25268
25269     /**
25270      * Toggles the editor between standard and source edit mode.
25271      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25272      */
25273     toggleSourceEdit : function(sourceEditMode){
25274         
25275         this.sourceEditMode = sourceEditMode === true;
25276         
25277         if(this.sourceEditMode){
25278  
25279             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25280             
25281         }else{
25282             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25283             //this.iframe.className = '';
25284             this.deferFocus();
25285         }
25286         //this.setSize(this.owner.wrap.getSize());
25287         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25288     },
25289
25290     
25291   
25292
25293     /**
25294      * Protected method that will not generally be called directly. If you need/want
25295      * custom HTML cleanup, this is the method you should override.
25296      * @param {String} html The HTML to be cleaned
25297      * return {String} The cleaned HTML
25298      */
25299     cleanHtml : function(html)
25300     {
25301         html = String(html);
25302         if(html.length > 5){
25303             if(Roo.isSafari){ // strip safari nonsense
25304                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25305             }
25306         }
25307         if(html == '&nbsp;'){
25308             html = '';
25309         }
25310         return html;
25311     },
25312
25313     /**
25314      * HTML Editor -> Textarea
25315      * Protected method that will not generally be called directly. Syncs the contents
25316      * of the editor iframe with the textarea.
25317      */
25318     syncValue : function()
25319     {
25320         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25321         if(this.initialized){
25322             
25323             this.undoManager.addEvent();
25324
25325             
25326             var bd = (this.doc.body || this.doc.documentElement);
25327            
25328             
25329             var sel = this.win.getSelection();
25330             
25331             var div = document.createElement('div');
25332             div.innerHTML = bd.innerHTML;
25333             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25334             if (gtx.length > 0) {
25335                 var rm = gtx.item(0).parentNode;
25336                 rm.parentNode.removeChild(rm);
25337             }
25338             
25339            
25340             if (this.enableBlocks) {
25341                 new Roo.htmleditor.FilterBlock({ node : div });
25342             }
25343             //?? tidy?
25344             var tidy = new Roo.htmleditor.TidySerializer({
25345                 inner:  true
25346             });
25347             var html  = tidy.serialize(div);
25348             
25349             
25350             if(Roo.isSafari){
25351                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25352                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25353                 if(m && m[1]){
25354                     html = '<div style="'+m[0]+'">' + html + '</div>';
25355                 }
25356             }
25357             html = this.cleanHtml(html);
25358             // fix up the special chars.. normaly like back quotes in word...
25359             // however we do not want to do this with chinese..
25360             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25361                 
25362                 var cc = match.charCodeAt();
25363
25364                 // Get the character value, handling surrogate pairs
25365                 if (match.length == 2) {
25366                     // It's a surrogate pair, calculate the Unicode code point
25367                     var high = match.charCodeAt(0) - 0xD800;
25368                     var low  = match.charCodeAt(1) - 0xDC00;
25369                     cc = (high * 0x400) + low + 0x10000;
25370                 }  else if (
25371                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25372                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25373                     (cc >= 0xf900 && cc < 0xfb00 )
25374                 ) {
25375                         return match;
25376                 }  
25377          
25378                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25379                 return "&#" + cc + ";";
25380                 
25381                 
25382             });
25383             
25384             
25385              
25386             if(this.owner.fireEvent('beforesync', this, html) !== false){
25387                 this.el.dom.value = html;
25388                 this.owner.fireEvent('sync', this, html);
25389             }
25390         }
25391     },
25392
25393     /**
25394      * TEXTAREA -> EDITABLE
25395      * Protected method that will not generally be called directly. Pushes the value of the textarea
25396      * into the iframe editor.
25397      */
25398     pushValue : function()
25399     {
25400         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25401         if(this.initialized){
25402             var v = this.el.dom.value.trim();
25403             
25404             
25405             if(this.owner.fireEvent('beforepush', this, v) !== false){
25406                 var d = (this.doc.body || this.doc.documentElement);
25407                 d.innerHTML = v;
25408                  
25409                 this.el.dom.value = d.innerHTML;
25410                 this.owner.fireEvent('push', this, v);
25411             }
25412             if (this.autoClean) {
25413                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25414                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25415             }
25416             if (this.enableBlocks) {
25417                 Roo.htmleditor.Block.initAll(this.doc.body);
25418             }
25419             
25420             this.updateLanguage();
25421             
25422             var lc = this.doc.body.lastChild;
25423             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25424                 // add an extra line at the end.
25425                 this.doc.body.appendChild(this.doc.createElement('br'));
25426             }
25427             
25428             
25429         }
25430     },
25431
25432     // private
25433     deferFocus : function(){
25434         this.focus.defer(10, this);
25435     },
25436
25437     // doc'ed in Field
25438     focus : function(){
25439         if(this.win && !this.sourceEditMode){
25440             this.win.focus();
25441         }else{
25442             this.el.focus();
25443         }
25444     },
25445     
25446     assignDocWin: function()
25447     {
25448         var iframe = this.iframe;
25449         
25450          if(Roo.isIE){
25451             this.doc = iframe.contentWindow.document;
25452             this.win = iframe.contentWindow;
25453         } else {
25454 //            if (!Roo.get(this.frameId)) {
25455 //                return;
25456 //            }
25457 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25458 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25459             
25460             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25461                 return;
25462             }
25463             
25464             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25465             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25466         }
25467     },
25468     
25469     // private
25470     initEditor : function(){
25471         //console.log("INIT EDITOR");
25472         this.assignDocWin();
25473         
25474         
25475         
25476         this.doc.designMode="on";
25477         this.doc.open();
25478         this.doc.write(this.getDocMarkup());
25479         this.doc.close();
25480         
25481         var dbody = (this.doc.body || this.doc.documentElement);
25482         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25483         // this copies styles from the containing element into thsi one..
25484         // not sure why we need all of this..
25485         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25486         
25487         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25488         //ss['background-attachment'] = 'fixed'; // w3c
25489         dbody.bgProperties = 'fixed'; // ie
25490         dbody.setAttribute("translate", "no");
25491         
25492         //Roo.DomHelper.applyStyles(dbody, ss);
25493         Roo.EventManager.on(this.doc, {
25494              
25495             'mouseup': this.onEditorEvent,
25496             'dblclick': this.onEditorEvent,
25497             'click': this.onEditorEvent,
25498             'keyup': this.onEditorEvent,
25499             
25500             buffer:100,
25501             scope: this
25502         });
25503         Roo.EventManager.on(this.doc, {
25504             'paste': this.onPasteEvent,
25505             scope : this
25506         });
25507         if(Roo.isGecko){
25508             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25509         }
25510         //??? needed???
25511         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25512             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25513         }
25514         this.initialized = true;
25515
25516         
25517         // initialize special key events - enter
25518         new Roo.htmleditor.KeyEnter({core : this});
25519         
25520          
25521         
25522         this.owner.fireEvent('initialize', this);
25523         this.pushValue();
25524     },
25525     // this is to prevent a href clicks resulting in a redirect?
25526    
25527     onPasteEvent : function(e,v)
25528     {
25529         // I think we better assume paste is going to be a dirty load of rubish from word..
25530         
25531         // even pasting into a 'email version' of this widget will have to clean up that mess.
25532         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25533         
25534         // check what type of paste - if it's an image, then handle it differently.
25535         if (cd.files.length > 0) {
25536             // pasting images?
25537             var urlAPI = (window.createObjectURL && window) || 
25538                 (window.URL && URL.revokeObjectURL && URL) || 
25539                 (window.webkitURL && webkitURL);
25540     
25541             var url = urlAPI.createObjectURL( cd.files[0]);
25542             this.insertAtCursor('<img src=" + url + ">');
25543             return false;
25544         }
25545         
25546         var html = cd.getData('text/html'); // clipboard event
25547         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25548         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25549         Roo.log(images);
25550         //Roo.log(imgs);
25551         // fixme..
25552         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
25553                        .map(function(g) { return g.toDataURL(); })
25554                        .filter(function(g) { return g != 'about:blank'; });
25555         
25556         
25557         html = this.cleanWordChars(html);
25558         
25559         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25560         
25561         
25562         var sn = this.getParentElement();
25563         // check if d contains a table, and prevent nesting??
25564         //Roo.log(d.getElementsByTagName('table'));
25565         //Roo.log(sn);
25566         //Roo.log(sn.closest('table'));
25567         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25568             e.preventDefault();
25569             this.insertAtCursor("You can not nest tables");
25570             //Roo.log("prevent?"); // fixme - 
25571             return false;
25572         }
25573         
25574         if (images.length > 0) {
25575             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25576                 img.setAttribute('src', images[i]);
25577             });
25578         }
25579         if (this.autoClean) {
25580             new Roo.htmleditor.FilterStyleToTag({ node : d });
25581             new Roo.htmleditor.FilterAttributes({
25582                 node : d,
25583                 attrib_white : ['href', 'src', 'name', 'align'],
25584                 attrib_clean : ['href', 'src' ] 
25585             });
25586             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25587             // should be fonts..
25588             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25589             new Roo.htmleditor.FilterParagraph({ node : d });
25590             new Roo.htmleditor.FilterSpan({ node : d });
25591             new Roo.htmleditor.FilterLongBr({ node : d });
25592         }
25593         if (this.enableBlocks) {
25594                 
25595             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25596                 if (img.closest('figure')) { // assume!! that it's aready
25597                     return;
25598                 }
25599                 var fig  = new Roo.htmleditor.BlockFigure({
25600                     image_src  : img.src
25601                 });
25602                 fig.updateElement(img); // replace it..
25603                 
25604             });
25605         }
25606         
25607         
25608         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25609         if (this.enableBlocks) {
25610             Roo.htmleditor.Block.initAll(this.doc.body);
25611         }
25612         
25613         
25614         e.preventDefault();
25615         return false;
25616         // default behaveiour should be our local cleanup paste? (optional?)
25617         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25618         //this.owner.fireEvent('paste', e, v);
25619     },
25620     // private
25621     onDestroy : function(){
25622         
25623         
25624         
25625         if(this.rendered){
25626             
25627             //for (var i =0; i < this.toolbars.length;i++) {
25628             //    // fixme - ask toolbars for heights?
25629             //    this.toolbars[i].onDestroy();
25630            // }
25631             
25632             //this.wrap.dom.innerHTML = '';
25633             //this.wrap.remove();
25634         }
25635     },
25636
25637     // private
25638     onFirstFocus : function(){
25639         
25640         this.assignDocWin();
25641         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25642         
25643         this.activated = true;
25644          
25645     
25646         if(Roo.isGecko){ // prevent silly gecko errors
25647             this.win.focus();
25648             var s = this.win.getSelection();
25649             if(!s.focusNode || s.focusNode.nodeType != 3){
25650                 var r = s.getRangeAt(0);
25651                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25652                 r.collapse(true);
25653                 this.deferFocus();
25654             }
25655             try{
25656                 this.execCmd('useCSS', true);
25657                 this.execCmd('styleWithCSS', false);
25658             }catch(e){}
25659         }
25660         this.owner.fireEvent('activate', this);
25661     },
25662
25663     // private
25664     adjustFont: function(btn){
25665         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25666         //if(Roo.isSafari){ // safari
25667         //    adjust *= 2;
25668        // }
25669         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25670         if(Roo.isSafari){ // safari
25671             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25672             v =  (v < 10) ? 10 : v;
25673             v =  (v > 48) ? 48 : v;
25674             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25675             
25676         }
25677         
25678         
25679         v = Math.max(1, v+adjust);
25680         
25681         this.execCmd('FontSize', v  );
25682     },
25683
25684     onEditorEvent : function(e)
25685     {
25686          
25687         
25688         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25689             return; // we do not handle this.. (undo manager does..)
25690         }
25691         // in theory this detects if the last element is not a br, then we try and do that.
25692         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25693         if (e &&
25694             e.target.nodeName == 'BODY' &&
25695             e.type == "mouseup" &&
25696             this.doc.body.lastChild
25697            ) {
25698             var lc = this.doc.body.lastChild;
25699             // gtx-trans is google translate plugin adding crap.
25700             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25701                 lc = lc.previousSibling;
25702             }
25703             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25704             // if last element is <BR> - then dont do anything.
25705             
25706                 var ns = this.doc.createElement('br');
25707                 this.doc.body.appendChild(ns);
25708                 range = this.doc.createRange();
25709                 range.setStartAfter(ns);
25710                 range.collapse(true);
25711                 var sel = this.win.getSelection();
25712                 sel.removeAllRanges();
25713                 sel.addRange(range);
25714             }
25715         }
25716         
25717         
25718         
25719         this.fireEditorEvent(e);
25720       //  this.updateToolbar();
25721         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25722     },
25723     
25724     fireEditorEvent: function(e)
25725     {
25726         this.owner.fireEvent('editorevent', this, e);
25727     },
25728
25729     insertTag : function(tg)
25730     {
25731         // could be a bit smarter... -> wrap the current selected tRoo..
25732         if (tg.toLowerCase() == 'span' ||
25733             tg.toLowerCase() == 'code' ||
25734             tg.toLowerCase() == 'sup' ||
25735             tg.toLowerCase() == 'sub' 
25736             ) {
25737             
25738             range = this.createRange(this.getSelection());
25739             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25740             wrappingNode.appendChild(range.extractContents());
25741             range.insertNode(wrappingNode);
25742
25743             return;
25744             
25745             
25746             
25747         }
25748         this.execCmd("formatblock",   tg);
25749         this.undoManager.addEvent(); 
25750     },
25751     
25752     insertText : function(txt)
25753     {
25754         
25755         
25756         var range = this.createRange();
25757         range.deleteContents();
25758                //alert(Sender.getAttribute('label'));
25759                
25760         range.insertNode(this.doc.createTextNode(txt));
25761         this.undoManager.addEvent();
25762     } ,
25763     
25764      
25765
25766     /**
25767      * Executes a Midas editor command on the editor document and performs necessary focus and
25768      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25769      * @param {String} cmd The Midas command
25770      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25771      */
25772     relayCmd : function(cmd, value)
25773     {
25774         
25775         switch (cmd) {
25776             case 'justifyleft':
25777             case 'justifyright':
25778             case 'justifycenter':
25779                 // if we are in a cell, then we will adjust the
25780                 var n = this.getParentElement();
25781                 var td = n.closest('td');
25782                 if (td) {
25783                     var bl = Roo.htmleditor.Block.factory(td);
25784                     bl.textAlign = cmd.replace('justify','');
25785                     bl.updateElement();
25786                     this.owner.fireEvent('editorevent', this);
25787                     return;
25788                 }
25789                 this.execCmd('styleWithCSS', true); // 
25790                 break;
25791             case 'bold':
25792             case 'italic':
25793                 // if there is no selection, then we insert, and set the curson inside it..
25794                 this.execCmd('styleWithCSS', false); 
25795                 break;
25796                 
25797         
25798             default:
25799                 break;
25800         }
25801         
25802         
25803         this.win.focus();
25804         this.execCmd(cmd, value);
25805         this.owner.fireEvent('editorevent', this);
25806         //this.updateToolbar();
25807         this.owner.deferFocus();
25808     },
25809
25810     /**
25811      * Executes a Midas editor command directly on the editor document.
25812      * For visual commands, you should use {@link #relayCmd} instead.
25813      * <b>This should only be called after the editor is initialized.</b>
25814      * @param {String} cmd The Midas command
25815      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25816      */
25817     execCmd : function(cmd, value){
25818         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25819         this.syncValue();
25820     },
25821  
25822  
25823    
25824     /**
25825      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25826      * to insert tRoo.
25827      * @param {String} text | dom node.. 
25828      */
25829     insertAtCursor : function(text)
25830     {
25831         
25832         if(!this.activated){
25833             return;
25834         }
25835          
25836         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25837             this.win.focus();
25838             
25839             
25840             // from jquery ui (MIT licenced)
25841             var range, node;
25842             var win = this.win;
25843             
25844             if (win.getSelection && win.getSelection().getRangeAt) {
25845                 
25846                 // delete the existing?
25847                 
25848                 this.createRange(this.getSelection()).deleteContents();
25849                 range = win.getSelection().getRangeAt(0);
25850                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25851                 range.insertNode(node);
25852                 range = range.cloneRange();
25853                 range.collapse(false);
25854                  
25855                 win.getSelection().removeAllRanges();
25856                 win.getSelection().addRange(range);
25857                 
25858                 
25859                 
25860             } else if (win.document.selection && win.document.selection.createRange) {
25861                 // no firefox support
25862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25863                 win.document.selection.createRange().pasteHTML(txt);
25864             
25865             } else {
25866                 // no firefox support
25867                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25868                 this.execCmd('InsertHTML', txt);
25869             } 
25870             this.syncValue();
25871             
25872             this.deferFocus();
25873         }
25874     },
25875  // private
25876     mozKeyPress : function(e){
25877         if(e.ctrlKey){
25878             var c = e.getCharCode(), cmd;
25879           
25880             if(c > 0){
25881                 c = String.fromCharCode(c).toLowerCase();
25882                 switch(c){
25883                     case 'b':
25884                         cmd = 'bold';
25885                         break;
25886                     case 'i':
25887                         cmd = 'italic';
25888                         break;
25889                     
25890                     case 'u':
25891                         cmd = 'underline';
25892                         break;
25893                     
25894                     //case 'v':
25895                       //  this.cleanUpPaste.defer(100, this);
25896                       //  return;
25897                         
25898                 }
25899                 if(cmd){
25900                     
25901                     this.relayCmd(cmd);
25902                     //this.win.focus();
25903                     //this.execCmd(cmd);
25904                     //this.deferFocus();
25905                     e.preventDefault();
25906                 }
25907                 
25908             }
25909         }
25910     },
25911
25912     // private
25913     fixKeys : function(){ // load time branching for fastest keydown performance
25914         
25915         
25916         if(Roo.isIE){
25917             return function(e){
25918                 var k = e.getKey(), r;
25919                 if(k == e.TAB){
25920                     e.stopEvent();
25921                     r = this.doc.selection.createRange();
25922                     if(r){
25923                         r.collapse(true);
25924                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25925                         this.deferFocus();
25926                     }
25927                     return;
25928                 }
25929                 /// this is handled by Roo.htmleditor.KeyEnter
25930                  /*
25931                 if(k == e.ENTER){
25932                     r = this.doc.selection.createRange();
25933                     if(r){
25934                         var target = r.parentElement();
25935                         if(!target || target.tagName.toLowerCase() != 'li'){
25936                             e.stopEvent();
25937                             r.pasteHTML('<br/>');
25938                             r.collapse(false);
25939                             r.select();
25940                         }
25941                     }
25942                 }
25943                 */
25944                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25945                 //    this.cleanUpPaste.defer(100, this);
25946                 //    return;
25947                 //}
25948                 
25949                 
25950             };
25951         }else if(Roo.isOpera){
25952             return function(e){
25953                 var k = e.getKey();
25954                 if(k == e.TAB){
25955                     e.stopEvent();
25956                     this.win.focus();
25957                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25958                     this.deferFocus();
25959                 }
25960                
25961                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25962                 //    this.cleanUpPaste.defer(100, this);
25963                  //   return;
25964                 //}
25965                 
25966             };
25967         }else if(Roo.isSafari){
25968             return function(e){
25969                 var k = e.getKey();
25970                 
25971                 if(k == e.TAB){
25972                     e.stopEvent();
25973                     this.execCmd('InsertText','\t');
25974                     this.deferFocus();
25975                     return;
25976                 }
25977                  this.mozKeyPress(e);
25978                 
25979                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25980                  //   this.cleanUpPaste.defer(100, this);
25981                  //   return;
25982                // }
25983                 
25984              };
25985         }
25986     }(),
25987     
25988     getAllAncestors: function()
25989     {
25990         var p = this.getSelectedNode();
25991         var a = [];
25992         if (!p) {
25993             a.push(p); // push blank onto stack..
25994             p = this.getParentElement();
25995         }
25996         
25997         
25998         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25999             a.push(p);
26000             p = p.parentNode;
26001         }
26002         a.push(this.doc.body);
26003         return a;
26004     },
26005     lastSel : false,
26006     lastSelNode : false,
26007     
26008     
26009     getSelection : function() 
26010     {
26011         this.assignDocWin();
26012         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26013     },
26014     /**
26015      * Select a dom node
26016      * @param {DomElement} node the node to select
26017      */
26018     selectNode : function(node, collapse)
26019     {
26020         var nodeRange = node.ownerDocument.createRange();
26021         try {
26022             nodeRange.selectNode(node);
26023         } catch (e) {
26024             nodeRange.selectNodeContents(node);
26025         }
26026         if (collapse === true) {
26027             nodeRange.collapse(true);
26028         }
26029         //
26030         var s = this.win.getSelection();
26031         s.removeAllRanges();
26032         s.addRange(nodeRange);
26033     },
26034     
26035     getSelectedNode: function() 
26036     {
26037         // this may only work on Gecko!!!
26038         
26039         // should we cache this!!!!
26040         
26041          
26042          
26043         var range = this.createRange(this.getSelection()).cloneRange();
26044         
26045         if (Roo.isIE) {
26046             var parent = range.parentElement();
26047             while (true) {
26048                 var testRange = range.duplicate();
26049                 testRange.moveToElementText(parent);
26050                 if (testRange.inRange(range)) {
26051                     break;
26052                 }
26053                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26054                     break;
26055                 }
26056                 parent = parent.parentElement;
26057             }
26058             return parent;
26059         }
26060         
26061         // is ancestor a text element.
26062         var ac =  range.commonAncestorContainer;
26063         if (ac.nodeType == 3) {
26064             ac = ac.parentNode;
26065         }
26066         
26067         var ar = ac.childNodes;
26068          
26069         var nodes = [];
26070         var other_nodes = [];
26071         var has_other_nodes = false;
26072         for (var i=0;i<ar.length;i++) {
26073             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26074                 continue;
26075             }
26076             // fullly contained node.
26077             
26078             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26079                 nodes.push(ar[i]);
26080                 continue;
26081             }
26082             
26083             // probably selected..
26084             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26085                 other_nodes.push(ar[i]);
26086                 continue;
26087             }
26088             // outer..
26089             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26090                 continue;
26091             }
26092             
26093             
26094             has_other_nodes = true;
26095         }
26096         if (!nodes.length && other_nodes.length) {
26097             nodes= other_nodes;
26098         }
26099         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26100             return false;
26101         }
26102         
26103         return nodes[0];
26104     },
26105     
26106     
26107     createRange: function(sel)
26108     {
26109         // this has strange effects when using with 
26110         // top toolbar - not sure if it's a great idea.
26111         //this.editor.contentWindow.focus();
26112         if (typeof sel != "undefined") {
26113             try {
26114                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26115             } catch(e) {
26116                 return this.doc.createRange();
26117             }
26118         } else {
26119             return this.doc.createRange();
26120         }
26121     },
26122     getParentElement: function()
26123     {
26124         
26125         this.assignDocWin();
26126         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26127         
26128         var range = this.createRange(sel);
26129          
26130         try {
26131             var p = range.commonAncestorContainer;
26132             while (p.nodeType == 3) { // text node
26133                 p = p.parentNode;
26134             }
26135             return p;
26136         } catch (e) {
26137             return null;
26138         }
26139     
26140     },
26141     /***
26142      *
26143      * Range intersection.. the hard stuff...
26144      *  '-1' = before
26145      *  '0' = hits..
26146      *  '1' = after.
26147      *         [ -- selected range --- ]
26148      *   [fail]                        [fail]
26149      *
26150      *    basically..
26151      *      if end is before start or  hits it. fail.
26152      *      if start is after end or hits it fail.
26153      *
26154      *   if either hits (but other is outside. - then it's not 
26155      *   
26156      *    
26157      **/
26158     
26159     
26160     // @see http://www.thismuchiknow.co.uk/?p=64.
26161     rangeIntersectsNode : function(range, node)
26162     {
26163         var nodeRange = node.ownerDocument.createRange();
26164         try {
26165             nodeRange.selectNode(node);
26166         } catch (e) {
26167             nodeRange.selectNodeContents(node);
26168         }
26169     
26170         var rangeStartRange = range.cloneRange();
26171         rangeStartRange.collapse(true);
26172     
26173         var rangeEndRange = range.cloneRange();
26174         rangeEndRange.collapse(false);
26175     
26176         var nodeStartRange = nodeRange.cloneRange();
26177         nodeStartRange.collapse(true);
26178     
26179         var nodeEndRange = nodeRange.cloneRange();
26180         nodeEndRange.collapse(false);
26181     
26182         return rangeStartRange.compareBoundaryPoints(
26183                  Range.START_TO_START, nodeEndRange) == -1 &&
26184                rangeEndRange.compareBoundaryPoints(
26185                  Range.START_TO_START, nodeStartRange) == 1;
26186         
26187          
26188     },
26189     rangeCompareNode : function(range, node)
26190     {
26191         var nodeRange = node.ownerDocument.createRange();
26192         try {
26193             nodeRange.selectNode(node);
26194         } catch (e) {
26195             nodeRange.selectNodeContents(node);
26196         }
26197         
26198         
26199         range.collapse(true);
26200     
26201         nodeRange.collapse(true);
26202      
26203         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26204         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26205          
26206         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26207         
26208         var nodeIsBefore   =  ss == 1;
26209         var nodeIsAfter    = ee == -1;
26210         
26211         if (nodeIsBefore && nodeIsAfter) {
26212             return 0; // outer
26213         }
26214         if (!nodeIsBefore && nodeIsAfter) {
26215             return 1; //right trailed.
26216         }
26217         
26218         if (nodeIsBefore && !nodeIsAfter) {
26219             return 2;  // left trailed.
26220         }
26221         // fully contined.
26222         return 3;
26223     },
26224  
26225     cleanWordChars : function(input) {// change the chars to hex code
26226         
26227        var swapCodes  = [ 
26228             [    8211, "&#8211;" ], 
26229             [    8212, "&#8212;" ], 
26230             [    8216,  "'" ],  
26231             [    8217, "'" ],  
26232             [    8220, '"' ],  
26233             [    8221, '"' ],  
26234             [    8226, "*" ],  
26235             [    8230, "..." ]
26236         ]; 
26237         var output = input;
26238         Roo.each(swapCodes, function(sw) { 
26239             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26240             
26241             output = output.replace(swapper, sw[1]);
26242         });
26243         
26244         return output;
26245     },
26246     
26247      
26248     
26249         
26250     
26251     cleanUpChild : function (node)
26252     {
26253         
26254         new Roo.htmleditor.FilterComment({node : node});
26255         new Roo.htmleditor.FilterAttributes({
26256                 node : node,
26257                 attrib_black : this.ablack,
26258                 attrib_clean : this.aclean,
26259                 style_white : this.cwhite,
26260                 style_black : this.cblack
26261         });
26262         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26263         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26264          
26265         
26266     },
26267     
26268     /**
26269      * Clean up MS wordisms...
26270      * @deprecated - use filter directly
26271      */
26272     cleanWord : function(node)
26273     {
26274         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26275         
26276     },
26277    
26278     
26279     /**
26280
26281      * @deprecated - use filters
26282      */
26283     cleanTableWidths : function(node)
26284     {
26285         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26286         
26287  
26288     },
26289     
26290      
26291         
26292     applyBlacklists : function()
26293     {
26294         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26295         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26296         
26297         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26298         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26299         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26300         
26301         this.white = [];
26302         this.black = [];
26303         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26304             if (b.indexOf(tag) > -1) {
26305                 return;
26306             }
26307             this.white.push(tag);
26308             
26309         }, this);
26310         
26311         Roo.each(w, function(tag) {
26312             if (b.indexOf(tag) > -1) {
26313                 return;
26314             }
26315             if (this.white.indexOf(tag) > -1) {
26316                 return;
26317             }
26318             this.white.push(tag);
26319             
26320         }, this);
26321         
26322         
26323         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26324             if (w.indexOf(tag) > -1) {
26325                 return;
26326             }
26327             this.black.push(tag);
26328             
26329         }, this);
26330         
26331         Roo.each(b, function(tag) {
26332             if (w.indexOf(tag) > -1) {
26333                 return;
26334             }
26335             if (this.black.indexOf(tag) > -1) {
26336                 return;
26337             }
26338             this.black.push(tag);
26339             
26340         }, this);
26341         
26342         
26343         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26344         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26345         
26346         this.cwhite = [];
26347         this.cblack = [];
26348         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26349             if (b.indexOf(tag) > -1) {
26350                 return;
26351             }
26352             this.cwhite.push(tag);
26353             
26354         }, this);
26355         
26356         Roo.each(w, function(tag) {
26357             if (b.indexOf(tag) > -1) {
26358                 return;
26359             }
26360             if (this.cwhite.indexOf(tag) > -1) {
26361                 return;
26362             }
26363             this.cwhite.push(tag);
26364             
26365         }, this);
26366         
26367         
26368         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26369             if (w.indexOf(tag) > -1) {
26370                 return;
26371             }
26372             this.cblack.push(tag);
26373             
26374         }, this);
26375         
26376         Roo.each(b, function(tag) {
26377             if (w.indexOf(tag) > -1) {
26378                 return;
26379             }
26380             if (this.cblack.indexOf(tag) > -1) {
26381                 return;
26382             }
26383             this.cblack.push(tag);
26384             
26385         }, this);
26386     },
26387     
26388     setStylesheets : function(stylesheets)
26389     {
26390         if(typeof(stylesheets) == 'string'){
26391             Roo.get(this.iframe.contentDocument.head).createChild({
26392                 tag : 'link',
26393                 rel : 'stylesheet',
26394                 type : 'text/css',
26395                 href : stylesheets
26396             });
26397             
26398             return;
26399         }
26400         var _this = this;
26401      
26402         Roo.each(stylesheets, function(s) {
26403             if(!s.length){
26404                 return;
26405             }
26406             
26407             Roo.get(_this.iframe.contentDocument.head).createChild({
26408                 tag : 'link',
26409                 rel : 'stylesheet',
26410                 type : 'text/css',
26411                 href : s
26412             });
26413         });
26414
26415         
26416     },
26417     
26418     
26419     updateLanguage : function()
26420     {
26421         if (!this.iframe || !this.iframe.contentDocument) {
26422             return;
26423         }
26424         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26425     },
26426     
26427     
26428     removeStylesheets : function()
26429     {
26430         var _this = this;
26431         
26432         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26433             s.remove();
26434         });
26435     },
26436     
26437     setStyle : function(style)
26438     {
26439         Roo.get(this.iframe.contentDocument.head).createChild({
26440             tag : 'style',
26441             type : 'text/css',
26442             html : style
26443         });
26444
26445         return;
26446     }
26447     
26448     // hide stuff that is not compatible
26449     /**
26450      * @event blur
26451      * @hide
26452      */
26453     /**
26454      * @event change
26455      * @hide
26456      */
26457     /**
26458      * @event focus
26459      * @hide
26460      */
26461     /**
26462      * @event specialkey
26463      * @hide
26464      */
26465     /**
26466      * @cfg {String} fieldClass @hide
26467      */
26468     /**
26469      * @cfg {String} focusClass @hide
26470      */
26471     /**
26472      * @cfg {String} autoCreate @hide
26473      */
26474     /**
26475      * @cfg {String} inputType @hide
26476      */
26477     /**
26478      * @cfg {String} invalidClass @hide
26479      */
26480     /**
26481      * @cfg {String} invalidText @hide
26482      */
26483     /**
26484      * @cfg {String} msgFx @hide
26485      */
26486     /**
26487      * @cfg {String} validateOnBlur @hide
26488      */
26489 });
26490
26491 Roo.HtmlEditorCore.white = [
26492         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26493         
26494        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26495        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26496        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26497        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26498        'TABLE',   'UL',         'XMP', 
26499        
26500        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26501       'THEAD',   'TR', 
26502      
26503       'DIR', 'MENU', 'OL', 'UL', 'DL',
26504        
26505       'EMBED',  'OBJECT'
26506 ];
26507
26508
26509 Roo.HtmlEditorCore.black = [
26510     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26511         'APPLET', // 
26512         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26513         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26514         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26515         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26516         //'FONT' // CLEAN LATER..
26517         'COLGROUP', 'COL'   // messy tables.
26518         
26519         
26520 ];
26521 Roo.HtmlEditorCore.clean = [ // ?? needed???
26522      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26523 ];
26524 Roo.HtmlEditorCore.tag_remove = [
26525     'FONT', 'TBODY'  
26526 ];
26527 // attributes..
26528
26529 Roo.HtmlEditorCore.ablack = [
26530     'on'
26531 ];
26532     
26533 Roo.HtmlEditorCore.aclean = [ 
26534     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26535 ];
26536
26537 // protocols..
26538 Roo.HtmlEditorCore.pwhite= [
26539         'http',  'https',  'mailto'
26540 ];
26541
26542 // white listed style attributes.
26543 Roo.HtmlEditorCore.cwhite= [
26544       //  'text-align', /// default is to allow most things..
26545       
26546          
26547 //        'font-size'//??
26548 ];
26549
26550 // black listed style attributes.
26551 Roo.HtmlEditorCore.cblack= [
26552       //  'font-size' -- this can be set by the project 
26553 ];
26554
26555
26556
26557
26558     //<script type="text/javascript">
26559
26560 /*
26561  * Ext JS Library 1.1.1
26562  * Copyright(c) 2006-2007, Ext JS, LLC.
26563  * Licence LGPL
26564  * 
26565  */
26566  
26567  
26568 Roo.form.HtmlEditor = function(config){
26569     
26570     
26571     
26572     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26573     
26574     if (!this.toolbars) {
26575         this.toolbars = [];
26576     }
26577     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26578     
26579     
26580 };
26581
26582 /**
26583  * @class Roo.form.HtmlEditor
26584  * @extends Roo.form.Field
26585  * Provides a lightweight HTML Editor component.
26586  *
26587  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26588  * 
26589  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26590  * supported by this editor.</b><br/><br/>
26591  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26592  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26593  */
26594 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26595     /**
26596      * @cfg {Boolean} clearUp
26597      */
26598     clearUp : true,
26599       /**
26600      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26601      */
26602     toolbars : false,
26603    
26604      /**
26605      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26606      *                        Roo.resizable.
26607      */
26608     resizable : false,
26609      /**
26610      * @cfg {Number} height (in pixels)
26611      */   
26612     height: 300,
26613    /**
26614      * @cfg {Number} width (in pixels)
26615      */   
26616     width: 500,
26617     
26618     /**
26619      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26620      * 
26621      */
26622     stylesheets: false,
26623     
26624     
26625      /**
26626      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26627      * 
26628      */
26629     cblack: false,
26630     /**
26631      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26632      * 
26633      */
26634     cwhite: false,
26635     
26636      /**
26637      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26638      * 
26639      */
26640     black: false,
26641     /**
26642      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26643      * 
26644      */
26645     white: false,
26646     /**
26647      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26648      */
26649     allowComments: false,
26650     /**
26651      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26652      */
26653     enableBlocks : true,
26654     
26655     /**
26656      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26657      *         if you are doing an email editor, this probably needs disabling, it's designed
26658      */
26659     autoClean: true,
26660     /**
26661      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
26662      */
26663     bodyCls : '',
26664     /**
26665      * @cfg {String} language default en - language of text (usefull for rtl languages)
26666      * 
26667      */
26668     language: 'en',
26669     
26670      
26671     // id of frame..
26672     frameId: false,
26673     
26674     // private properties
26675     validationEvent : false,
26676     deferHeight: true,
26677     initialized : false,
26678     activated : false,
26679     
26680     onFocus : Roo.emptyFn,
26681     iframePad:3,
26682     hideMode:'offsets',
26683     
26684     actionMode : 'container', // defaults to hiding it...
26685     
26686     defaultAutoCreate : { // modified by initCompnoent..
26687         tag: "textarea",
26688         style:"width:500px;height:300px;",
26689         autocomplete: "new-password"
26690     },
26691
26692     // private
26693     initComponent : function(){
26694         this.addEvents({
26695             /**
26696              * @event initialize
26697              * Fires when the editor is fully initialized (including the iframe)
26698              * @param {HtmlEditor} this
26699              */
26700             initialize: true,
26701             /**
26702              * @event activate
26703              * Fires when the editor is first receives the focus. Any insertion must wait
26704              * until after this event.
26705              * @param {HtmlEditor} this
26706              */
26707             activate: true,
26708              /**
26709              * @event beforesync
26710              * Fires before the textarea is updated with content from the editor iframe. Return false
26711              * to cancel the sync.
26712              * @param {HtmlEditor} this
26713              * @param {String} html
26714              */
26715             beforesync: true,
26716              /**
26717              * @event beforepush
26718              * Fires before the iframe editor is updated with content from the textarea. Return false
26719              * to cancel the push.
26720              * @param {HtmlEditor} this
26721              * @param {String} html
26722              */
26723             beforepush: true,
26724              /**
26725              * @event sync
26726              * Fires when the textarea is updated with content from the editor iframe.
26727              * @param {HtmlEditor} this
26728              * @param {String} html
26729              */
26730             sync: true,
26731              /**
26732              * @event push
26733              * Fires when the iframe editor is updated with content from the textarea.
26734              * @param {HtmlEditor} this
26735              * @param {String} html
26736              */
26737             push: true,
26738              /**
26739              * @event editmodechange
26740              * Fires when the editor switches edit modes
26741              * @param {HtmlEditor} this
26742              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26743              */
26744             editmodechange: true,
26745             /**
26746              * @event editorevent
26747              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26748              * @param {HtmlEditor} this
26749              */
26750             editorevent: true,
26751             /**
26752              * @event firstfocus
26753              * Fires when on first focus - needed by toolbars..
26754              * @param {HtmlEditor} this
26755              */
26756             firstfocus: true,
26757             /**
26758              * @event autosave
26759              * Auto save the htmlEditor value as a file into Events
26760              * @param {HtmlEditor} this
26761              */
26762             autosave: true,
26763             /**
26764              * @event savedpreview
26765              * preview the saved version of htmlEditor
26766              * @param {HtmlEditor} this
26767              */
26768             savedpreview: true,
26769             
26770             /**
26771             * @event stylesheetsclick
26772             * Fires when press the Sytlesheets button
26773             * @param {Roo.HtmlEditorCore} this
26774             */
26775             stylesheetsclick: true,
26776             /**
26777             * @event paste
26778             * Fires when press user pastes into the editor
26779             * @param {Roo.HtmlEditorCore} this
26780             */
26781             paste: true 
26782         });
26783         this.defaultAutoCreate =  {
26784             tag: "textarea",
26785             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26786             autocomplete: "new-password"
26787         };
26788     },
26789
26790     /**
26791      * Protected method that will not generally be called directly. It
26792      * is called when the editor creates its toolbar. Override this method if you need to
26793      * add custom toolbar buttons.
26794      * @param {HtmlEditor} editor
26795      */
26796     createToolbar : function(editor){
26797         Roo.log("create toolbars");
26798         if (!editor.toolbars || !editor.toolbars.length) {
26799             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26800         }
26801         
26802         for (var i =0 ; i < editor.toolbars.length;i++) {
26803             editor.toolbars[i] = Roo.factory(
26804                     typeof(editor.toolbars[i]) == 'string' ?
26805                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26806                 Roo.form.HtmlEditor);
26807             editor.toolbars[i].init(editor);
26808         }
26809          
26810         
26811     },
26812     /**
26813      * get the Context selected node
26814      * @returns {DomElement|boolean} selected node if active or false if none
26815      * 
26816      */
26817     getSelectedNode : function()
26818     {
26819         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26820             return false;
26821         }
26822         return this.toolbars[1].tb.selectedNode;
26823     
26824     },
26825     // private
26826     onRender : function(ct, position)
26827     {
26828         var _t = this;
26829         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26830         
26831         this.wrap = this.el.wrap({
26832             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26833         });
26834         
26835         this.editorcore.onRender(ct, position);
26836          
26837         if (this.resizable) {
26838             this.resizeEl = new Roo.Resizable(this.wrap, {
26839                 pinned : true,
26840                 wrap: true,
26841                 dynamic : true,
26842                 minHeight : this.height,
26843                 height: this.height,
26844                 handles : this.resizable,
26845                 width: this.width,
26846                 listeners : {
26847                     resize : function(r, w, h) {
26848                         _t.onResize(w,h); // -something
26849                     }
26850                 }
26851             });
26852             
26853         }
26854         this.createToolbar(this);
26855        
26856         
26857         if(!this.width){
26858             this.setSize(this.wrap.getSize());
26859         }
26860         if (this.resizeEl) {
26861             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26862             // should trigger onReize..
26863         }
26864         
26865         this.keyNav = new Roo.KeyNav(this.el, {
26866             
26867             "tab" : function(e){
26868                 e.preventDefault();
26869                 
26870                 var value = this.getValue();
26871                 
26872                 var start = this.el.dom.selectionStart;
26873                 var end = this.el.dom.selectionEnd;
26874                 
26875                 if(!e.shiftKey){
26876                     
26877                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26878                     this.el.dom.setSelectionRange(end + 1, end + 1);
26879                     return;
26880                 }
26881                 
26882                 var f = value.substring(0, start).split("\t");
26883                 
26884                 if(f.pop().length != 0){
26885                     return;
26886                 }
26887                 
26888                 this.setValue(f.join("\t") + value.substring(end));
26889                 this.el.dom.setSelectionRange(start - 1, start - 1);
26890                 
26891             },
26892             
26893             "home" : function(e){
26894                 e.preventDefault();
26895                 
26896                 var curr = this.el.dom.selectionStart;
26897                 var lines = this.getValue().split("\n");
26898                 
26899                 if(!lines.length){
26900                     return;
26901                 }
26902                 
26903                 if(e.ctrlKey){
26904                     this.el.dom.setSelectionRange(0, 0);
26905                     return;
26906                 }
26907                 
26908                 var pos = 0;
26909                 
26910                 for (var i = 0; i < lines.length;i++) {
26911                     pos += lines[i].length;
26912                     
26913                     if(i != 0){
26914                         pos += 1;
26915                     }
26916                     
26917                     if(pos < curr){
26918                         continue;
26919                     }
26920                     
26921                     pos -= lines[i].length;
26922                     
26923                     break;
26924                 }
26925                 
26926                 if(!e.shiftKey){
26927                     this.el.dom.setSelectionRange(pos, pos);
26928                     return;
26929                 }
26930                 
26931                 this.el.dom.selectionStart = pos;
26932                 this.el.dom.selectionEnd = curr;
26933             },
26934             
26935             "end" : function(e){
26936                 e.preventDefault();
26937                 
26938                 var curr = this.el.dom.selectionStart;
26939                 var lines = this.getValue().split("\n");
26940                 
26941                 if(!lines.length){
26942                     return;
26943                 }
26944                 
26945                 if(e.ctrlKey){
26946                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26947                     return;
26948                 }
26949                 
26950                 var pos = 0;
26951                 
26952                 for (var i = 0; i < lines.length;i++) {
26953                     
26954                     pos += lines[i].length;
26955                     
26956                     if(i != 0){
26957                         pos += 1;
26958                     }
26959                     
26960                     if(pos < curr){
26961                         continue;
26962                     }
26963                     
26964                     break;
26965                 }
26966                 
26967                 if(!e.shiftKey){
26968                     this.el.dom.setSelectionRange(pos, pos);
26969                     return;
26970                 }
26971                 
26972                 this.el.dom.selectionStart = curr;
26973                 this.el.dom.selectionEnd = pos;
26974             },
26975
26976             scope : this,
26977
26978             doRelay : function(foo, bar, hname){
26979                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26980             },
26981
26982             forceKeyDown: true
26983         });
26984         
26985 //        if(this.autosave && this.w){
26986 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26987 //        }
26988     },
26989
26990     // private
26991     onResize : function(w, h)
26992     {
26993         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26994         var ew = false;
26995         var eh = false;
26996         
26997         if(this.el ){
26998             if(typeof w == 'number'){
26999                 var aw = w - this.wrap.getFrameWidth('lr');
27000                 this.el.setWidth(this.adjustWidth('textarea', aw));
27001                 ew = aw;
27002             }
27003             if(typeof h == 'number'){
27004                 var tbh = 0;
27005                 for (var i =0; i < this.toolbars.length;i++) {
27006                     // fixme - ask toolbars for heights?
27007                     tbh += this.toolbars[i].tb.el.getHeight();
27008                     if (this.toolbars[i].footer) {
27009                         tbh += this.toolbars[i].footer.el.getHeight();
27010                     }
27011                 }
27012                 
27013                 
27014                 
27015                 
27016                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27017                 ah -= 5; // knock a few pixes off for look..
27018 //                Roo.log(ah);
27019                 this.el.setHeight(this.adjustWidth('textarea', ah));
27020                 var eh = ah;
27021             }
27022         }
27023         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27024         this.editorcore.onResize(ew,eh);
27025         
27026     },
27027
27028     /**
27029      * Toggles the editor between standard and source edit mode.
27030      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27031      */
27032     toggleSourceEdit : function(sourceEditMode)
27033     {
27034         this.editorcore.toggleSourceEdit(sourceEditMode);
27035         
27036         if(this.editorcore.sourceEditMode){
27037             Roo.log('editor - showing textarea');
27038             
27039 //            Roo.log('in');
27040 //            Roo.log(this.syncValue());
27041             this.editorcore.syncValue();
27042             this.el.removeClass('x-hidden');
27043             this.el.dom.removeAttribute('tabIndex');
27044             this.el.focus();
27045             this.el.dom.scrollTop = 0;
27046             
27047             
27048             for (var i = 0; i < this.toolbars.length; i++) {
27049                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27050                     this.toolbars[i].tb.hide();
27051                     this.toolbars[i].footer.hide();
27052                 }
27053             }
27054             
27055         }else{
27056             Roo.log('editor - hiding textarea');
27057 //            Roo.log('out')
27058 //            Roo.log(this.pushValue()); 
27059             this.editorcore.pushValue();
27060             
27061             this.el.addClass('x-hidden');
27062             this.el.dom.setAttribute('tabIndex', -1);
27063             
27064             for (var i = 0; i < this.toolbars.length; i++) {
27065                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27066                     this.toolbars[i].tb.show();
27067                     this.toolbars[i].footer.show();
27068                 }
27069             }
27070             
27071             //this.deferFocus();
27072         }
27073         
27074         this.setSize(this.wrap.getSize());
27075         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27076         
27077         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27078     },
27079  
27080     // private (for BoxComponent)
27081     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27082
27083     // private (for BoxComponent)
27084     getResizeEl : function(){
27085         return this.wrap;
27086     },
27087
27088     // private (for BoxComponent)
27089     getPositionEl : function(){
27090         return this.wrap;
27091     },
27092
27093     // private
27094     initEvents : function(){
27095         this.originalValue = this.getValue();
27096     },
27097
27098     /**
27099      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27100      * @method
27101      */
27102     markInvalid : Roo.emptyFn,
27103     /**
27104      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27105      * @method
27106      */
27107     clearInvalid : Roo.emptyFn,
27108
27109     setValue : function(v){
27110         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27111         this.editorcore.pushValue();
27112     },
27113
27114     /**
27115      * update the language in the body - really done by core
27116      * @param {String} language - eg. en / ar / zh-CN etc..
27117      */
27118     updateLanguage : function(lang)
27119     {
27120         this.language = lang;
27121         this.editorcore.language = lang;
27122         this.editorcore.updateLanguage();
27123      
27124     },
27125     // private
27126     deferFocus : function(){
27127         this.focus.defer(10, this);
27128     },
27129
27130     // doc'ed in Field
27131     focus : function(){
27132         this.editorcore.focus();
27133         
27134     },
27135       
27136
27137     // private
27138     onDestroy : function(){
27139         
27140         
27141         
27142         if(this.rendered){
27143             
27144             for (var i =0; i < this.toolbars.length;i++) {
27145                 // fixme - ask toolbars for heights?
27146                 this.toolbars[i].onDestroy();
27147             }
27148             
27149             this.wrap.dom.innerHTML = '';
27150             this.wrap.remove();
27151         }
27152     },
27153
27154     // private
27155     onFirstFocus : function(){
27156         //Roo.log("onFirstFocus");
27157         this.editorcore.onFirstFocus();
27158          for (var i =0; i < this.toolbars.length;i++) {
27159             this.toolbars[i].onFirstFocus();
27160         }
27161         
27162     },
27163     
27164     // private
27165     syncValue : function()
27166     {
27167         this.editorcore.syncValue();
27168     },
27169     
27170     pushValue : function()
27171     {
27172         this.editorcore.pushValue();
27173     },
27174     
27175     setStylesheets : function(stylesheets)
27176     {
27177         this.editorcore.setStylesheets(stylesheets);
27178     },
27179     
27180     removeStylesheets : function()
27181     {
27182         this.editorcore.removeStylesheets();
27183     }
27184      
27185     
27186     // hide stuff that is not compatible
27187     /**
27188      * @event blur
27189      * @hide
27190      */
27191     /**
27192      * @event change
27193      * @hide
27194      */
27195     /**
27196      * @event focus
27197      * @hide
27198      */
27199     /**
27200      * @event specialkey
27201      * @hide
27202      */
27203     /**
27204      * @cfg {String} fieldClass @hide
27205      */
27206     /**
27207      * @cfg {String} focusClass @hide
27208      */
27209     /**
27210      * @cfg {String} autoCreate @hide
27211      */
27212     /**
27213      * @cfg {String} inputType @hide
27214      */
27215     /**
27216      * @cfg {String} invalidClass @hide
27217      */
27218     /**
27219      * @cfg {String} invalidText @hide
27220      */
27221     /**
27222      * @cfg {String} msgFx @hide
27223      */
27224     /**
27225      * @cfg {String} validateOnBlur @hide
27226      */
27227 });
27228  
27229     /*
27230  * Based on
27231  * Ext JS Library 1.1.1
27232  * Copyright(c) 2006-2007, Ext JS, LLC.
27233  *  
27234  
27235  */
27236
27237 /**
27238  * @class Roo.form.HtmlEditor.ToolbarStandard
27239  * Basic Toolbar
27240
27241  * Usage:
27242  *
27243  new Roo.form.HtmlEditor({
27244     ....
27245     toolbars : [
27246         new Roo.form.HtmlEditorToolbar1({
27247             disable : { fonts: 1 , format: 1, ..., ... , ...],
27248             btns : [ .... ]
27249         })
27250     }
27251      
27252  * 
27253  * @cfg {Object} disable List of elements to disable..
27254  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27255  * 
27256  * 
27257  * NEEDS Extra CSS? 
27258  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27259  */
27260  
27261 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27262 {
27263     
27264     Roo.apply(this, config);
27265     
27266     // default disabled, based on 'good practice'..
27267     this.disable = this.disable || {};
27268     Roo.applyIf(this.disable, {
27269         fontSize : true,
27270         colors : true,
27271         specialElements : true
27272     });
27273     
27274     
27275     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27276     // dont call parent... till later.
27277 }
27278
27279 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27280     
27281     tb: false,
27282     
27283     rendered: false,
27284     
27285     editor : false,
27286     editorcore : false,
27287     /**
27288      * @cfg {Object} disable  List of toolbar elements to disable
27289          
27290      */
27291     disable : false,
27292     
27293     
27294      /**
27295      * @cfg {String} createLinkText The default text for the create link prompt
27296      */
27297     createLinkText : 'Please enter the URL for the link:',
27298     /**
27299      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27300      */
27301     defaultLinkValue : 'http:/'+'/',
27302    
27303     
27304       /**
27305      * @cfg {Array} fontFamilies An array of available font families
27306      */
27307     fontFamilies : [
27308         'Arial',
27309         'Courier New',
27310         'Tahoma',
27311         'Times New Roman',
27312         'Verdana'
27313     ],
27314     
27315     specialChars : [
27316            "&#169;",
27317           "&#174;",     
27318           "&#8482;",    
27319           "&#163;" ,    
27320          // "&#8212;",    
27321           "&#8230;",    
27322           "&#247;" ,    
27323         //  "&#225;" ,     ?? a acute?
27324            "&#8364;"    , //Euro
27325        //   "&#8220;"    ,
27326         //  "&#8221;"    ,
27327         //  "&#8226;"    ,
27328           "&#176;"  //   , // degrees
27329
27330          // "&#233;"     , // e ecute
27331          // "&#250;"     , // u ecute?
27332     ],
27333     
27334     specialElements : [
27335         {
27336             text: "Insert Table",
27337             xtype: 'MenuItem',
27338             xns : Roo.Menu,
27339             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27340                 
27341         },
27342         {    
27343             text: "Insert Image",
27344             xtype: 'MenuItem',
27345             xns : Roo.Menu,
27346             ihtml : '<img src="about:blank"/>'
27347             
27348         }
27349         
27350          
27351     ],
27352     
27353     
27354     inputElements : [ 
27355             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27356             "input:submit", "input:button", "select", "textarea", "label" ],
27357     formats : [
27358         ["p"] ,  
27359         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27360         ["pre"],[ "code"], 
27361         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27362         ['div'],['span'],
27363         ['sup'],['sub']
27364     ],
27365     
27366     cleanStyles : [
27367         "font-size"
27368     ],
27369      /**
27370      * @cfg {String} defaultFont default font to use.
27371      */
27372     defaultFont: 'tahoma',
27373    
27374     fontSelect : false,
27375     
27376     
27377     formatCombo : false,
27378     
27379     init : function(editor)
27380     {
27381         this.editor = editor;
27382         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27383         var editorcore = this.editorcore;
27384         
27385         var _t = this;
27386         
27387         var fid = editorcore.frameId;
27388         var etb = this;
27389         function btn(id, toggle, handler){
27390             var xid = fid + '-'+ id ;
27391             return {
27392                 id : xid,
27393                 cmd : id,
27394                 cls : 'x-btn-icon x-edit-'+id,
27395                 enableToggle:toggle !== false,
27396                 scope: _t, // was editor...
27397                 handler:handler||_t.relayBtnCmd,
27398                 clickEvent:'mousedown',
27399                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27400                 tabIndex:-1
27401             };
27402         }
27403         
27404         
27405         
27406         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27407         this.tb = tb;
27408          // stop form submits
27409         tb.el.on('click', function(e){
27410             e.preventDefault(); // what does this do?
27411         });
27412
27413         if(!this.disable.font) { // && !Roo.isSafari){
27414             /* why no safari for fonts 
27415             editor.fontSelect = tb.el.createChild({
27416                 tag:'select',
27417                 tabIndex: -1,
27418                 cls:'x-font-select',
27419                 html: this.createFontOptions()
27420             });
27421             
27422             editor.fontSelect.on('change', function(){
27423                 var font = editor.fontSelect.dom.value;
27424                 editor.relayCmd('fontname', font);
27425                 editor.deferFocus();
27426             }, editor);
27427             
27428             tb.add(
27429                 editor.fontSelect.dom,
27430                 '-'
27431             );
27432             */
27433             
27434         };
27435         if(!this.disable.formats){
27436             this.formatCombo = new Roo.form.ComboBox({
27437                 store: new Roo.data.SimpleStore({
27438                     id : 'tag',
27439                     fields: ['tag'],
27440                     data : this.formats // from states.js
27441                 }),
27442                 blockFocus : true,
27443                 name : '',
27444                 //autoCreate : {tag: "div",  size: "20"},
27445                 displayField:'tag',
27446                 typeAhead: false,
27447                 mode: 'local',
27448                 editable : false,
27449                 triggerAction: 'all',
27450                 emptyText:'Add tag',
27451                 selectOnFocus:true,
27452                 width:135,
27453                 listeners : {
27454                     'select': function(c, r, i) {
27455                         editorcore.insertTag(r.get('tag'));
27456                         editor.focus();
27457                     }
27458                 }
27459
27460             });
27461             tb.addField(this.formatCombo);
27462             
27463         }
27464         
27465         if(!this.disable.format){
27466             tb.add(
27467                 btn('bold'),
27468                 btn('italic'),
27469                 btn('underline'),
27470                 btn('strikethrough')
27471             );
27472         };
27473         if(!this.disable.fontSize){
27474             tb.add(
27475                 '-',
27476                 
27477                 
27478                 btn('increasefontsize', false, editorcore.adjustFont),
27479                 btn('decreasefontsize', false, editorcore.adjustFont)
27480             );
27481         };
27482         
27483         
27484         if(!this.disable.colors){
27485             tb.add(
27486                 '-', {
27487                     id:editorcore.frameId +'-forecolor',
27488                     cls:'x-btn-icon x-edit-forecolor',
27489                     clickEvent:'mousedown',
27490                     tooltip: this.buttonTips['forecolor'] || undefined,
27491                     tabIndex:-1,
27492                     menu : new Roo.menu.ColorMenu({
27493                         allowReselect: true,
27494                         focus: Roo.emptyFn,
27495                         value:'000000',
27496                         plain:true,
27497                         selectHandler: function(cp, color){
27498                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27499                             editor.deferFocus();
27500                         },
27501                         scope: editorcore,
27502                         clickEvent:'mousedown'
27503                     })
27504                 }, {
27505                     id:editorcore.frameId +'backcolor',
27506                     cls:'x-btn-icon x-edit-backcolor',
27507                     clickEvent:'mousedown',
27508                     tooltip: this.buttonTips['backcolor'] || undefined,
27509                     tabIndex:-1,
27510                     menu : new Roo.menu.ColorMenu({
27511                         focus: Roo.emptyFn,
27512                         value:'FFFFFF',
27513                         plain:true,
27514                         allowReselect: true,
27515                         selectHandler: function(cp, color){
27516                             if(Roo.isGecko){
27517                                 editorcore.execCmd('useCSS', false);
27518                                 editorcore.execCmd('hilitecolor', color);
27519                                 editorcore.execCmd('useCSS', true);
27520                                 editor.deferFocus();
27521                             }else{
27522                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27523                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27524                                 editor.deferFocus();
27525                             }
27526                         },
27527                         scope:editorcore,
27528                         clickEvent:'mousedown'
27529                     })
27530                 }
27531             );
27532         };
27533         // now add all the items...
27534         
27535
27536         if(!this.disable.alignments){
27537             tb.add(
27538                 '-',
27539                 btn('justifyleft'),
27540                 btn('justifycenter'),
27541                 btn('justifyright')
27542             );
27543         };
27544
27545         //if(!Roo.isSafari){
27546             if(!this.disable.links){
27547                 tb.add(
27548                     '-',
27549                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27550                 );
27551             };
27552
27553             if(!this.disable.lists){
27554                 tb.add(
27555                     '-',
27556                     btn('insertorderedlist'),
27557                     btn('insertunorderedlist')
27558                 );
27559             }
27560             if(!this.disable.sourceEdit){
27561                 tb.add(
27562                     '-',
27563                     btn('sourceedit', true, function(btn){
27564                         this.toggleSourceEdit(btn.pressed);
27565                     })
27566                 );
27567             }
27568         //}
27569         
27570         var smenu = { };
27571         // special menu.. - needs to be tidied up..
27572         if (!this.disable.special) {
27573             smenu = {
27574                 text: "&#169;",
27575                 cls: 'x-edit-none',
27576                 
27577                 menu : {
27578                     items : []
27579                 }
27580             };
27581             for (var i =0; i < this.specialChars.length; i++) {
27582                 smenu.menu.items.push({
27583                     
27584                     html: this.specialChars[i],
27585                     handler: function(a,b) {
27586                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27587                         //editor.insertAtCursor(a.html);
27588                         
27589                     },
27590                     tabIndex:-1
27591                 });
27592             }
27593             
27594             
27595             tb.add(smenu);
27596             
27597             
27598         }
27599         
27600         var cmenu = { };
27601         if (!this.disable.cleanStyles) {
27602             cmenu = {
27603                 cls: 'x-btn-icon x-btn-clear',
27604                 
27605                 menu : {
27606                     items : []
27607                 }
27608             };
27609             for (var i =0; i < this.cleanStyles.length; i++) {
27610                 cmenu.menu.items.push({
27611                     actiontype : this.cleanStyles[i],
27612                     html: 'Remove ' + this.cleanStyles[i],
27613                     handler: function(a,b) {
27614 //                        Roo.log(a);
27615 //                        Roo.log(b);
27616                         var c = Roo.get(editorcore.doc.body);
27617                         c.select('[style]').each(function(s) {
27618                             s.dom.style.removeProperty(a.actiontype);
27619                         });
27620                         editorcore.syncValue();
27621                     },
27622                     tabIndex:-1
27623                 });
27624             }
27625             cmenu.menu.items.push({
27626                 actiontype : 'tablewidths',
27627                 html: 'Remove Table Widths',
27628                 handler: function(a,b) {
27629                     editorcore.cleanTableWidths();
27630                     editorcore.syncValue();
27631                 },
27632                 tabIndex:-1
27633             });
27634             cmenu.menu.items.push({
27635                 actiontype : 'word',
27636                 html: 'Remove MS Word Formating',
27637                 handler: function(a,b) {
27638                     editorcore.cleanWord();
27639                     editorcore.syncValue();
27640                 },
27641                 tabIndex:-1
27642             });
27643             
27644             cmenu.menu.items.push({
27645                 actiontype : 'all',
27646                 html: 'Remove All Styles',
27647                 handler: function(a,b) {
27648                     
27649                     var c = Roo.get(editorcore.doc.body);
27650                     c.select('[style]').each(function(s) {
27651                         s.dom.removeAttribute('style');
27652                     });
27653                     editorcore.syncValue();
27654                 },
27655                 tabIndex:-1
27656             });
27657             
27658             cmenu.menu.items.push({
27659                 actiontype : 'all',
27660                 html: 'Remove All CSS Classes',
27661                 handler: function(a,b) {
27662                     
27663                     var c = Roo.get(editorcore.doc.body);
27664                     c.select('[class]').each(function(s) {
27665                         s.dom.removeAttribute('class');
27666                     });
27667                     editorcore.cleanWord();
27668                     editorcore.syncValue();
27669                 },
27670                 tabIndex:-1
27671             });
27672             
27673              cmenu.menu.items.push({
27674                 actiontype : 'tidy',
27675                 html: 'Tidy HTML Source',
27676                 handler: function(a,b) {
27677                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27678                     editorcore.syncValue();
27679                 },
27680                 tabIndex:-1
27681             });
27682             
27683             
27684             tb.add(cmenu);
27685         }
27686          
27687         if (!this.disable.specialElements) {
27688             var semenu = {
27689                 text: "Other;",
27690                 cls: 'x-edit-none',
27691                 menu : {
27692                     items : []
27693                 }
27694             };
27695             for (var i =0; i < this.specialElements.length; i++) {
27696                 semenu.menu.items.push(
27697                     Roo.apply({ 
27698                         handler: function(a,b) {
27699                             editor.insertAtCursor(this.ihtml);
27700                         }
27701                     }, this.specialElements[i])
27702                 );
27703                     
27704             }
27705             
27706             tb.add(semenu);
27707             
27708             
27709         }
27710          
27711         
27712         if (this.btns) {
27713             for(var i =0; i< this.btns.length;i++) {
27714                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27715                 b.cls =  'x-edit-none';
27716                 
27717                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27718                     b.cls += ' x-init-enable';
27719                 }
27720                 
27721                 b.scope = editorcore;
27722                 tb.add(b);
27723             }
27724         
27725         }
27726         
27727         
27728         
27729         // disable everything...
27730         
27731         this.tb.items.each(function(item){
27732             
27733            if(
27734                 item.id != editorcore.frameId+ '-sourceedit' && 
27735                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27736             ){
27737                 
27738                 item.disable();
27739             }
27740         });
27741         this.rendered = true;
27742         
27743         // the all the btns;
27744         editor.on('editorevent', this.updateToolbar, this);
27745         // other toolbars need to implement this..
27746         //editor.on('editmodechange', this.updateToolbar, this);
27747     },
27748     
27749     
27750     relayBtnCmd : function(btn) {
27751         this.editorcore.relayCmd(btn.cmd);
27752     },
27753     // private used internally
27754     createLink : function(){
27755         //Roo.log("create link?");
27756         var ec = this.editorcore;
27757         var ar = ec.getAllAncestors();
27758         var n = false;
27759         for(var i = 0;i< ar.length;i++) {
27760             if (ar[i] && ar[i].nodeName == 'A') {
27761                 n = ar[i];
27762                 break;
27763             }
27764         }
27765         
27766         (function() {
27767             
27768             Roo.MessageBox.show({
27769                 title : "Add / Edit Link URL",
27770                 msg : "Enter the url for the link",
27771                 buttons: Roo.MessageBox.OKCANCEL,
27772                 fn: function(btn, url){
27773                     if (btn != 'ok') {
27774                         return;
27775                     }
27776                     if(url && url != 'http:/'+'/'){
27777                         if (n) {
27778                             n.setAttribute('href', url);
27779                         } else {
27780                             ec.relayCmd('createlink', url);
27781                         }
27782                     }
27783                 },
27784                 minWidth:250,
27785                 prompt:true,
27786                 //multiline: multiline,
27787                 modal : true,
27788                 value :  n  ? n.getAttribute('href') : '' 
27789             });
27790             
27791              
27792         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27793         
27794     },
27795
27796     
27797     /**
27798      * Protected method that will not generally be called directly. It triggers
27799      * a toolbar update by reading the markup state of the current selection in the editor.
27800      */
27801     updateToolbar: function(){
27802
27803         if(!this.editorcore.activated){
27804             this.editor.onFirstFocus();
27805             return;
27806         }
27807
27808         var btns = this.tb.items.map, 
27809             doc = this.editorcore.doc,
27810             frameId = this.editorcore.frameId;
27811
27812         if(!this.disable.font && !Roo.isSafari){
27813             /*
27814             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27815             if(name != this.fontSelect.dom.value){
27816                 this.fontSelect.dom.value = name;
27817             }
27818             */
27819         }
27820         if(!this.disable.format){
27821             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27822             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27823             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27824             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27825         }
27826         if(!this.disable.alignments){
27827             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27828             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27829             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27830         }
27831         if(!Roo.isSafari && !this.disable.lists){
27832             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27833             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27834         }
27835         
27836         var ans = this.editorcore.getAllAncestors();
27837         if (this.formatCombo) {
27838             
27839             
27840             var store = this.formatCombo.store;
27841             this.formatCombo.setValue("");
27842             for (var i =0; i < ans.length;i++) {
27843                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27844                     // select it..
27845                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27846                     break;
27847                 }
27848             }
27849         }
27850         
27851         
27852         
27853         // hides menus... - so this cant be on a menu...
27854         Roo.menu.MenuMgr.hideAll();
27855
27856         //this.editorsyncValue();
27857     },
27858    
27859     
27860     createFontOptions : function(){
27861         var buf = [], fs = this.fontFamilies, ff, lc;
27862         
27863         
27864         
27865         for(var i = 0, len = fs.length; i< len; i++){
27866             ff = fs[i];
27867             lc = ff.toLowerCase();
27868             buf.push(
27869                 '<option value="',lc,'" style="font-family:',ff,';"',
27870                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27871                     ff,
27872                 '</option>'
27873             );
27874         }
27875         return buf.join('');
27876     },
27877     
27878     toggleSourceEdit : function(sourceEditMode){
27879         
27880         Roo.log("toolbar toogle");
27881         if(sourceEditMode === undefined){
27882             sourceEditMode = !this.sourceEditMode;
27883         }
27884         this.sourceEditMode = sourceEditMode === true;
27885         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27886         // just toggle the button?
27887         if(btn.pressed !== this.sourceEditMode){
27888             btn.toggle(this.sourceEditMode);
27889             return;
27890         }
27891         
27892         if(sourceEditMode){
27893             Roo.log("disabling buttons");
27894             this.tb.items.each(function(item){
27895                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27896                     item.disable();
27897                 }
27898             });
27899           
27900         }else{
27901             Roo.log("enabling buttons");
27902             if(this.editorcore.initialized){
27903                 this.tb.items.each(function(item){
27904                     item.enable();
27905                 });
27906                 // initialize 'blocks'
27907                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27908                     Roo.htmleditor.Block.factory(e).updateElement(e);
27909                 },this);
27910             
27911             }
27912             
27913         }
27914         Roo.log("calling toggole on editor");
27915         // tell the editor that it's been pressed..
27916         this.editor.toggleSourceEdit(sourceEditMode);
27917        
27918     },
27919      /**
27920      * Object collection of toolbar tooltips for the buttons in the editor. The key
27921      * is the command id associated with that button and the value is a valid QuickTips object.
27922      * For example:
27923 <pre><code>
27924 {
27925     bold : {
27926         title: 'Bold (Ctrl+B)',
27927         text: 'Make the selected text bold.',
27928         cls: 'x-html-editor-tip'
27929     },
27930     italic : {
27931         title: 'Italic (Ctrl+I)',
27932         text: 'Make the selected text italic.',
27933         cls: 'x-html-editor-tip'
27934     },
27935     ...
27936 </code></pre>
27937     * @type Object
27938      */
27939     buttonTips : {
27940         bold : {
27941             title: 'Bold (Ctrl+B)',
27942             text: 'Make the selected text bold.',
27943             cls: 'x-html-editor-tip'
27944         },
27945         italic : {
27946             title: 'Italic (Ctrl+I)',
27947             text: 'Make the selected text italic.',
27948             cls: 'x-html-editor-tip'
27949         },
27950         underline : {
27951             title: 'Underline (Ctrl+U)',
27952             text: 'Underline the selected text.',
27953             cls: 'x-html-editor-tip'
27954         },
27955         strikethrough : {
27956             title: 'Strikethrough',
27957             text: 'Strikethrough the selected text.',
27958             cls: 'x-html-editor-tip'
27959         },
27960         increasefontsize : {
27961             title: 'Grow Text',
27962             text: 'Increase the font size.',
27963             cls: 'x-html-editor-tip'
27964         },
27965         decreasefontsize : {
27966             title: 'Shrink Text',
27967             text: 'Decrease the font size.',
27968             cls: 'x-html-editor-tip'
27969         },
27970         backcolor : {
27971             title: 'Text Highlight Color',
27972             text: 'Change the background color of the selected text.',
27973             cls: 'x-html-editor-tip'
27974         },
27975         forecolor : {
27976             title: 'Font Color',
27977             text: 'Change the color of the selected text.',
27978             cls: 'x-html-editor-tip'
27979         },
27980         justifyleft : {
27981             title: 'Align Text Left',
27982             text: 'Align text to the left.',
27983             cls: 'x-html-editor-tip'
27984         },
27985         justifycenter : {
27986             title: 'Center Text',
27987             text: 'Center text in the editor.',
27988             cls: 'x-html-editor-tip'
27989         },
27990         justifyright : {
27991             title: 'Align Text Right',
27992             text: 'Align text to the right.',
27993             cls: 'x-html-editor-tip'
27994         },
27995         insertunorderedlist : {
27996             title: 'Bullet List',
27997             text: 'Start a bulleted list.',
27998             cls: 'x-html-editor-tip'
27999         },
28000         insertorderedlist : {
28001             title: 'Numbered List',
28002             text: 'Start a numbered list.',
28003             cls: 'x-html-editor-tip'
28004         },
28005         createlink : {
28006             title: 'Hyperlink',
28007             text: 'Make the selected text a hyperlink.',
28008             cls: 'x-html-editor-tip'
28009         },
28010         sourceedit : {
28011             title: 'Source Edit',
28012             text: 'Switch to source editing mode.',
28013             cls: 'x-html-editor-tip'
28014         }
28015     },
28016     // private
28017     onDestroy : function(){
28018         if(this.rendered){
28019             
28020             this.tb.items.each(function(item){
28021                 if(item.menu){
28022                     item.menu.removeAll();
28023                     if(item.menu.el){
28024                         item.menu.el.destroy();
28025                     }
28026                 }
28027                 item.destroy();
28028             });
28029              
28030         }
28031     },
28032     onFirstFocus: function() {
28033         this.tb.items.each(function(item){
28034            item.enable();
28035         });
28036     }
28037 };
28038
28039
28040
28041
28042 // <script type="text/javascript">
28043 /*
28044  * Based on
28045  * Ext JS Library 1.1.1
28046  * Copyright(c) 2006-2007, Ext JS, LLC.
28047  *  
28048  
28049  */
28050
28051  
28052 /**
28053  * @class Roo.form.HtmlEditor.ToolbarContext
28054  * Context Toolbar
28055  * 
28056  * Usage:
28057  *
28058  new Roo.form.HtmlEditor({
28059     ....
28060     toolbars : [
28061         { xtype: 'ToolbarStandard', styles : {} }
28062         { xtype: 'ToolbarContext', disable : {} }
28063     ]
28064 })
28065
28066      
28067  * 
28068  * @config : {Object} disable List of elements to disable.. (not done yet.)
28069  * @config : {Object} styles  Map of styles available.
28070  * 
28071  */
28072
28073 Roo.form.HtmlEditor.ToolbarContext = function(config)
28074 {
28075     
28076     Roo.apply(this, config);
28077     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28078     // dont call parent... till later.
28079     this.styles = this.styles || {};
28080 }
28081
28082  
28083
28084 Roo.form.HtmlEditor.ToolbarContext.types = {
28085     'IMG' : [
28086         {
28087             name : 'width',
28088             title: "Width",
28089             width: 40
28090         },
28091         {
28092             name : 'height',
28093             title: "Height",
28094             width: 40
28095         },
28096         {
28097             name : 'align',
28098             title: "Align",
28099             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28100             width : 80
28101             
28102         },
28103         {
28104             name : 'border',
28105             title: "Border",
28106             width: 40
28107         },
28108         {
28109             name : 'alt',
28110             title: "Alt",
28111             width: 120
28112         },
28113         {
28114             name : 'src',
28115             title: "Src",
28116             width: 220
28117         }
28118         
28119     ],
28120     
28121     'FIGURE' : [
28122         {
28123             name : 'align',
28124             title: "Align",
28125             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28126             width : 80  
28127         }
28128     ],
28129     'A' : [
28130         {
28131             name : 'name',
28132             title: "Name",
28133             width: 50
28134         },
28135         {
28136             name : 'target',
28137             title: "Target",
28138             width: 120
28139         },
28140         {
28141             name : 'href',
28142             title: "Href",
28143             width: 220
28144         } // border?
28145         
28146     ],
28147     
28148     'INPUT' : [
28149         {
28150             name : 'name',
28151             title: "name",
28152             width: 120
28153         },
28154         {
28155             name : 'value',
28156             title: "Value",
28157             width: 120
28158         },
28159         {
28160             name : 'width',
28161             title: "Width",
28162             width: 40
28163         }
28164     ],
28165     'LABEL' : [
28166          {
28167             name : 'for',
28168             title: "For",
28169             width: 120
28170         }
28171     ],
28172     'TEXTAREA' : [
28173         {
28174             name : 'name',
28175             title: "name",
28176             width: 120
28177         },
28178         {
28179             name : 'rows',
28180             title: "Rows",
28181             width: 20
28182         },
28183         {
28184             name : 'cols',
28185             title: "Cols",
28186             width: 20
28187         }
28188     ],
28189     'SELECT' : [
28190         {
28191             name : 'name',
28192             title: "name",
28193             width: 120
28194         },
28195         {
28196             name : 'selectoptions',
28197             title: "Options",
28198             width: 200
28199         }
28200     ],
28201     
28202     // should we really allow this??
28203     // should this just be 
28204     'BODY' : [
28205         
28206         {
28207             name : 'title',
28208             title: "Title",
28209             width: 200,
28210             disabled : true
28211         }
28212     ],
28213  
28214     '*' : [
28215         // empty.
28216     ]
28217
28218 };
28219
28220 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28221 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28222
28223 Roo.form.HtmlEditor.ToolbarContext.options = {
28224         'font-family'  : [ 
28225                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28226                 [ 'Courier New', 'Courier New'],
28227                 [ 'Tahoma', 'Tahoma'],
28228                 [ 'Times New Roman,serif', 'Times'],
28229                 [ 'Verdana','Verdana' ]
28230         ]
28231 };
28232
28233 // fixme - these need to be configurable..
28234  
28235
28236 //Roo.form.HtmlEditor.ToolbarContext.types
28237
28238
28239 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28240     
28241     tb: false,
28242     
28243     rendered: false,
28244     
28245     editor : false,
28246     editorcore : false,
28247     /**
28248      * @cfg {Object} disable  List of toolbar elements to disable
28249          
28250      */
28251     disable : false,
28252     /**
28253      * @cfg {Object} styles List of styles 
28254      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28255      *
28256      * These must be defined in the page, so they get rendered correctly..
28257      * .headline { }
28258      * TD.underline { }
28259      * 
28260      */
28261     styles : false,
28262     
28263     options: false,
28264     
28265     toolbars : false,
28266     
28267     init : function(editor)
28268     {
28269         this.editor = editor;
28270         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28271         var editorcore = this.editorcore;
28272         
28273         var fid = editorcore.frameId;
28274         var etb = this;
28275         function btn(id, toggle, handler){
28276             var xid = fid + '-'+ id ;
28277             return {
28278                 id : xid,
28279                 cmd : id,
28280                 cls : 'x-btn-icon x-edit-'+id,
28281                 enableToggle:toggle !== false,
28282                 scope: editorcore, // was editor...
28283                 handler:handler||editorcore.relayBtnCmd,
28284                 clickEvent:'mousedown',
28285                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28286                 tabIndex:-1
28287             };
28288         }
28289         // create a new element.
28290         var wdiv = editor.wrap.createChild({
28291                 tag: 'div'
28292             }, editor.wrap.dom.firstChild.nextSibling, true);
28293         
28294         // can we do this more than once??
28295         
28296          // stop form submits
28297       
28298  
28299         // disable everything...
28300         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28301         this.toolbars = {};
28302         // block toolbars are built in updateToolbar when needed.
28303         for (var i in  ty) {
28304             
28305             this.toolbars[i] = this.buildToolbar(ty[i],i);
28306         }
28307         this.tb = this.toolbars.BODY;
28308         this.tb.el.show();
28309         this.buildFooter();
28310         this.footer.show();
28311         editor.on('hide', function( ) { this.footer.hide() }, this);
28312         editor.on('show', function( ) { this.footer.show() }, this);
28313         
28314          
28315         this.rendered = true;
28316         
28317         // the all the btns;
28318         editor.on('editorevent', this.updateToolbar, this);
28319         // other toolbars need to implement this..
28320         //editor.on('editmodechange', this.updateToolbar, this);
28321     },
28322     
28323     
28324     
28325     /**
28326      * Protected method that will not generally be called directly. It triggers
28327      * a toolbar update by reading the markup state of the current selection in the editor.
28328      *
28329      * Note you can force an update by calling on('editorevent', scope, false)
28330      */
28331     updateToolbar: function(editor ,ev, sel)
28332     {
28333         
28334         if (ev) {
28335             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28336         }
28337         
28338         //Roo.log(ev);
28339         // capture mouse up - this is handy for selecting images..
28340         // perhaps should go somewhere else...
28341         if(!this.editorcore.activated){
28342              this.editor.onFirstFocus();
28343             return;
28344         }
28345         //Roo.log(ev ? ev.target : 'NOTARGET');
28346         
28347         
28348         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28349         // selectNode - might want to handle IE?
28350         
28351         
28352         
28353         if (ev &&
28354             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28355             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28356             // they have click on an image...
28357             // let's see if we can change the selection...
28358             sel = ev.target;
28359             
28360             // this triggers looping?
28361             //this.editorcore.selectNode(sel);
28362              
28363         }
28364         
28365         // this forces an id..
28366         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28367              e.classList.remove('roo-ed-selection');
28368         });
28369         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28370         //Roo.get(node).addClass('roo-ed-selection');
28371       
28372         //var updateFooter = sel ? false : true; 
28373         
28374         
28375         var ans = this.editorcore.getAllAncestors();
28376         
28377         // pick
28378         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28379         
28380         if (!sel) { 
28381             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28382             sel = sel ? sel : this.editorcore.doc.body;
28383             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28384             
28385         }
28386         
28387         var tn = sel.tagName.toUpperCase();
28388         var lastSel = this.tb.selectedNode;
28389         this.tb.selectedNode = sel;
28390         var left_label = tn;
28391         
28392         // ok see if we are editing a block?
28393         
28394         var db = false;
28395         // you are not actually selecting the block.
28396         if (sel && sel.hasAttribute('data-block')) {
28397             db = sel;
28398         } else if (sel && sel.closest('[data-block]')) {
28399             
28400             db = sel.closest('[data-block]');
28401             //var cepar = sel.closest('[contenteditable=true]');
28402             //if (db && cepar && cepar.tagName != 'BODY') {
28403             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28404             //}   
28405         }
28406         
28407         
28408         var block = false;
28409         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28410         if (db && this.editorcore.enableBlocks) {
28411             block = Roo.htmleditor.Block.factory(db);
28412             
28413             
28414             if (block) {
28415                  db.className = (
28416                         db.classList.length > 0  ? db.className + ' ' : ''
28417                     )  + 'roo-ed-selection';
28418                  
28419                  // since we removed it earlier... its not there..
28420                 tn = 'BLOCK.' + db.getAttribute('data-block');
28421                 
28422                 //this.editorcore.selectNode(db);
28423                 if (typeof(this.toolbars[tn]) == 'undefined') {
28424                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28425                 }
28426                 this.toolbars[tn].selectedNode = db;
28427                 left_label = block.friendly_name;
28428                 ans = this.editorcore.getAllAncestors();
28429             }
28430             
28431                 
28432             
28433         }
28434         
28435         
28436         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28437             return; // no change?
28438         }
28439         
28440         
28441           
28442         this.tb.el.hide();
28443         ///console.log("show: " + tn);
28444         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28445         
28446         this.tb.el.show();
28447         // update name
28448         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28449         
28450         
28451         // update attributes
28452         if (block && this.tb.fields) {
28453              
28454             this.tb.fields.each(function(e) {
28455                 e.setValue(block[e.name]);
28456             });
28457             
28458             
28459         } else  if (this.tb.fields && this.tb.selectedNode) {
28460             this.tb.fields.each( function(e) {
28461                 if (e.stylename) {
28462                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28463                     return;
28464                 } 
28465                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28466             }, this);
28467             this.updateToolbarStyles(this.tb.selectedNode);  
28468         }
28469         
28470         
28471        
28472         Roo.menu.MenuMgr.hideAll();
28473
28474         
28475         
28476     
28477         // update the footer
28478         //
28479         this.updateFooter(ans);
28480              
28481     },
28482     
28483     updateToolbarStyles : function(sel)
28484     {
28485         var hasStyles = false;
28486         for(var i in this.styles) {
28487             hasStyles = true;
28488             break;
28489         }
28490         
28491         // update styles
28492         if (hasStyles && this.tb.hasStyles) { 
28493             var st = this.tb.fields.item(0);
28494             
28495             st.store.removeAll();
28496             var cn = sel.className.split(/\s+/);
28497             
28498             var avs = [];
28499             if (this.styles['*']) {
28500                 
28501                 Roo.each(this.styles['*'], function(v) {
28502                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28503                 });
28504             }
28505             if (this.styles[tn]) { 
28506                 Roo.each(this.styles[tn], function(v) {
28507                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28508                 });
28509             }
28510             
28511             st.store.loadData(avs);
28512             st.collapse();
28513             st.setValue(cn);
28514         }
28515     },
28516     
28517      
28518     updateFooter : function(ans)
28519     {
28520         var html = '';
28521         if (ans === false) {
28522             this.footDisp.dom.innerHTML = '';
28523             return;
28524         }
28525         
28526         this.footerEls = ans.reverse();
28527         Roo.each(this.footerEls, function(a,i) {
28528             if (!a) { return; }
28529             html += html.length ? ' &gt; '  :  '';
28530             
28531             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28532             
28533         });
28534        
28535         // 
28536         var sz = this.footDisp.up('td').getSize();
28537         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28538         this.footDisp.dom.style.marginLeft = '5px';
28539         
28540         this.footDisp.dom.style.overflow = 'hidden';
28541         
28542         this.footDisp.dom.innerHTML = html;
28543             
28544         
28545     },
28546    
28547        
28548     // private
28549     onDestroy : function(){
28550         if(this.rendered){
28551             
28552             this.tb.items.each(function(item){
28553                 if(item.menu){
28554                     item.menu.removeAll();
28555                     if(item.menu.el){
28556                         item.menu.el.destroy();
28557                     }
28558                 }
28559                 item.destroy();
28560             });
28561              
28562         }
28563     },
28564     onFirstFocus: function() {
28565         // need to do this for all the toolbars..
28566         this.tb.items.each(function(item){
28567            item.enable();
28568         });
28569     },
28570     buildToolbar: function(tlist, nm, friendly_name, block)
28571     {
28572         var editor = this.editor;
28573         var editorcore = this.editorcore;
28574          // create a new element.
28575         var wdiv = editor.wrap.createChild({
28576                 tag: 'div'
28577             }, editor.wrap.dom.firstChild.nextSibling, true);
28578         
28579        
28580         var tb = new Roo.Toolbar(wdiv);
28581         ///this.tb = tb; // << this sets the active toolbar..
28582         if (tlist === false && block) {
28583             tlist = block.contextMenu(this);
28584         }
28585         
28586         tb.hasStyles = false;
28587         tb.name = nm;
28588         
28589         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28590         
28591         var styles = Array.from(this.styles);
28592         
28593         
28594         // styles...
28595         if (styles && styles.length) {
28596             tb.hasStyles = true;
28597             // this needs a multi-select checkbox...
28598             tb.addField( new Roo.form.ComboBox({
28599                 store: new Roo.data.SimpleStore({
28600                     id : 'val',
28601                     fields: ['val', 'selected'],
28602                     data : [] 
28603                 }),
28604                 name : '-roo-edit-className',
28605                 attrname : 'className',
28606                 displayField: 'val',
28607                 typeAhead: false,
28608                 mode: 'local',
28609                 editable : false,
28610                 triggerAction: 'all',
28611                 emptyText:'Select Style',
28612                 selectOnFocus:true,
28613                 width: 130,
28614                 listeners : {
28615                     'select': function(c, r, i) {
28616                         // initial support only for on class per el..
28617                         tb.selectedNode.className =  r ? r.get('val') : '';
28618                         editorcore.syncValue();
28619                     }
28620                 }
28621     
28622             }));
28623         }
28624         
28625         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28626         
28627         
28628         for (var i = 0; i < tlist.length; i++) {
28629             
28630             // newer versions will use xtype cfg to create menus.
28631             if (typeof(tlist[i].xtype) != 'undefined') {
28632                 
28633                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28634                 
28635                 
28636                 continue;
28637             }
28638             
28639             var item = tlist[i];
28640             tb.add(item.title + ":&nbsp;");
28641             
28642             
28643             //optname == used so you can configure the options available..
28644             var opts = item.opts ? item.opts : false;
28645             if (item.optname) { // use the b
28646                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28647            
28648             }
28649             
28650             if (opts) {
28651                 // opts == pulldown..
28652                 tb.addField( new Roo.form.ComboBox({
28653                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28654                         id : 'val',
28655                         fields: ['val', 'display'],
28656                         data : opts  
28657                     }),
28658                     name : '-roo-edit-' + tlist[i].name,
28659                     
28660                     attrname : tlist[i].name,
28661                     stylename : item.style ? item.style : false,
28662                     
28663                     displayField: item.displayField ? item.displayField : 'val',
28664                     valueField :  'val',
28665                     typeAhead: false,
28666                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28667                     editable : false,
28668                     triggerAction: 'all',
28669                     emptyText:'Select',
28670                     selectOnFocus:true,
28671                     width: item.width ? item.width  : 130,
28672                     listeners : {
28673                         'select': function(c, r, i) {
28674                              
28675                             
28676                             if (c.stylename) {
28677                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28678                                 editorcore.syncValue();
28679                                 return;
28680                             }
28681                             if (r === false) {
28682                                 tb.selectedNode.removeAttribute(c.attrname);
28683                                 editorcore.syncValue();
28684                                 return;
28685                             }
28686                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28687                             editorcore.syncValue();
28688                         }
28689                     }
28690
28691                 }));
28692                 continue;
28693                     
28694                  
28695                 /*
28696                 tb.addField( new Roo.form.TextField({
28697                     name: i,
28698                     width: 100,
28699                     //allowBlank:false,
28700                     value: ''
28701                 }));
28702                 continue;
28703                 */
28704             }
28705             tb.addField( new Roo.form.TextField({
28706                 name: '-roo-edit-' + tlist[i].name,
28707                 attrname : tlist[i].name,
28708                 
28709                 width: item.width,
28710                 //allowBlank:true,
28711                 value: '',
28712                 listeners: {
28713                     'change' : function(f, nv, ov) {
28714                         
28715                          
28716                         tb.selectedNode.setAttribute(f.attrname, nv);
28717                         editorcore.syncValue();
28718                     }
28719                 }
28720             }));
28721              
28722         }
28723         
28724         var _this = this;
28725         var show_delete = !block || block.deleteTitle !== false;
28726         if(nm == 'BODY'){
28727             show_delete = false;
28728             tb.addSeparator();
28729         
28730             tb.addButton( {
28731                 text: 'Stylesheets',
28732
28733                 listeners : {
28734                     click : function ()
28735                     {
28736                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28737                     }
28738                 }
28739             });
28740         }
28741         
28742         tb.addFill();
28743         if (show_delete) {
28744             tb.addButton({
28745                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28746         
28747                 listeners : {
28748                     click : function ()
28749                     {
28750                         var sn = tb.selectedNode;
28751                         if (block) {
28752                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28753                             
28754                         }
28755                         if (!sn) {
28756                             return;
28757                         }
28758                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28759                         if (sn.hasAttribute('data-block')) {
28760                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28761                             sn.parentNode.removeChild(sn);
28762                             
28763                         } else if (sn && sn.tagName != 'BODY') {
28764                             // remove and keep parents.
28765                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28766                             a.replaceTag(sn);
28767                         }
28768                         
28769                         
28770                         var range = editorcore.createRange();
28771             
28772                         range.setStart(stn,0);
28773                         range.setEnd(stn,0); 
28774                         var selection = editorcore.getSelection();
28775                         selection.removeAllRanges();
28776                         selection.addRange(range);
28777                         
28778                         
28779                         //_this.updateToolbar(null, null, pn);
28780                         _this.updateToolbar(null, null, null);
28781                         _this.updateFooter(false);
28782                         
28783                     }
28784                 }
28785                 
28786                         
28787                     
28788                 
28789             });
28790         }    
28791         
28792         tb.el.on('click', function(e){
28793             e.preventDefault(); // what does this do?
28794         });
28795         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28796         tb.el.hide();
28797         
28798         // dont need to disable them... as they will get hidden
28799         return tb;
28800          
28801         
28802     },
28803     buildFooter : function()
28804     {
28805         
28806         var fel = this.editor.wrap.createChild();
28807         this.footer = new Roo.Toolbar(fel);
28808         // toolbar has scrolly on left / right?
28809         var footDisp= new Roo.Toolbar.Fill();
28810         var _t = this;
28811         this.footer.add(
28812             {
28813                 text : '&lt;',
28814                 xtype: 'Button',
28815                 handler : function() {
28816                     _t.footDisp.scrollTo('left',0,true)
28817                 }
28818             }
28819         );
28820         this.footer.add( footDisp );
28821         this.footer.add( 
28822             {
28823                 text : '&gt;',
28824                 xtype: 'Button',
28825                 handler : function() {
28826                     // no animation..
28827                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28828                 }
28829             }
28830         );
28831         var fel = Roo.get(footDisp.el);
28832         fel.addClass('x-editor-context');
28833         this.footDispWrap = fel; 
28834         this.footDispWrap.overflow  = 'hidden';
28835         
28836         this.footDisp = fel.createChild();
28837         this.footDispWrap.on('click', this.onContextClick, this)
28838         
28839         
28840     },
28841     // when the footer contect changes
28842     onContextClick : function (ev,dom)
28843     {
28844         ev.preventDefault();
28845         var  cn = dom.className;
28846         //Roo.log(cn);
28847         if (!cn.match(/x-ed-loc-/)) {
28848             return;
28849         }
28850         var n = cn.split('-').pop();
28851         var ans = this.footerEls;
28852         var sel = ans[n];
28853         
28854         this.editorcore.selectNode(sel);
28855         
28856         
28857         this.updateToolbar(null, null, sel);
28858         
28859         
28860     }
28861     
28862     
28863     
28864     
28865     
28866 });
28867
28868
28869
28870
28871
28872 /*
28873  * Based on:
28874  * Ext JS Library 1.1.1
28875  * Copyright(c) 2006-2007, Ext JS, LLC.
28876  *
28877  * Originally Released Under LGPL - original licence link has changed is not relivant.
28878  *
28879  * Fork - LGPL
28880  * <script type="text/javascript">
28881  */
28882  
28883 /**
28884  * @class Roo.form.BasicForm
28885  * @extends Roo.util.Observable
28886  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28887  * @constructor
28888  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28889  * @param {Object} config Configuration options
28890  */
28891 Roo.form.BasicForm = function(el, config){
28892     this.allItems = [];
28893     this.childForms = [];
28894     Roo.apply(this, config);
28895     /*
28896      * The Roo.form.Field items in this form.
28897      * @type MixedCollection
28898      */
28899      
28900      
28901     this.items = new Roo.util.MixedCollection(false, function(o){
28902         return o.id || (o.id = Roo.id());
28903     });
28904     this.addEvents({
28905         /**
28906          * @event beforeaction
28907          * Fires before any action is performed. Return false to cancel the action.
28908          * @param {Form} this
28909          * @param {Action} action The action to be performed
28910          */
28911         beforeaction: true,
28912         /**
28913          * @event actionfailed
28914          * Fires when an action fails.
28915          * @param {Form} this
28916          * @param {Action} action The action that failed
28917          */
28918         actionfailed : true,
28919         /**
28920          * @event actioncomplete
28921          * Fires when an action is completed.
28922          * @param {Form} this
28923          * @param {Action} action The action that completed
28924          */
28925         actioncomplete : true
28926     });
28927     if(el){
28928         this.initEl(el);
28929     }
28930     Roo.form.BasicForm.superclass.constructor.call(this);
28931     
28932     Roo.form.BasicForm.popover.apply();
28933 };
28934
28935 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28936     /**
28937      * @cfg {String} method
28938      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28939      */
28940     /**
28941      * @cfg {DataReader} reader
28942      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28943      * This is optional as there is built-in support for processing JSON.
28944      */
28945     /**
28946      * @cfg {DataReader} errorReader
28947      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28948      * This is completely optional as there is built-in support for processing JSON.
28949      */
28950     /**
28951      * @cfg {String} url
28952      * The URL to use for form actions if one isn't supplied in the action options.
28953      */
28954     /**
28955      * @cfg {Boolean} fileUpload
28956      * Set to true if this form is a file upload.
28957      */
28958      
28959     /**
28960      * @cfg {Object} baseParams
28961      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28962      */
28963      /**
28964      
28965     /**
28966      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28967      */
28968     timeout: 30,
28969
28970     // private
28971     activeAction : null,
28972
28973     /**
28974      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28975      * or setValues() data instead of when the form was first created.
28976      */
28977     trackResetOnLoad : false,
28978     
28979     
28980     /**
28981      * childForms - used for multi-tab forms
28982      * @type {Array}
28983      */
28984     childForms : false,
28985     
28986     /**
28987      * allItems - full list of fields.
28988      * @type {Array}
28989      */
28990     allItems : false,
28991     
28992     /**
28993      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28994      * element by passing it or its id or mask the form itself by passing in true.
28995      * @type Mixed
28996      */
28997     waitMsgTarget : false,
28998     
28999     /**
29000      * @type Boolean
29001      */
29002     disableMask : false,
29003     
29004     /**
29005      * @cfg {Boolean} errorMask (true|false) default false
29006      */
29007     errorMask : false,
29008     
29009     /**
29010      * @cfg {Number} maskOffset Default 100
29011      */
29012     maskOffset : 100,
29013
29014     // private
29015     initEl : function(el){
29016         this.el = Roo.get(el);
29017         this.id = this.el.id || Roo.id();
29018         this.el.on('submit', this.onSubmit, this);
29019         this.el.addClass('x-form');
29020     },
29021
29022     // private
29023     onSubmit : function(e){
29024         e.stopEvent();
29025     },
29026
29027     /**
29028      * Returns true if client-side validation on the form is successful.
29029      * @return Boolean
29030      */
29031     isValid : function(){
29032         var valid = true;
29033         var target = false;
29034         this.items.each(function(f){
29035             if(f.validate()){
29036                 return;
29037             }
29038             
29039             valid = false;
29040                 
29041             if(!target && f.el.isVisible(true)){
29042                 target = f;
29043             }
29044         });
29045         
29046         if(this.errorMask && !valid){
29047             Roo.form.BasicForm.popover.mask(this, target);
29048         }
29049         
29050         return valid;
29051     },
29052     /**
29053      * Returns array of invalid form fields.
29054      * @return Array
29055      */
29056     
29057     invalidFields : function()
29058     {
29059         var ret = [];
29060         this.items.each(function(f){
29061             if(f.validate()){
29062                 return;
29063             }
29064             ret.push(f);
29065             
29066         });
29067         
29068         return ret;
29069     },
29070     
29071     
29072     /**
29073      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29074      * @return Boolean
29075      */
29076     isDirty : function(){
29077         var dirty = false;
29078         this.items.each(function(f){
29079            if(f.isDirty()){
29080                dirty = true;
29081                return false;
29082            }
29083         });
29084         return dirty;
29085     },
29086     
29087     /**
29088      * Returns true if any fields in this form have changed since their original load. (New version)
29089      * @return Boolean
29090      */
29091     
29092     hasChanged : function()
29093     {
29094         var dirty = false;
29095         this.items.each(function(f){
29096            if(f.hasChanged()){
29097                dirty = true;
29098                return false;
29099            }
29100         });
29101         return dirty;
29102         
29103     },
29104     /**
29105      * Resets all hasChanged to 'false' -
29106      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29107      * So hasChanged storage is only to be used for this purpose
29108      * @return Boolean
29109      */
29110     resetHasChanged : function()
29111     {
29112         this.items.each(function(f){
29113            f.resetHasChanged();
29114         });
29115         
29116     },
29117     
29118     
29119     /**
29120      * Performs a predefined action (submit or load) or custom actions you define on this form.
29121      * @param {String} actionName The name of the action type
29122      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29123      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29124      * accept other config options):
29125      * <pre>
29126 Property          Type             Description
29127 ----------------  ---------------  ----------------------------------------------------------------------------------
29128 url               String           The url for the action (defaults to the form's url)
29129 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29130 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29131 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29132                                    validate the form on the client (defaults to false)
29133      * </pre>
29134      * @return {BasicForm} this
29135      */
29136     doAction : function(action, options){
29137         if(typeof action == 'string'){
29138             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29139         }
29140         if(this.fireEvent('beforeaction', this, action) !== false){
29141             this.beforeAction(action);
29142             action.run.defer(100, action);
29143         }
29144         return this;
29145     },
29146
29147     /**
29148      * Shortcut to do a submit action.
29149      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29150      * @return {BasicForm} this
29151      */
29152     submit : function(options){
29153         this.doAction('submit', options);
29154         return this;
29155     },
29156
29157     /**
29158      * Shortcut to do a load action.
29159      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29160      * @return {BasicForm} this
29161      */
29162     load : function(options){
29163         this.doAction('load', options);
29164         return this;
29165     },
29166
29167     /**
29168      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29169      * @param {Record} record The record to edit
29170      * @return {BasicForm} this
29171      */
29172     updateRecord : function(record){
29173         record.beginEdit();
29174         var fs = record.fields;
29175         fs.each(function(f){
29176             var field = this.findField(f.name);
29177             if(field){
29178                 record.set(f.name, field.getValue());
29179             }
29180         }, this);
29181         record.endEdit();
29182         return this;
29183     },
29184
29185     /**
29186      * Loads an Roo.data.Record into this form.
29187      * @param {Record} record The record to load
29188      * @return {BasicForm} this
29189      */
29190     loadRecord : function(record){
29191         this.setValues(record.data);
29192         return this;
29193     },
29194
29195     // private
29196     beforeAction : function(action){
29197         var o = action.options;
29198         
29199         if(!this.disableMask) {
29200             if(this.waitMsgTarget === true){
29201                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29202             }else if(this.waitMsgTarget){
29203                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29204                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29205             }else {
29206                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29207             }
29208         }
29209         
29210          
29211     },
29212
29213     // private
29214     afterAction : function(action, success){
29215         this.activeAction = null;
29216         var o = action.options;
29217         
29218         if(!this.disableMask) {
29219             if(this.waitMsgTarget === true){
29220                 this.el.unmask();
29221             }else if(this.waitMsgTarget){
29222                 this.waitMsgTarget.unmask();
29223             }else{
29224                 Roo.MessageBox.updateProgress(1);
29225                 Roo.MessageBox.hide();
29226             }
29227         }
29228         
29229         if(success){
29230             if(o.reset){
29231                 this.reset();
29232             }
29233             Roo.callback(o.success, o.scope, [this, action]);
29234             this.fireEvent('actioncomplete', this, action);
29235             
29236         }else{
29237             
29238             // failure condition..
29239             // we have a scenario where updates need confirming.
29240             // eg. if a locking scenario exists..
29241             // we look for { errors : { needs_confirm : true }} in the response.
29242             if (
29243                 (typeof(action.result) != 'undefined')  &&
29244                 (typeof(action.result.errors) != 'undefined')  &&
29245                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29246            ){
29247                 var _t = this;
29248                 Roo.MessageBox.confirm(
29249                     "Change requires confirmation",
29250                     action.result.errorMsg,
29251                     function(r) {
29252                         if (r != 'yes') {
29253                             return;
29254                         }
29255                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29256                     }
29257                     
29258                 );
29259                 
29260                 
29261                 
29262                 return;
29263             }
29264             
29265             Roo.callback(o.failure, o.scope, [this, action]);
29266             // show an error message if no failed handler is set..
29267             if (!this.hasListener('actionfailed')) {
29268                 Roo.MessageBox.alert("Error",
29269                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29270                         action.result.errorMsg :
29271                         "Saving Failed, please check your entries or try again"
29272                 );
29273             }
29274             
29275             this.fireEvent('actionfailed', this, action);
29276         }
29277         
29278     },
29279
29280     /**
29281      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29282      * @param {String} id The value to search for
29283      * @return Field
29284      */
29285     findField : function(id){
29286         var field = this.items.get(id);
29287         if(!field){
29288             this.items.each(function(f){
29289                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29290                     field = f;
29291                     return false;
29292                 }
29293             });
29294         }
29295         return field || null;
29296     },
29297
29298     /**
29299      * Add a secondary form to this one, 
29300      * Used to provide tabbed forms. One form is primary, with hidden values 
29301      * which mirror the elements from the other forms.
29302      * 
29303      * @param {Roo.form.Form} form to add.
29304      * 
29305      */
29306     addForm : function(form)
29307     {
29308        
29309         if (this.childForms.indexOf(form) > -1) {
29310             // already added..
29311             return;
29312         }
29313         this.childForms.push(form);
29314         var n = '';
29315         Roo.each(form.allItems, function (fe) {
29316             
29317             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29318             if (this.findField(n)) { // already added..
29319                 return;
29320             }
29321             var add = new Roo.form.Hidden({
29322                 name : n
29323             });
29324             add.render(this.el);
29325             
29326             this.add( add );
29327         }, this);
29328         
29329     },
29330     /**
29331      * Mark fields in this form invalid in bulk.
29332      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29333      * @return {BasicForm} this
29334      */
29335     markInvalid : function(errors){
29336         if(errors instanceof Array){
29337             for(var i = 0, len = errors.length; i < len; i++){
29338                 var fieldError = errors[i];
29339                 var f = this.findField(fieldError.id);
29340                 if(f){
29341                     f.markInvalid(fieldError.msg);
29342                 }
29343             }
29344         }else{
29345             var field, id;
29346             for(id in errors){
29347                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29348                     field.markInvalid(errors[id]);
29349                 }
29350             }
29351         }
29352         Roo.each(this.childForms || [], function (f) {
29353             f.markInvalid(errors);
29354         });
29355         
29356         return this;
29357     },
29358
29359     /**
29360      * Set values for fields in this form in bulk.
29361      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29362      * @return {BasicForm} this
29363      */
29364     setValues : function(values){
29365         if(values instanceof Array){ // array of objects
29366             for(var i = 0, len = values.length; i < len; i++){
29367                 var v = values[i];
29368                 var f = this.findField(v.id);
29369                 if(f){
29370                     f.setValue(v.value);
29371                     if(this.trackResetOnLoad){
29372                         f.originalValue = f.getValue();
29373                     }
29374                 }
29375             }
29376         }else{ // object hash
29377             var field, id;
29378             for(id in values){
29379                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29380                     
29381                     if (field.setFromData && 
29382                         field.valueField && 
29383                         field.displayField &&
29384                         // combos' with local stores can 
29385                         // be queried via setValue()
29386                         // to set their value..
29387                         (field.store && !field.store.isLocal)
29388                         ) {
29389                         // it's a combo
29390                         var sd = { };
29391                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29392                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29393                         field.setFromData(sd);
29394                         
29395                     } else {
29396                         field.setValue(values[id]);
29397                     }
29398                     
29399                     
29400                     if(this.trackResetOnLoad){
29401                         field.originalValue = field.getValue();
29402                     }
29403                 }
29404             }
29405         }
29406         this.resetHasChanged();
29407         
29408         
29409         Roo.each(this.childForms || [], function (f) {
29410             f.setValues(values);
29411             f.resetHasChanged();
29412         });
29413                 
29414         return this;
29415     },
29416  
29417     /**
29418      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29419      * they are returned as an array.
29420      * @param {Boolean} asString
29421      * @return {Object}
29422      */
29423     getValues : function(asString)
29424     {
29425         if (this.childForms) {
29426             // copy values from the child forms
29427             Roo.each(this.childForms, function (f) {
29428                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29429             }, this);
29430         }
29431         
29432         // use formdata
29433         if (typeof(FormData) != 'undefined' && asString !== true) {
29434             // this relies on a 'recent' version of chrome apparently...
29435             try {
29436                 var fd = (new FormData(this.el.dom)).entries();
29437                 var ret = {};
29438                 var ent = fd.next();
29439                 while (!ent.done) {
29440                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29441                     ent = fd.next();
29442                 };
29443                 return ret;
29444             } catch(e) {
29445                 
29446             }
29447             
29448         }
29449         
29450         
29451         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29452         if(asString === true){
29453             return fs;
29454         }
29455         return Roo.urlDecode(fs);
29456     },
29457     
29458     /**
29459      * Returns the fields in this form as an object with key/value pairs. 
29460      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29461      * Normally this will not return readOnly data 
29462      * @param {Boolean} with_readonly return readonly field data.
29463      * @return {Object}
29464      */
29465     getFieldValues : function(with_readonly)
29466     {
29467         if (this.childForms) {
29468             // copy values from the child forms
29469             // should this call getFieldValues - probably not as we do not currently copy
29470             // hidden fields when we generate..
29471             Roo.each(this.childForms, function (f) {
29472                 this.setValues(f.getFieldValues());
29473             }, this);
29474         }
29475         
29476         var ret = {};
29477         this.items.each(function(f){
29478             
29479             if (f.readOnly && with_readonly !== true) {
29480                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29481                         // if a subform contains a copy of them.
29482                         // if you have subforms with the same editable data, you will need to copy the data back
29483                         // and forth.
29484             }
29485             
29486             if (!f.getName()) {
29487                 return;
29488             }
29489             var v = f.getValue();
29490             if (f.inputType =='radio') {
29491                 if (typeof(ret[f.getName()]) == 'undefined') {
29492                     ret[f.getName()] = ''; // empty..
29493                 }
29494                 
29495                 if (!f.el.dom.checked) {
29496                     return;
29497                     
29498                 }
29499                 v = f.el.dom.value;
29500                 
29501             }
29502             
29503             // not sure if this supported any more..
29504             if ((typeof(v) == 'object') && f.getRawValue) {
29505                 v = f.getRawValue() ; // dates..
29506             }
29507             // combo boxes where name != hiddenName...
29508             if (f.name != f.getName()) {
29509                 ret[f.name] = f.getRawValue();
29510             }
29511             ret[f.getName()] = v;
29512         });
29513         
29514         return ret;
29515     },
29516
29517     /**
29518      * Clears all invalid messages in this form.
29519      * @return {BasicForm} this
29520      */
29521     clearInvalid : function(){
29522         this.items.each(function(f){
29523            f.clearInvalid();
29524         });
29525         
29526         Roo.each(this.childForms || [], function (f) {
29527             f.clearInvalid();
29528         });
29529         
29530         
29531         return this;
29532     },
29533
29534     /**
29535      * Resets this form.
29536      * @return {BasicForm} this
29537      */
29538     reset : function(){
29539         this.items.each(function(f){
29540             f.reset();
29541         });
29542         
29543         Roo.each(this.childForms || [], function (f) {
29544             f.reset();
29545         });
29546         this.resetHasChanged();
29547         
29548         return this;
29549     },
29550
29551     /**
29552      * Add Roo.form components to this form.
29553      * @param {Field} field1
29554      * @param {Field} field2 (optional)
29555      * @param {Field} etc (optional)
29556      * @return {BasicForm} this
29557      */
29558     add : function(){
29559         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29560         return this;
29561     },
29562
29563
29564     /**
29565      * Removes a field from the items collection (does NOT remove its markup).
29566      * @param {Field} field
29567      * @return {BasicForm} this
29568      */
29569     remove : function(field){
29570         this.items.remove(field);
29571         return this;
29572     },
29573
29574     /**
29575      * Looks at the fields in this form, checks them for an id attribute,
29576      * and calls applyTo on the existing dom element with that id.
29577      * @return {BasicForm} this
29578      */
29579     render : function(){
29580         this.items.each(function(f){
29581             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29582                 f.applyTo(f.id);
29583             }
29584         });
29585         return this;
29586     },
29587
29588     /**
29589      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29590      * @param {Object} values
29591      * @return {BasicForm} this
29592      */
29593     applyToFields : function(o){
29594         this.items.each(function(f){
29595            Roo.apply(f, o);
29596         });
29597         return this;
29598     },
29599
29600     /**
29601      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29602      * @param {Object} values
29603      * @return {BasicForm} this
29604      */
29605     applyIfToFields : function(o){
29606         this.items.each(function(f){
29607            Roo.applyIf(f, o);
29608         });
29609         return this;
29610     }
29611 });
29612
29613 // back compat
29614 Roo.BasicForm = Roo.form.BasicForm;
29615
29616 Roo.apply(Roo.form.BasicForm, {
29617     
29618     popover : {
29619         
29620         padding : 5,
29621         
29622         isApplied : false,
29623         
29624         isMasked : false,
29625         
29626         form : false,
29627         
29628         target : false,
29629         
29630         intervalID : false,
29631         
29632         maskEl : false,
29633         
29634         apply : function()
29635         {
29636             if(this.isApplied){
29637                 return;
29638             }
29639             
29640             this.maskEl = {
29641                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29642                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29643                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29644                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29645             };
29646             
29647             this.maskEl.top.enableDisplayMode("block");
29648             this.maskEl.left.enableDisplayMode("block");
29649             this.maskEl.bottom.enableDisplayMode("block");
29650             this.maskEl.right.enableDisplayMode("block");
29651             
29652             Roo.get(document.body).on('click', function(){
29653                 this.unmask();
29654             }, this);
29655             
29656             Roo.get(document.body).on('touchstart', function(){
29657                 this.unmask();
29658             }, this);
29659             
29660             this.isApplied = true
29661         },
29662         
29663         mask : function(form, target)
29664         {
29665             this.form = form;
29666             
29667             this.target = target;
29668             
29669             if(!this.form.errorMask || !target.el){
29670                 return;
29671             }
29672             
29673             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29674             
29675             var ot = this.target.el.calcOffsetsTo(scrollable);
29676             
29677             var scrollTo = ot[1] - this.form.maskOffset;
29678             
29679             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29680             
29681             scrollable.scrollTo('top', scrollTo);
29682             
29683             var el = this.target.wrap || this.target.el;
29684             
29685             var box = el.getBox();
29686             
29687             this.maskEl.top.setStyle('position', 'absolute');
29688             this.maskEl.top.setStyle('z-index', 10000);
29689             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29690             this.maskEl.top.setLeft(0);
29691             this.maskEl.top.setTop(0);
29692             this.maskEl.top.show();
29693             
29694             this.maskEl.left.setStyle('position', 'absolute');
29695             this.maskEl.left.setStyle('z-index', 10000);
29696             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29697             this.maskEl.left.setLeft(0);
29698             this.maskEl.left.setTop(box.y - this.padding);
29699             this.maskEl.left.show();
29700
29701             this.maskEl.bottom.setStyle('position', 'absolute');
29702             this.maskEl.bottom.setStyle('z-index', 10000);
29703             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29704             this.maskEl.bottom.setLeft(0);
29705             this.maskEl.bottom.setTop(box.bottom + this.padding);
29706             this.maskEl.bottom.show();
29707
29708             this.maskEl.right.setStyle('position', 'absolute');
29709             this.maskEl.right.setStyle('z-index', 10000);
29710             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29711             this.maskEl.right.setLeft(box.right + this.padding);
29712             this.maskEl.right.setTop(box.y - this.padding);
29713             this.maskEl.right.show();
29714
29715             this.intervalID = window.setInterval(function() {
29716                 Roo.form.BasicForm.popover.unmask();
29717             }, 10000);
29718
29719             window.onwheel = function(){ return false;};
29720             
29721             (function(){ this.isMasked = true; }).defer(500, this);
29722             
29723         },
29724         
29725         unmask : function()
29726         {
29727             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29728                 return;
29729             }
29730             
29731             this.maskEl.top.setStyle('position', 'absolute');
29732             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29733             this.maskEl.top.hide();
29734
29735             this.maskEl.left.setStyle('position', 'absolute');
29736             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29737             this.maskEl.left.hide();
29738
29739             this.maskEl.bottom.setStyle('position', 'absolute');
29740             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29741             this.maskEl.bottom.hide();
29742
29743             this.maskEl.right.setStyle('position', 'absolute');
29744             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29745             this.maskEl.right.hide();
29746             
29747             window.onwheel = function(){ return true;};
29748             
29749             if(this.intervalID){
29750                 window.clearInterval(this.intervalID);
29751                 this.intervalID = false;
29752             }
29753             
29754             this.isMasked = false;
29755             
29756         }
29757         
29758     }
29759     
29760 });/*
29761  * Based on:
29762  * Ext JS Library 1.1.1
29763  * Copyright(c) 2006-2007, Ext JS, LLC.
29764  *
29765  * Originally Released Under LGPL - original licence link has changed is not relivant.
29766  *
29767  * Fork - LGPL
29768  * <script type="text/javascript">
29769  */
29770
29771 /**
29772  * @class Roo.form.Form
29773  * @extends Roo.form.BasicForm
29774  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29775  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29776  * @constructor
29777  * @param {Object} config Configuration options
29778  */
29779 Roo.form.Form = function(config){
29780     var xitems =  [];
29781     if (config.items) {
29782         xitems = config.items;
29783         delete config.items;
29784     }
29785    
29786     
29787     Roo.form.Form.superclass.constructor.call(this, null, config);
29788     this.url = this.url || this.action;
29789     if(!this.root){
29790         this.root = new Roo.form.Layout(Roo.applyIf({
29791             id: Roo.id()
29792         }, config));
29793     }
29794     this.active = this.root;
29795     /**
29796      * Array of all the buttons that have been added to this form via {@link addButton}
29797      * @type Array
29798      */
29799     this.buttons = [];
29800     this.allItems = [];
29801     this.addEvents({
29802         /**
29803          * @event clientvalidation
29804          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29805          * @param {Form} this
29806          * @param {Boolean} valid true if the form has passed client-side validation
29807          */
29808         clientvalidation: true,
29809         /**
29810          * @event rendered
29811          * Fires when the form is rendered
29812          * @param {Roo.form.Form} form
29813          */
29814         rendered : true
29815     });
29816     
29817     if (this.progressUrl) {
29818             // push a hidden field onto the list of fields..
29819             this.addxtype( {
29820                     xns: Roo.form, 
29821                     xtype : 'Hidden', 
29822                     name : 'UPLOAD_IDENTIFIER' 
29823             });
29824         }
29825         
29826     
29827     Roo.each(xitems, this.addxtype, this);
29828     
29829 };
29830
29831 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29832      /**
29833      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29834      */
29835     
29836     /**
29837      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29838      */
29839     /**
29840      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29841      */
29842     /**
29843      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29844      */
29845     buttonAlign:'center',
29846
29847     /**
29848      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29849      */
29850     minButtonWidth:75,
29851
29852     /**
29853      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29854      * This property cascades to child containers if not set.
29855      */
29856     labelAlign:'left',
29857
29858     /**
29859      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29860      * fires a looping event with that state. This is required to bind buttons to the valid
29861      * state using the config value formBind:true on the button.
29862      */
29863     monitorValid : false,
29864
29865     /**
29866      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29867      */
29868     monitorPoll : 200,
29869     
29870     /**
29871      * @cfg {String} progressUrl - Url to return progress data 
29872      */
29873     
29874     progressUrl : false,
29875     /**
29876      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29877      * sending a formdata with extra parameters - eg uploaded elements.
29878      */
29879     
29880     formData : false,
29881     
29882     /**
29883      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29884      * fields are added and the column is closed. If no fields are passed the column remains open
29885      * until end() is called.
29886      * @param {Object} config The config to pass to the column
29887      * @param {Field} field1 (optional)
29888      * @param {Field} field2 (optional)
29889      * @param {Field} etc (optional)
29890      * @return Column The column container object
29891      */
29892     column : function(c){
29893         var col = new Roo.form.Column(c);
29894         this.start(col);
29895         if(arguments.length > 1){ // duplicate code required because of Opera
29896             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29897             this.end();
29898         }
29899         return col;
29900     },
29901
29902     /**
29903      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29904      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29905      * until end() is called.
29906      * @param {Object} config The config to pass to the fieldset
29907      * @param {Field} field1 (optional)
29908      * @param {Field} field2 (optional)
29909      * @param {Field} etc (optional)
29910      * @return FieldSet The fieldset container object
29911      */
29912     fieldset : function(c){
29913         var fs = new Roo.form.FieldSet(c);
29914         this.start(fs);
29915         if(arguments.length > 1){ // duplicate code required because of Opera
29916             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29917             this.end();
29918         }
29919         return fs;
29920     },
29921
29922     /**
29923      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29924      * fields are added and the container is closed. If no fields are passed the container remains open
29925      * until end() is called.
29926      * @param {Object} config The config to pass to the Layout
29927      * @param {Field} field1 (optional)
29928      * @param {Field} field2 (optional)
29929      * @param {Field} etc (optional)
29930      * @return Layout The container object
29931      */
29932     container : function(c){
29933         var l = new Roo.form.Layout(c);
29934         this.start(l);
29935         if(arguments.length > 1){ // duplicate code required because of Opera
29936             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29937             this.end();
29938         }
29939         return l;
29940     },
29941
29942     /**
29943      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29944      * @param {Object} container A Roo.form.Layout or subclass of Layout
29945      * @return {Form} this
29946      */
29947     start : function(c){
29948         // cascade label info
29949         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29950         this.active.stack.push(c);
29951         c.ownerCt = this.active;
29952         this.active = c;
29953         return this;
29954     },
29955
29956     /**
29957      * Closes the current open container
29958      * @return {Form} this
29959      */
29960     end : function(){
29961         if(this.active == this.root){
29962             return this;
29963         }
29964         this.active = this.active.ownerCt;
29965         return this;
29966     },
29967
29968     /**
29969      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29970      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29971      * as the label of the field.
29972      * @param {Field} field1
29973      * @param {Field} field2 (optional)
29974      * @param {Field} etc. (optional)
29975      * @return {Form} this
29976      */
29977     add : function(){
29978         this.active.stack.push.apply(this.active.stack, arguments);
29979         this.allItems.push.apply(this.allItems,arguments);
29980         var r = [];
29981         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29982             if(a[i].isFormField){
29983                 r.push(a[i]);
29984             }
29985         }
29986         if(r.length > 0){
29987             Roo.form.Form.superclass.add.apply(this, r);
29988         }
29989         return this;
29990     },
29991     
29992
29993     
29994     
29995     
29996      /**
29997      * Find any element that has been added to a form, using it's ID or name
29998      * This can include framesets, columns etc. along with regular fields..
29999      * @param {String} id - id or name to find.
30000      
30001      * @return {Element} e - or false if nothing found.
30002      */
30003     findbyId : function(id)
30004     {
30005         var ret = false;
30006         if (!id) {
30007             return ret;
30008         }
30009         Roo.each(this.allItems, function(f){
30010             if (f.id == id || f.name == id ){
30011                 ret = f;
30012                 return false;
30013             }
30014         });
30015         return ret;
30016     },
30017
30018     
30019     
30020     /**
30021      * Render this form into the passed container. This should only be called once!
30022      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30023      * @return {Form} this
30024      */
30025     render : function(ct)
30026     {
30027         
30028         
30029         
30030         ct = Roo.get(ct);
30031         var o = this.autoCreate || {
30032             tag: 'form',
30033             method : this.method || 'POST',
30034             id : this.id || Roo.id()
30035         };
30036         this.initEl(ct.createChild(o));
30037
30038         this.root.render(this.el);
30039         
30040        
30041              
30042         this.items.each(function(f){
30043             f.render('x-form-el-'+f.id);
30044         });
30045
30046         if(this.buttons.length > 0){
30047             // tables are required to maintain order and for correct IE layout
30048             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30049                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30050                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30051             }}, null, true);
30052             var tr = tb.getElementsByTagName('tr')[0];
30053             for(var i = 0, len = this.buttons.length; i < len; i++) {
30054                 var b = this.buttons[i];
30055                 var td = document.createElement('td');
30056                 td.className = 'x-form-btn-td';
30057                 b.render(tr.appendChild(td));
30058             }
30059         }
30060         if(this.monitorValid){ // initialize after render
30061             this.startMonitoring();
30062         }
30063         this.fireEvent('rendered', this);
30064         return this;
30065     },
30066
30067     /**
30068      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30069      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30070      * object or a valid Roo.DomHelper element config
30071      * @param {Function} handler The function called when the button is clicked
30072      * @param {Object} scope (optional) The scope of the handler function
30073      * @return {Roo.Button}
30074      */
30075     addButton : function(config, handler, scope){
30076         var bc = {
30077             handler: handler,
30078             scope: scope,
30079             minWidth: this.minButtonWidth,
30080             hideParent:true
30081         };
30082         if(typeof config == "string"){
30083             bc.text = config;
30084         }else{
30085             Roo.apply(bc, config);
30086         }
30087         var btn = new Roo.Button(null, bc);
30088         this.buttons.push(btn);
30089         return btn;
30090     },
30091
30092      /**
30093      * Adds a series of form elements (using the xtype property as the factory method.
30094      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30095      * @param {Object} config 
30096      */
30097     
30098     addxtype : function()
30099     {
30100         var ar = Array.prototype.slice.call(arguments, 0);
30101         var ret = false;
30102         for(var i = 0; i < ar.length; i++) {
30103             if (!ar[i]) {
30104                 continue; // skip -- if this happends something invalid got sent, we 
30105                 // should ignore it, as basically that interface element will not show up
30106                 // and that should be pretty obvious!!
30107             }
30108             
30109             if (Roo.form[ar[i].xtype]) {
30110                 ar[i].form = this;
30111                 var fe = Roo.factory(ar[i], Roo.form);
30112                 if (!ret) {
30113                     ret = fe;
30114                 }
30115                 fe.form = this;
30116                 if (fe.store) {
30117                     fe.store.form = this;
30118                 }
30119                 if (fe.isLayout) {  
30120                          
30121                     this.start(fe);
30122                     this.allItems.push(fe);
30123                     if (fe.items && fe.addxtype) {
30124                         fe.addxtype.apply(fe, fe.items);
30125                         delete fe.items;
30126                     }
30127                      this.end();
30128                     continue;
30129                 }
30130                 
30131                 
30132                  
30133                 this.add(fe);
30134               //  console.log('adding ' + ar[i].xtype);
30135             }
30136             if (ar[i].xtype == 'Button') {  
30137                 //console.log('adding button');
30138                 //console.log(ar[i]);
30139                 this.addButton(ar[i]);
30140                 this.allItems.push(fe);
30141                 continue;
30142             }
30143             
30144             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30145                 alert('end is not supported on xtype any more, use items');
30146             //    this.end();
30147             //    //console.log('adding end');
30148             }
30149             
30150         }
30151         return ret;
30152     },
30153     
30154     /**
30155      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30156      * option "monitorValid"
30157      */
30158     startMonitoring : function(){
30159         if(!this.bound){
30160             this.bound = true;
30161             Roo.TaskMgr.start({
30162                 run : this.bindHandler,
30163                 interval : this.monitorPoll || 200,
30164                 scope: this
30165             });
30166         }
30167     },
30168
30169     /**
30170      * Stops monitoring of the valid state of this form
30171      */
30172     stopMonitoring : function(){
30173         this.bound = false;
30174     },
30175
30176     // private
30177     bindHandler : function(){
30178         if(!this.bound){
30179             return false; // stops binding
30180         }
30181         var valid = true;
30182         this.items.each(function(f){
30183             if(!f.isValid(true)){
30184                 valid = false;
30185                 return false;
30186             }
30187         });
30188         for(var i = 0, len = this.buttons.length; i < len; i++){
30189             var btn = this.buttons[i];
30190             if(btn.formBind === true && btn.disabled === valid){
30191                 btn.setDisabled(!valid);
30192             }
30193         }
30194         this.fireEvent('clientvalidation', this, valid);
30195     }
30196     
30197     
30198     
30199     
30200     
30201     
30202     
30203     
30204 });
30205
30206
30207 // back compat
30208 Roo.Form = Roo.form.Form;
30209 /*
30210  * Based on:
30211  * Ext JS Library 1.1.1
30212  * Copyright(c) 2006-2007, Ext JS, LLC.
30213  *
30214  * Originally Released Under LGPL - original licence link has changed is not relivant.
30215  *
30216  * Fork - LGPL
30217  * <script type="text/javascript">
30218  */
30219
30220 // as we use this in bootstrap.
30221 Roo.namespace('Roo.form');
30222  /**
30223  * @class Roo.form.Action
30224  * Internal Class used to handle form actions
30225  * @constructor
30226  * @param {Roo.form.BasicForm} el The form element or its id
30227  * @param {Object} config Configuration options
30228  */
30229
30230  
30231  
30232 // define the action interface
30233 Roo.form.Action = function(form, options){
30234     this.form = form;
30235     this.options = options || {};
30236 };
30237 /**
30238  * Client Validation Failed
30239  * @const 
30240  */
30241 Roo.form.Action.CLIENT_INVALID = 'client';
30242 /**
30243  * Server Validation Failed
30244  * @const 
30245  */
30246 Roo.form.Action.SERVER_INVALID = 'server';
30247  /**
30248  * Connect to Server Failed
30249  * @const 
30250  */
30251 Roo.form.Action.CONNECT_FAILURE = 'connect';
30252 /**
30253  * Reading Data from Server Failed
30254  * @const 
30255  */
30256 Roo.form.Action.LOAD_FAILURE = 'load';
30257
30258 Roo.form.Action.prototype = {
30259     type : 'default',
30260     failureType : undefined,
30261     response : undefined,
30262     result : undefined,
30263
30264     // interface method
30265     run : function(options){
30266
30267     },
30268
30269     // interface method
30270     success : function(response){
30271
30272     },
30273
30274     // interface method
30275     handleResponse : function(response){
30276
30277     },
30278
30279     // default connection failure
30280     failure : function(response){
30281         
30282         this.response = response;
30283         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30284         this.form.afterAction(this, false);
30285     },
30286
30287     processResponse : function(response){
30288         this.response = response;
30289         if(!response.responseText){
30290             return true;
30291         }
30292         this.result = this.handleResponse(response);
30293         return this.result;
30294     },
30295
30296     // utility functions used internally
30297     getUrl : function(appendParams){
30298         var url = this.options.url || this.form.url || this.form.el.dom.action;
30299         if(appendParams){
30300             var p = this.getParams();
30301             if(p){
30302                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30303             }
30304         }
30305         return url;
30306     },
30307
30308     getMethod : function(){
30309         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30310     },
30311
30312     getParams : function(){
30313         var bp = this.form.baseParams;
30314         var p = this.options.params;
30315         if(p){
30316             if(typeof p == "object"){
30317                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30318             }else if(typeof p == 'string' && bp){
30319                 p += '&' + Roo.urlEncode(bp);
30320             }
30321         }else if(bp){
30322             p = Roo.urlEncode(bp);
30323         }
30324         return p;
30325     },
30326
30327     createCallback : function(){
30328         return {
30329             success: this.success,
30330             failure: this.failure,
30331             scope: this,
30332             timeout: (this.form.timeout*1000),
30333             upload: this.form.fileUpload ? this.success : undefined
30334         };
30335     }
30336 };
30337
30338 Roo.form.Action.Submit = function(form, options){
30339     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30340 };
30341
30342 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30343     type : 'submit',
30344
30345     haveProgress : false,
30346     uploadComplete : false,
30347     
30348     // uploadProgress indicator.
30349     uploadProgress : function()
30350     {
30351         if (!this.form.progressUrl) {
30352             return;
30353         }
30354         
30355         if (!this.haveProgress) {
30356             Roo.MessageBox.progress("Uploading", "Uploading");
30357         }
30358         if (this.uploadComplete) {
30359            Roo.MessageBox.hide();
30360            return;
30361         }
30362         
30363         this.haveProgress = true;
30364    
30365         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30366         
30367         var c = new Roo.data.Connection();
30368         c.request({
30369             url : this.form.progressUrl,
30370             params: {
30371                 id : uid
30372             },
30373             method: 'GET',
30374             success : function(req){
30375                //console.log(data);
30376                 var rdata = false;
30377                 var edata;
30378                 try  {
30379                    rdata = Roo.decode(req.responseText)
30380                 } catch (e) {
30381                     Roo.log("Invalid data from server..");
30382                     Roo.log(edata);
30383                     return;
30384                 }
30385                 if (!rdata || !rdata.success) {
30386                     Roo.log(rdata);
30387                     Roo.MessageBox.alert(Roo.encode(rdata));
30388                     return;
30389                 }
30390                 var data = rdata.data;
30391                 
30392                 if (this.uploadComplete) {
30393                    Roo.MessageBox.hide();
30394                    return;
30395                 }
30396                    
30397                 if (data){
30398                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30399                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30400                     );
30401                 }
30402                 this.uploadProgress.defer(2000,this);
30403             },
30404        
30405             failure: function(data) {
30406                 Roo.log('progress url failed ');
30407                 Roo.log(data);
30408             },
30409             scope : this
30410         });
30411            
30412     },
30413     
30414     
30415     run : function()
30416     {
30417         // run get Values on the form, so it syncs any secondary forms.
30418         this.form.getValues();
30419         
30420         var o = this.options;
30421         var method = this.getMethod();
30422         var isPost = method == 'POST';
30423         if(o.clientValidation === false || this.form.isValid()){
30424             
30425             if (this.form.progressUrl) {
30426                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30427                     (new Date() * 1) + '' + Math.random());
30428                     
30429             } 
30430             
30431             
30432             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30433                 form:this.form.el.dom,
30434                 url:this.getUrl(!isPost),
30435                 method: method,
30436                 params:isPost ? this.getParams() : null,
30437                 isUpload: this.form.fileUpload,
30438                 formData : this.form.formData
30439             }));
30440             
30441             this.uploadProgress();
30442
30443         }else if (o.clientValidation !== false){ // client validation failed
30444             this.failureType = Roo.form.Action.CLIENT_INVALID;
30445             this.form.afterAction(this, false);
30446         }
30447     },
30448
30449     success : function(response)
30450     {
30451         this.uploadComplete= true;
30452         if (this.haveProgress) {
30453             Roo.MessageBox.hide();
30454         }
30455         
30456         
30457         var result = this.processResponse(response);
30458         if(result === true || result.success){
30459             this.form.afterAction(this, true);
30460             return;
30461         }
30462         if(result.errors){
30463             this.form.markInvalid(result.errors);
30464             this.failureType = Roo.form.Action.SERVER_INVALID;
30465         }
30466         this.form.afterAction(this, false);
30467     },
30468     failure : function(response)
30469     {
30470         this.uploadComplete= true;
30471         if (this.haveProgress) {
30472             Roo.MessageBox.hide();
30473         }
30474         
30475         this.response = response;
30476         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30477         this.form.afterAction(this, false);
30478     },
30479     
30480     handleResponse : function(response){
30481         if(this.form.errorReader){
30482             var rs = this.form.errorReader.read(response);
30483             var errors = [];
30484             if(rs.records){
30485                 for(var i = 0, len = rs.records.length; i < len; i++) {
30486                     var r = rs.records[i];
30487                     errors[i] = r.data;
30488                 }
30489             }
30490             if(errors.length < 1){
30491                 errors = null;
30492             }
30493             return {
30494                 success : rs.success,
30495                 errors : errors
30496             };
30497         }
30498         var ret = false;
30499         try {
30500             ret = Roo.decode(response.responseText);
30501         } catch (e) {
30502             ret = {
30503                 success: false,
30504                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30505                 errors : []
30506             };
30507         }
30508         return ret;
30509         
30510     }
30511 });
30512
30513
30514 Roo.form.Action.Load = function(form, options){
30515     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30516     this.reader = this.form.reader;
30517 };
30518
30519 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30520     type : 'load',
30521
30522     run : function(){
30523         
30524         Roo.Ajax.request(Roo.apply(
30525                 this.createCallback(), {
30526                     method:this.getMethod(),
30527                     url:this.getUrl(false),
30528                     params:this.getParams()
30529         }));
30530     },
30531
30532     success : function(response){
30533         
30534         var result = this.processResponse(response);
30535         if(result === true || !result.success || !result.data){
30536             this.failureType = Roo.form.Action.LOAD_FAILURE;
30537             this.form.afterAction(this, false);
30538             return;
30539         }
30540         this.form.clearInvalid();
30541         this.form.setValues(result.data);
30542         this.form.afterAction(this, true);
30543     },
30544
30545     handleResponse : function(response){
30546         if(this.form.reader){
30547             var rs = this.form.reader.read(response);
30548             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30549             return {
30550                 success : rs.success,
30551                 data : data
30552             };
30553         }
30554         return Roo.decode(response.responseText);
30555     }
30556 });
30557
30558 Roo.form.Action.ACTION_TYPES = {
30559     'load' : Roo.form.Action.Load,
30560     'submit' : Roo.form.Action.Submit
30561 };/*
30562  * Based on:
30563  * Ext JS Library 1.1.1
30564  * Copyright(c) 2006-2007, Ext JS, LLC.
30565  *
30566  * Originally Released Under LGPL - original licence link has changed is not relivant.
30567  *
30568  * Fork - LGPL
30569  * <script type="text/javascript">
30570  */
30571  
30572 /**
30573  * @class Roo.form.Layout
30574  * @extends Roo.Component
30575  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30576  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30577  * @constructor
30578  * @param {Object} config Configuration options
30579  */
30580 Roo.form.Layout = function(config){
30581     var xitems = [];
30582     if (config.items) {
30583         xitems = config.items;
30584         delete config.items;
30585     }
30586     Roo.form.Layout.superclass.constructor.call(this, config);
30587     this.stack = [];
30588     Roo.each(xitems, this.addxtype, this);
30589      
30590 };
30591
30592 Roo.extend(Roo.form.Layout, Roo.Component, {
30593     /**
30594      * @cfg {String/Object} autoCreate
30595      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30596      */
30597     /**
30598      * @cfg {String/Object/Function} style
30599      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30600      * a function which returns such a specification.
30601      */
30602     /**
30603      * @cfg {String} labelAlign
30604      * Valid values are "left," "top" and "right" (defaults to "left")
30605      */
30606     /**
30607      * @cfg {Number} labelWidth
30608      * Fixed width in pixels of all field labels (defaults to undefined)
30609      */
30610     /**
30611      * @cfg {Boolean} clear
30612      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30613      */
30614     clear : true,
30615     /**
30616      * @cfg {String} labelSeparator
30617      * The separator to use after field labels (defaults to ':')
30618      */
30619     labelSeparator : ':',
30620     /**
30621      * @cfg {Boolean} hideLabels
30622      * True to suppress the display of field labels in this layout (defaults to false)
30623      */
30624     hideLabels : false,
30625
30626     // private
30627     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30628     
30629     isLayout : true,
30630     
30631     // private
30632     onRender : function(ct, position){
30633         if(this.el){ // from markup
30634             this.el = Roo.get(this.el);
30635         }else {  // generate
30636             var cfg = this.getAutoCreate();
30637             this.el = ct.createChild(cfg, position);
30638         }
30639         if(this.style){
30640             this.el.applyStyles(this.style);
30641         }
30642         if(this.labelAlign){
30643             this.el.addClass('x-form-label-'+this.labelAlign);
30644         }
30645         if(this.hideLabels){
30646             this.labelStyle = "display:none";
30647             this.elementStyle = "padding-left:0;";
30648         }else{
30649             if(typeof this.labelWidth == 'number'){
30650                 this.labelStyle = "width:"+this.labelWidth+"px;";
30651                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30652             }
30653             if(this.labelAlign == 'top'){
30654                 this.labelStyle = "width:auto;";
30655                 this.elementStyle = "padding-left:0;";
30656             }
30657         }
30658         var stack = this.stack;
30659         var slen = stack.length;
30660         if(slen > 0){
30661             if(!this.fieldTpl){
30662                 var t = new Roo.Template(
30663                     '<div class="x-form-item {5}">',
30664                         '<label for="{0}" style="{2}">{1}{4}</label>',
30665                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30666                         '</div>',
30667                     '</div><div class="x-form-clear-left"></div>'
30668                 );
30669                 t.disableFormats = true;
30670                 t.compile();
30671                 Roo.form.Layout.prototype.fieldTpl = t;
30672             }
30673             for(var i = 0; i < slen; i++) {
30674                 if(stack[i].isFormField){
30675                     this.renderField(stack[i]);
30676                 }else{
30677                     this.renderComponent(stack[i]);
30678                 }
30679             }
30680         }
30681         if(this.clear){
30682             this.el.createChild({cls:'x-form-clear'});
30683         }
30684     },
30685
30686     // private
30687     renderField : function(f){
30688         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30689                f.id, //0
30690                f.fieldLabel, //1
30691                f.labelStyle||this.labelStyle||'', //2
30692                this.elementStyle||'', //3
30693                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30694                f.itemCls||this.itemCls||''  //5
30695        ], true).getPrevSibling());
30696     },
30697
30698     // private
30699     renderComponent : function(c){
30700         c.render(c.isLayout ? this.el : this.el.createChild());    
30701     },
30702     /**
30703      * Adds a object form elements (using the xtype property as the factory method.)
30704      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30705      * @param {Object} config 
30706      */
30707     addxtype : function(o)
30708     {
30709         // create the lement.
30710         o.form = this.form;
30711         var fe = Roo.factory(o, Roo.form);
30712         this.form.allItems.push(fe);
30713         this.stack.push(fe);
30714         
30715         if (fe.isFormField) {
30716             this.form.items.add(fe);
30717         }
30718          
30719         return fe;
30720     }
30721 });
30722
30723 /**
30724  * @class Roo.form.Column
30725  * @extends Roo.form.Layout
30726  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30727  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30728  * @constructor
30729  * @param {Object} config Configuration options
30730  */
30731 Roo.form.Column = function(config){
30732     Roo.form.Column.superclass.constructor.call(this, config);
30733 };
30734
30735 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30736     /**
30737      * @cfg {Number/String} width
30738      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30739      */
30740     /**
30741      * @cfg {String/Object} autoCreate
30742      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30743      */
30744
30745     // private
30746     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30747
30748     // private
30749     onRender : function(ct, position){
30750         Roo.form.Column.superclass.onRender.call(this, ct, position);
30751         if(this.width){
30752             this.el.setWidth(this.width);
30753         }
30754     }
30755 });
30756
30757
30758 /**
30759  * @class Roo.form.Row
30760  * @extends Roo.form.Layout
30761  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30762  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30763  * @constructor
30764  * @param {Object} config Configuration options
30765  */
30766
30767  
30768 Roo.form.Row = function(config){
30769     Roo.form.Row.superclass.constructor.call(this, config);
30770 };
30771  
30772 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30773       /**
30774      * @cfg {Number/String} width
30775      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30776      */
30777     /**
30778      * @cfg {Number/String} height
30779      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30780      */
30781     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30782     
30783     padWidth : 20,
30784     // private
30785     onRender : function(ct, position){
30786         //console.log('row render');
30787         if(!this.rowTpl){
30788             var t = new Roo.Template(
30789                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30790                     '<label for="{0}" style="{2}">{1}{4}</label>',
30791                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30792                     '</div>',
30793                 '</div>'
30794             );
30795             t.disableFormats = true;
30796             t.compile();
30797             Roo.form.Layout.prototype.rowTpl = t;
30798         }
30799         this.fieldTpl = this.rowTpl;
30800         
30801         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30802         var labelWidth = 100;
30803         
30804         if ((this.labelAlign != 'top')) {
30805             if (typeof this.labelWidth == 'number') {
30806                 labelWidth = this.labelWidth
30807             }
30808             this.padWidth =  20 + labelWidth;
30809             
30810         }
30811         
30812         Roo.form.Column.superclass.onRender.call(this, ct, position);
30813         if(this.width){
30814             this.el.setWidth(this.width);
30815         }
30816         if(this.height){
30817             this.el.setHeight(this.height);
30818         }
30819     },
30820     
30821     // private
30822     renderField : function(f){
30823         f.fieldEl = this.fieldTpl.append(this.el, [
30824                f.id, f.fieldLabel,
30825                f.labelStyle||this.labelStyle||'',
30826                this.elementStyle||'',
30827                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30828                f.itemCls||this.itemCls||'',
30829                f.width ? f.width + this.padWidth : 160 + this.padWidth
30830        ],true);
30831     }
30832 });
30833  
30834
30835 /**
30836  * @class Roo.form.FieldSet
30837  * @extends Roo.form.Layout
30838  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30839  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30840  * @constructor
30841  * @param {Object} config Configuration options
30842  */
30843 Roo.form.FieldSet = function(config){
30844     Roo.form.FieldSet.superclass.constructor.call(this, config);
30845 };
30846
30847 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30848     /**
30849      * @cfg {String} legend
30850      * The text to display as the legend for the FieldSet (defaults to '')
30851      */
30852     /**
30853      * @cfg {String/Object} autoCreate
30854      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30855      */
30856
30857     // private
30858     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30859
30860     // private
30861     onRender : function(ct, position){
30862         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30863         if(this.legend){
30864             this.setLegend(this.legend);
30865         }
30866     },
30867
30868     // private
30869     setLegend : function(text){
30870         if(this.rendered){
30871             this.el.child('legend').update(text);
30872         }
30873     }
30874 });/*
30875  * Based on:
30876  * Ext JS Library 1.1.1
30877  * Copyright(c) 2006-2007, Ext JS, LLC.
30878  *
30879  * Originally Released Under LGPL - original licence link has changed is not relivant.
30880  *
30881  * Fork - LGPL
30882  * <script type="text/javascript">
30883  */
30884 /**
30885  * @class Roo.form.VTypes
30886  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30887  * @static
30888  */
30889 Roo.form.VTypes = function(){
30890     // closure these in so they are only created once.
30891     var alpha = /^[a-zA-Z_]+$/;
30892     var alphanum = /^[a-zA-Z0-9_]+$/;
30893     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30894     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30895
30896     // All these messages and functions are configurable
30897     return {
30898         /**
30899          * The function used to validate email addresses
30900          * @param {String} value The email address
30901          */
30902         'email' : function(v){
30903             return email.test(v);
30904         },
30905         /**
30906          * The error text to display when the email validation function returns false
30907          * @type String
30908          */
30909         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30910         /**
30911          * The keystroke filter mask to be applied on email input
30912          * @type RegExp
30913          */
30914         'emailMask' : /[a-z0-9_\.\-@]/i,
30915
30916         /**
30917          * The function used to validate URLs
30918          * @param {String} value The URL
30919          */
30920         'url' : function(v){
30921             return url.test(v);
30922         },
30923         /**
30924          * The error text to display when the url validation function returns false
30925          * @type String
30926          */
30927         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30928         
30929         /**
30930          * The function used to validate alpha values
30931          * @param {String} value The value
30932          */
30933         'alpha' : function(v){
30934             return alpha.test(v);
30935         },
30936         /**
30937          * The error text to display when the alpha validation function returns false
30938          * @type String
30939          */
30940         'alphaText' : 'This field should only contain letters and _',
30941         /**
30942          * The keystroke filter mask to be applied on alpha input
30943          * @type RegExp
30944          */
30945         'alphaMask' : /[a-z_]/i,
30946
30947         /**
30948          * The function used to validate alphanumeric values
30949          * @param {String} value The value
30950          */
30951         'alphanum' : function(v){
30952             return alphanum.test(v);
30953         },
30954         /**
30955          * The error text to display when the alphanumeric validation function returns false
30956          * @type String
30957          */
30958         'alphanumText' : 'This field should only contain letters, numbers and _',
30959         /**
30960          * The keystroke filter mask to be applied on alphanumeric input
30961          * @type RegExp
30962          */
30963         'alphanumMask' : /[a-z0-9_]/i
30964     };
30965 }();//<script type="text/javascript">
30966
30967 /**
30968  * @class Roo.form.FCKeditor
30969  * @extends Roo.form.TextArea
30970  * Wrapper around the FCKEditor http://www.fckeditor.net
30971  * @constructor
30972  * Creates a new FCKeditor
30973  * @param {Object} config Configuration options
30974  */
30975 Roo.form.FCKeditor = function(config){
30976     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30977     this.addEvents({
30978          /**
30979          * @event editorinit
30980          * Fired when the editor is initialized - you can add extra handlers here..
30981          * @param {FCKeditor} this
30982          * @param {Object} the FCK object.
30983          */
30984         editorinit : true
30985     });
30986     
30987     
30988 };
30989 Roo.form.FCKeditor.editors = { };
30990 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30991 {
30992     //defaultAutoCreate : {
30993     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30994     //},
30995     // private
30996     /**
30997      * @cfg {Object} fck options - see fck manual for details.
30998      */
30999     fckconfig : false,
31000     
31001     /**
31002      * @cfg {Object} fck toolbar set (Basic or Default)
31003      */
31004     toolbarSet : 'Basic',
31005     /**
31006      * @cfg {Object} fck BasePath
31007      */ 
31008     basePath : '/fckeditor/',
31009     
31010     
31011     frame : false,
31012     
31013     value : '',
31014     
31015    
31016     onRender : function(ct, position)
31017     {
31018         if(!this.el){
31019             this.defaultAutoCreate = {
31020                 tag: "textarea",
31021                 style:"width:300px;height:60px;",
31022                 autocomplete: "new-password"
31023             };
31024         }
31025         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31026         /*
31027         if(this.grow){
31028             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31029             if(this.preventScrollbars){
31030                 this.el.setStyle("overflow", "hidden");
31031             }
31032             this.el.setHeight(this.growMin);
31033         }
31034         */
31035         //console.log('onrender' + this.getId() );
31036         Roo.form.FCKeditor.editors[this.getId()] = this;
31037          
31038
31039         this.replaceTextarea() ;
31040         
31041     },
31042     
31043     getEditor : function() {
31044         return this.fckEditor;
31045     },
31046     /**
31047      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31048      * @param {Mixed} value The value to set
31049      */
31050     
31051     
31052     setValue : function(value)
31053     {
31054         //console.log('setValue: ' + value);
31055         
31056         if(typeof(value) == 'undefined') { // not sure why this is happending...
31057             return;
31058         }
31059         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31060         
31061         //if(!this.el || !this.getEditor()) {
31062         //    this.value = value;
31063             //this.setValue.defer(100,this,[value]);    
31064         //    return;
31065         //} 
31066         
31067         if(!this.getEditor()) {
31068             return;
31069         }
31070         
31071         this.getEditor().SetData(value);
31072         
31073         //
31074
31075     },
31076
31077     /**
31078      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31079      * @return {Mixed} value The field value
31080      */
31081     getValue : function()
31082     {
31083         
31084         if (this.frame && this.frame.dom.style.display == 'none') {
31085             return Roo.form.FCKeditor.superclass.getValue.call(this);
31086         }
31087         
31088         if(!this.el || !this.getEditor()) {
31089            
31090            // this.getValue.defer(100,this); 
31091             return this.value;
31092         }
31093        
31094         
31095         var value=this.getEditor().GetData();
31096         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31097         return Roo.form.FCKeditor.superclass.getValue.call(this);
31098         
31099
31100     },
31101
31102     /**
31103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31104      * @return {Mixed} value The field value
31105      */
31106     getRawValue : function()
31107     {
31108         if (this.frame && this.frame.dom.style.display == 'none') {
31109             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31110         }
31111         
31112         if(!this.el || !this.getEditor()) {
31113             //this.getRawValue.defer(100,this); 
31114             return this.value;
31115             return;
31116         }
31117         
31118         
31119         
31120         var value=this.getEditor().GetData();
31121         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31122         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31123          
31124     },
31125     
31126     setSize : function(w,h) {
31127         
31128         
31129         
31130         //if (this.frame && this.frame.dom.style.display == 'none') {
31131         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31132         //    return;
31133         //}
31134         //if(!this.el || !this.getEditor()) {
31135         //    this.setSize.defer(100,this, [w,h]); 
31136         //    return;
31137         //}
31138         
31139         
31140         
31141         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31142         
31143         this.frame.dom.setAttribute('width', w);
31144         this.frame.dom.setAttribute('height', h);
31145         this.frame.setSize(w,h);
31146         
31147     },
31148     
31149     toggleSourceEdit : function(value) {
31150         
31151       
31152          
31153         this.el.dom.style.display = value ? '' : 'none';
31154         this.frame.dom.style.display = value ?  'none' : '';
31155         
31156     },
31157     
31158     
31159     focus: function(tag)
31160     {
31161         if (this.frame.dom.style.display == 'none') {
31162             return Roo.form.FCKeditor.superclass.focus.call(this);
31163         }
31164         if(!this.el || !this.getEditor()) {
31165             this.focus.defer(100,this, [tag]); 
31166             return;
31167         }
31168         
31169         
31170         
31171         
31172         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31173         this.getEditor().Focus();
31174         if (tgs.length) {
31175             if (!this.getEditor().Selection.GetSelection()) {
31176                 this.focus.defer(100,this, [tag]); 
31177                 return;
31178             }
31179             
31180             
31181             var r = this.getEditor().EditorDocument.createRange();
31182             r.setStart(tgs[0],0);
31183             r.setEnd(tgs[0],0);
31184             this.getEditor().Selection.GetSelection().removeAllRanges();
31185             this.getEditor().Selection.GetSelection().addRange(r);
31186             this.getEditor().Focus();
31187         }
31188         
31189     },
31190     
31191     
31192     
31193     replaceTextarea : function()
31194     {
31195         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31196             return ;
31197         }
31198         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31199         //{
31200             // We must check the elements firstly using the Id and then the name.
31201         var oTextarea = document.getElementById( this.getId() );
31202         
31203         var colElementsByName = document.getElementsByName( this.getId() ) ;
31204          
31205         oTextarea.style.display = 'none' ;
31206
31207         if ( oTextarea.tabIndex ) {            
31208             this.TabIndex = oTextarea.tabIndex ;
31209         }
31210         
31211         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31212         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31213         this.frame = Roo.get(this.getId() + '___Frame')
31214     },
31215     
31216     _getConfigHtml : function()
31217     {
31218         var sConfig = '' ;
31219
31220         for ( var o in this.fckconfig ) {
31221             sConfig += sConfig.length > 0  ? '&amp;' : '';
31222             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31223         }
31224
31225         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31226     },
31227     
31228     
31229     _getIFrameHtml : function()
31230     {
31231         var sFile = 'fckeditor.html' ;
31232         /* no idea what this is about..
31233         try
31234         {
31235             if ( (/fcksource=true/i).test( window.top.location.search ) )
31236                 sFile = 'fckeditor.original.html' ;
31237         }
31238         catch (e) { 
31239         */
31240
31241         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31242         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31243         
31244         
31245         var html = '<iframe id="' + this.getId() +
31246             '___Frame" src="' + sLink +
31247             '" width="' + this.width +
31248             '" height="' + this.height + '"' +
31249             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31250             ' frameborder="0" scrolling="no"></iframe>' ;
31251
31252         return html ;
31253     },
31254     
31255     _insertHtmlBefore : function( html, element )
31256     {
31257         if ( element.insertAdjacentHTML )       {
31258             // IE
31259             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31260         } else { // Gecko
31261             var oRange = document.createRange() ;
31262             oRange.setStartBefore( element ) ;
31263             var oFragment = oRange.createContextualFragment( html );
31264             element.parentNode.insertBefore( oFragment, element ) ;
31265         }
31266     }
31267     
31268     
31269   
31270     
31271     
31272     
31273     
31274
31275 });
31276
31277 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31278
31279 function FCKeditor_OnComplete(editorInstance){
31280     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31281     f.fckEditor = editorInstance;
31282     //console.log("loaded");
31283     f.fireEvent('editorinit', f, editorInstance);
31284
31285   
31286
31287  
31288
31289
31290
31291
31292
31293
31294
31295
31296
31297
31298
31299
31300
31301
31302
31303 //<script type="text/javascript">
31304 /**
31305  * @class Roo.form.GridField
31306  * @extends Roo.form.Field
31307  * Embed a grid (or editable grid into a form)
31308  * STATUS ALPHA
31309  * 
31310  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31311  * it needs 
31312  * xgrid.store = Roo.data.Store
31313  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31314  * xgrid.store.reader = Roo.data.JsonReader 
31315  * 
31316  * 
31317  * @constructor
31318  * Creates a new GridField
31319  * @param {Object} config Configuration options
31320  */
31321 Roo.form.GridField = function(config){
31322     Roo.form.GridField.superclass.constructor.call(this, config);
31323      
31324 };
31325
31326 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31327     /**
31328      * @cfg {Number} width  - used to restrict width of grid..
31329      */
31330     width : 100,
31331     /**
31332      * @cfg {Number} height - used to restrict height of grid..
31333      */
31334     height : 50,
31335      /**
31336      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31337          * 
31338          *}
31339      */
31340     xgrid : false, 
31341     /**
31342      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31343      * {tag: "input", type: "checkbox", autocomplete: "off"})
31344      */
31345    // defaultAutoCreate : { tag: 'div' },
31346     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31347     /**
31348      * @cfg {String} addTitle Text to include for adding a title.
31349      */
31350     addTitle : false,
31351     //
31352     onResize : function(){
31353         Roo.form.Field.superclass.onResize.apply(this, arguments);
31354     },
31355
31356     initEvents : function(){
31357         // Roo.form.Checkbox.superclass.initEvents.call(this);
31358         // has no events...
31359        
31360     },
31361
31362
31363     getResizeEl : function(){
31364         return this.wrap;
31365     },
31366
31367     getPositionEl : function(){
31368         return this.wrap;
31369     },
31370
31371     // private
31372     onRender : function(ct, position){
31373         
31374         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31375         var style = this.style;
31376         delete this.style;
31377         
31378         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31379         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31380         this.viewEl = this.wrap.createChild({ tag: 'div' });
31381         if (style) {
31382             this.viewEl.applyStyles(style);
31383         }
31384         if (this.width) {
31385             this.viewEl.setWidth(this.width);
31386         }
31387         if (this.height) {
31388             this.viewEl.setHeight(this.height);
31389         }
31390         //if(this.inputValue !== undefined){
31391         //this.setValue(this.value);
31392         
31393         
31394         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31395         
31396         
31397         this.grid.render();
31398         this.grid.getDataSource().on('remove', this.refreshValue, this);
31399         this.grid.getDataSource().on('update', this.refreshValue, this);
31400         this.grid.on('afteredit', this.refreshValue, this);
31401  
31402     },
31403      
31404     
31405     /**
31406      * Sets the value of the item. 
31407      * @param {String} either an object  or a string..
31408      */
31409     setValue : function(v){
31410         //this.value = v;
31411         v = v || []; // empty set..
31412         // this does not seem smart - it really only affects memoryproxy grids..
31413         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31414             var ds = this.grid.getDataSource();
31415             // assumes a json reader..
31416             var data = {}
31417             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31418             ds.loadData( data);
31419         }
31420         // clear selection so it does not get stale.
31421         if (this.grid.sm) { 
31422             this.grid.sm.clearSelections();
31423         }
31424         
31425         Roo.form.GridField.superclass.setValue.call(this, v);
31426         this.refreshValue();
31427         // should load data in the grid really....
31428     },
31429     
31430     // private
31431     refreshValue: function() {
31432          var val = [];
31433         this.grid.getDataSource().each(function(r) {
31434             val.push(r.data);
31435         });
31436         this.el.dom.value = Roo.encode(val);
31437     }
31438     
31439      
31440     
31441     
31442 });/*
31443  * Based on:
31444  * Ext JS Library 1.1.1
31445  * Copyright(c) 2006-2007, Ext JS, LLC.
31446  *
31447  * Originally Released Under LGPL - original licence link has changed is not relivant.
31448  *
31449  * Fork - LGPL
31450  * <script type="text/javascript">
31451  */
31452 /**
31453  * @class Roo.form.DisplayField
31454  * @extends Roo.form.Field
31455  * A generic Field to display non-editable data.
31456  * @cfg {Boolean} closable (true|false) default false
31457  * @constructor
31458  * Creates a new Display Field item.
31459  * @param {Object} config Configuration options
31460  */
31461 Roo.form.DisplayField = function(config){
31462     Roo.form.DisplayField.superclass.constructor.call(this, config);
31463     
31464     this.addEvents({
31465         /**
31466          * @event close
31467          * Fires after the click the close btn
31468              * @param {Roo.form.DisplayField} this
31469              */
31470         close : true
31471     });
31472 };
31473
31474 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31475     inputType:      'hidden',
31476     allowBlank:     true,
31477     readOnly:         true,
31478     
31479  
31480     /**
31481      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31482      */
31483     focusClass : undefined,
31484     /**
31485      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31486      */
31487     fieldClass: 'x-form-field',
31488     
31489      /**
31490      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31491      */
31492     valueRenderer: undefined,
31493     
31494     width: 100,
31495     /**
31496      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31497      * {tag: "input", type: "checkbox", autocomplete: "off"})
31498      */
31499      
31500  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31501  
31502     closable : false,
31503     
31504     onResize : function(){
31505         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31506         
31507     },
31508
31509     initEvents : function(){
31510         // Roo.form.Checkbox.superclass.initEvents.call(this);
31511         // has no events...
31512         
31513         if(this.closable){
31514             this.closeEl.on('click', this.onClose, this);
31515         }
31516        
31517     },
31518
31519
31520     getResizeEl : function(){
31521         return this.wrap;
31522     },
31523
31524     getPositionEl : function(){
31525         return this.wrap;
31526     },
31527
31528     // private
31529     onRender : function(ct, position){
31530         
31531         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31532         //if(this.inputValue !== undefined){
31533         this.wrap = this.el.wrap();
31534         
31535         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31536         
31537         if(this.closable){
31538             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31539         }
31540         
31541         if (this.bodyStyle) {
31542             this.viewEl.applyStyles(this.bodyStyle);
31543         }
31544         //this.viewEl.setStyle('padding', '2px');
31545         
31546         this.setValue(this.value);
31547         
31548     },
31549 /*
31550     // private
31551     initValue : Roo.emptyFn,
31552
31553   */
31554
31555         // private
31556     onClick : function(){
31557         
31558     },
31559
31560     /**
31561      * Sets the checked state of the checkbox.
31562      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31563      */
31564     setValue : function(v){
31565         this.value = v;
31566         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31567         // this might be called before we have a dom element..
31568         if (!this.viewEl) {
31569             return;
31570         }
31571         this.viewEl.dom.innerHTML = html;
31572         Roo.form.DisplayField.superclass.setValue.call(this, v);
31573
31574     },
31575     
31576     onClose : function(e)
31577     {
31578         e.preventDefault();
31579         
31580         this.fireEvent('close', this);
31581     }
31582 });/*
31583  * 
31584  * Licence- LGPL
31585  * 
31586  */
31587
31588 /**
31589  * @class Roo.form.DayPicker
31590  * @extends Roo.form.Field
31591  * A Day picker show [M] [T] [W] ....
31592  * @constructor
31593  * Creates a new Day Picker
31594  * @param {Object} config Configuration options
31595  */
31596 Roo.form.DayPicker= function(config){
31597     Roo.form.DayPicker.superclass.constructor.call(this, config);
31598      
31599 };
31600
31601 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31602     /**
31603      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31604      */
31605     focusClass : undefined,
31606     /**
31607      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31608      */
31609     fieldClass: "x-form-field",
31610    
31611     /**
31612      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31613      * {tag: "input", type: "checkbox", autocomplete: "off"})
31614      */
31615     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31616     
31617    
31618     actionMode : 'viewEl', 
31619     //
31620     // private
31621  
31622     inputType : 'hidden',
31623     
31624      
31625     inputElement: false, // real input element?
31626     basedOn: false, // ????
31627     
31628     isFormField: true, // not sure where this is needed!!!!
31629
31630     onResize : function(){
31631         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31632         if(!this.boxLabel){
31633             this.el.alignTo(this.wrap, 'c-c');
31634         }
31635     },
31636
31637     initEvents : function(){
31638         Roo.form.Checkbox.superclass.initEvents.call(this);
31639         this.el.on("click", this.onClick,  this);
31640         this.el.on("change", this.onClick,  this);
31641     },
31642
31643
31644     getResizeEl : function(){
31645         return this.wrap;
31646     },
31647
31648     getPositionEl : function(){
31649         return this.wrap;
31650     },
31651
31652     
31653     // private
31654     onRender : function(ct, position){
31655         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31656        
31657         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31658         
31659         var r1 = '<table><tr>';
31660         var r2 = '<tr class="x-form-daypick-icons">';
31661         for (var i=0; i < 7; i++) {
31662             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31663             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31664         }
31665         
31666         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31667         viewEl.select('img').on('click', this.onClick, this);
31668         this.viewEl = viewEl;   
31669         
31670         
31671         // this will not work on Chrome!!!
31672         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31673         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31674         
31675         
31676           
31677
31678     },
31679
31680     // private
31681     initValue : Roo.emptyFn,
31682
31683     /**
31684      * Returns the checked state of the checkbox.
31685      * @return {Boolean} True if checked, else false
31686      */
31687     getValue : function(){
31688         return this.el.dom.value;
31689         
31690     },
31691
31692         // private
31693     onClick : function(e){ 
31694         //this.setChecked(!this.checked);
31695         Roo.get(e.target).toggleClass('x-menu-item-checked');
31696         this.refreshValue();
31697         //if(this.el.dom.checked != this.checked){
31698         //    this.setValue(this.el.dom.checked);
31699        // }
31700     },
31701     
31702     // private
31703     refreshValue : function()
31704     {
31705         var val = '';
31706         this.viewEl.select('img',true).each(function(e,i,n)  {
31707             val += e.is(".x-menu-item-checked") ? String(n) : '';
31708         });
31709         this.setValue(val, true);
31710     },
31711
31712     /**
31713      * Sets the checked state of the checkbox.
31714      * On is always based on a string comparison between inputValue and the param.
31715      * @param {Boolean/String} value - the value to set 
31716      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31717      */
31718     setValue : function(v,suppressEvent){
31719         if (!this.el.dom) {
31720             return;
31721         }
31722         var old = this.el.dom.value ;
31723         this.el.dom.value = v;
31724         if (suppressEvent) {
31725             return ;
31726         }
31727          
31728         // update display..
31729         this.viewEl.select('img',true).each(function(e,i,n)  {
31730             
31731             var on = e.is(".x-menu-item-checked");
31732             var newv = v.indexOf(String(n)) > -1;
31733             if (on != newv) {
31734                 e.toggleClass('x-menu-item-checked');
31735             }
31736             
31737         });
31738         
31739         
31740         this.fireEvent('change', this, v, old);
31741         
31742         
31743     },
31744    
31745     // handle setting of hidden value by some other method!!?!?
31746     setFromHidden: function()
31747     {
31748         if(!this.el){
31749             return;
31750         }
31751         //console.log("SET FROM HIDDEN");
31752         //alert('setFrom hidden');
31753         this.setValue(this.el.dom.value);
31754     },
31755     
31756     onDestroy : function()
31757     {
31758         if(this.viewEl){
31759             Roo.get(this.viewEl).remove();
31760         }
31761          
31762         Roo.form.DayPicker.superclass.onDestroy.call(this);
31763     }
31764
31765 });/*
31766  * RooJS Library 1.1.1
31767  * Copyright(c) 2008-2011  Alan Knowles
31768  *
31769  * License - LGPL
31770  */
31771  
31772
31773 /**
31774  * @class Roo.form.ComboCheck
31775  * @extends Roo.form.ComboBox
31776  * A combobox for multiple select items.
31777  *
31778  * FIXME - could do with a reset button..
31779  * 
31780  * @constructor
31781  * Create a new ComboCheck
31782  * @param {Object} config Configuration options
31783  */
31784 Roo.form.ComboCheck = function(config){
31785     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31786     // should verify some data...
31787     // like
31788     // hiddenName = required..
31789     // displayField = required
31790     // valudField == required
31791     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31792     var _t = this;
31793     Roo.each(req, function(e) {
31794         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31795             throw "Roo.form.ComboCheck : missing value for: " + e;
31796         }
31797     });
31798     
31799     
31800 };
31801
31802 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31803      
31804      
31805     editable : false,
31806      
31807     selectedClass: 'x-menu-item-checked', 
31808     
31809     // private
31810     onRender : function(ct, position){
31811         var _t = this;
31812         
31813         
31814         
31815         if(!this.tpl){
31816             var cls = 'x-combo-list';
31817
31818             
31819             this.tpl =  new Roo.Template({
31820                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31821                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31822                    '<span>{' + this.displayField + '}</span>' +
31823                     '</div>' 
31824                 
31825             });
31826         }
31827  
31828         
31829         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31830         this.view.singleSelect = false;
31831         this.view.multiSelect = true;
31832         this.view.toggleSelect = true;
31833         this.pageTb.add(new Roo.Toolbar.Fill(), {
31834             
31835             text: 'Done',
31836             handler: function()
31837             {
31838                 _t.collapse();
31839             }
31840         });
31841     },
31842     
31843     onViewOver : function(e, t){
31844         // do nothing...
31845         return;
31846         
31847     },
31848     
31849     onViewClick : function(doFocus,index){
31850         return;
31851         
31852     },
31853     select: function () {
31854         //Roo.log("SELECT CALLED");
31855     },
31856      
31857     selectByValue : function(xv, scrollIntoView){
31858         var ar = this.getValueArray();
31859         var sels = [];
31860         
31861         Roo.each(ar, function(v) {
31862             if(v === undefined || v === null){
31863                 return;
31864             }
31865             var r = this.findRecord(this.valueField, v);
31866             if(r){
31867                 sels.push(this.store.indexOf(r))
31868                 
31869             }
31870         },this);
31871         this.view.select(sels);
31872         return false;
31873     },
31874     
31875     
31876     
31877     onSelect : function(record, index){
31878        // Roo.log("onselect Called");
31879        // this is only called by the clear button now..
31880         this.view.clearSelections();
31881         this.setValue('[]');
31882         if (this.value != this.valueBefore) {
31883             this.fireEvent('change', this, this.value, this.valueBefore);
31884             this.valueBefore = this.value;
31885         }
31886     },
31887     getValueArray : function()
31888     {
31889         var ar = [] ;
31890         
31891         try {
31892             //Roo.log(this.value);
31893             if (typeof(this.value) == 'undefined') {
31894                 return [];
31895             }
31896             var ar = Roo.decode(this.value);
31897             return  ar instanceof Array ? ar : []; //?? valid?
31898             
31899         } catch(e) {
31900             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31901             return [];
31902         }
31903          
31904     },
31905     expand : function ()
31906     {
31907         
31908         Roo.form.ComboCheck.superclass.expand.call(this);
31909         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31910         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31911         
31912
31913     },
31914     
31915     collapse : function(){
31916         Roo.form.ComboCheck.superclass.collapse.call(this);
31917         var sl = this.view.getSelectedIndexes();
31918         var st = this.store;
31919         var nv = [];
31920         var tv = [];
31921         var r;
31922         Roo.each(sl, function(i) {
31923             r = st.getAt(i);
31924             nv.push(r.get(this.valueField));
31925         },this);
31926         this.setValue(Roo.encode(nv));
31927         if (this.value != this.valueBefore) {
31928
31929             this.fireEvent('change', this, this.value, this.valueBefore);
31930             this.valueBefore = this.value;
31931         }
31932         
31933     },
31934     
31935     setValue : function(v){
31936         // Roo.log(v);
31937         this.value = v;
31938         
31939         var vals = this.getValueArray();
31940         var tv = [];
31941         Roo.each(vals, function(k) {
31942             var r = this.findRecord(this.valueField, k);
31943             if(r){
31944                 tv.push(r.data[this.displayField]);
31945             }else if(this.valueNotFoundText !== undefined){
31946                 tv.push( this.valueNotFoundText );
31947             }
31948         },this);
31949        // Roo.log(tv);
31950         
31951         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31952         this.hiddenField.value = v;
31953         this.value = v;
31954     }
31955     
31956 });/*
31957  * Based on:
31958  * Ext JS Library 1.1.1
31959  * Copyright(c) 2006-2007, Ext JS, LLC.
31960  *
31961  * Originally Released Under LGPL - original licence link has changed is not relivant.
31962  *
31963  * Fork - LGPL
31964  * <script type="text/javascript">
31965  */
31966  
31967 /**
31968  * @class Roo.form.Signature
31969  * @extends Roo.form.Field
31970  * Signature field.  
31971  * @constructor
31972  * 
31973  * @param {Object} config Configuration options
31974  */
31975
31976 Roo.form.Signature = function(config){
31977     Roo.form.Signature.superclass.constructor.call(this, config);
31978     
31979     this.addEvents({// not in used??
31980          /**
31981          * @event confirm
31982          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31983              * @param {Roo.form.Signature} combo This combo box
31984              */
31985         'confirm' : true,
31986         /**
31987          * @event reset
31988          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31989              * @param {Roo.form.ComboBox} combo This combo box
31990              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31991              */
31992         'reset' : true
31993     });
31994 };
31995
31996 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31997     /**
31998      * @cfg {Object} labels Label to use when rendering a form.
31999      * defaults to 
32000      * labels : { 
32001      *      clear : "Clear",
32002      *      confirm : "Confirm"
32003      *  }
32004      */
32005     labels : { 
32006         clear : "Clear",
32007         confirm : "Confirm"
32008     },
32009     /**
32010      * @cfg {Number} width The signature panel width (defaults to 300)
32011      */
32012     width: 300,
32013     /**
32014      * @cfg {Number} height The signature panel height (defaults to 100)
32015      */
32016     height : 100,
32017     /**
32018      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32019      */
32020     allowBlank : false,
32021     
32022     //private
32023     // {Object} signPanel The signature SVG panel element (defaults to {})
32024     signPanel : {},
32025     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32026     isMouseDown : false,
32027     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32028     isConfirmed : false,
32029     // {String} signatureTmp SVG mapping string (defaults to empty string)
32030     signatureTmp : '',
32031     
32032     
32033     defaultAutoCreate : { // modified by initCompnoent..
32034         tag: "input",
32035         type:"hidden"
32036     },
32037
32038     // private
32039     onRender : function(ct, position){
32040         
32041         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32042         
32043         this.wrap = this.el.wrap({
32044             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32045         });
32046         
32047         this.createToolbar(this);
32048         this.signPanel = this.wrap.createChild({
32049                 tag: 'div',
32050                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32051             }, this.el
32052         );
32053             
32054         this.svgID = Roo.id();
32055         this.svgEl = this.signPanel.createChild({
32056               xmlns : 'http://www.w3.org/2000/svg',
32057               tag : 'svg',
32058               id : this.svgID + "-svg",
32059               width: this.width,
32060               height: this.height,
32061               viewBox: '0 0 '+this.width+' '+this.height,
32062               cn : [
32063                 {
32064                     tag: "rect",
32065                     id: this.svgID + "-svg-r",
32066                     width: this.width,
32067                     height: this.height,
32068                     fill: "#ffa"
32069                 },
32070                 {
32071                     tag: "line",
32072                     id: this.svgID + "-svg-l",
32073                     x1: "0", // start
32074                     y1: (this.height*0.8), // start set the line in 80% of height
32075                     x2: this.width, // end
32076                     y2: (this.height*0.8), // end set the line in 80% of height
32077                     'stroke': "#666",
32078                     'stroke-width': "1",
32079                     'stroke-dasharray': "3",
32080                     'shape-rendering': "crispEdges",
32081                     'pointer-events': "none"
32082                 },
32083                 {
32084                     tag: "path",
32085                     id: this.svgID + "-svg-p",
32086                     'stroke': "navy",
32087                     'stroke-width': "3",
32088                     'fill': "none",
32089                     'pointer-events': 'none'
32090                 }
32091               ]
32092         });
32093         this.createSVG();
32094         this.svgBox = this.svgEl.dom.getScreenCTM();
32095     },
32096     createSVG : function(){ 
32097         var svg = this.signPanel;
32098         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32099         var t = this;
32100
32101         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32102         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32103         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32104         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32105         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32106         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32107         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32108         
32109     },
32110     isTouchEvent : function(e){
32111         return e.type.match(/^touch/);
32112     },
32113     getCoords : function (e) {
32114         var pt    = this.svgEl.dom.createSVGPoint();
32115         pt.x = e.clientX; 
32116         pt.y = e.clientY;
32117         if (this.isTouchEvent(e)) {
32118             pt.x =  e.targetTouches[0].clientX;
32119             pt.y = e.targetTouches[0].clientY;
32120         }
32121         var a = this.svgEl.dom.getScreenCTM();
32122         var b = a.inverse();
32123         var mx = pt.matrixTransform(b);
32124         return mx.x + ',' + mx.y;
32125     },
32126     //mouse event headler 
32127     down : function (e) {
32128         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32129         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32130         
32131         this.isMouseDown = true;
32132         
32133         e.preventDefault();
32134     },
32135     move : function (e) {
32136         if (this.isMouseDown) {
32137             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32138             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32139         }
32140         
32141         e.preventDefault();
32142     },
32143     up : function (e) {
32144         this.isMouseDown = false;
32145         var sp = this.signatureTmp.split(' ');
32146         
32147         if(sp.length > 1){
32148             if(!sp[sp.length-2].match(/^L/)){
32149                 sp.pop();
32150                 sp.pop();
32151                 sp.push("");
32152                 this.signatureTmp = sp.join(" ");
32153             }
32154         }
32155         if(this.getValue() != this.signatureTmp){
32156             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32157             this.isConfirmed = false;
32158         }
32159         e.preventDefault();
32160     },
32161     
32162     /**
32163      * Protected method that will not generally be called directly. It
32164      * is called when the editor creates its toolbar. Override this method if you need to
32165      * add custom toolbar buttons.
32166      * @param {HtmlEditor} editor
32167      */
32168     createToolbar : function(editor){
32169          function btn(id, toggle, handler){
32170             var xid = fid + '-'+ id ;
32171             return {
32172                 id : xid,
32173                 cmd : id,
32174                 cls : 'x-btn-icon x-edit-'+id,
32175                 enableToggle:toggle !== false,
32176                 scope: editor, // was editor...
32177                 handler:handler||editor.relayBtnCmd,
32178                 clickEvent:'mousedown',
32179                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32180                 tabIndex:-1
32181             };
32182         }
32183         
32184         
32185         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32186         this.tb = tb;
32187         this.tb.add(
32188            {
32189                 cls : ' x-signature-btn x-signature-'+id,
32190                 scope: editor, // was editor...
32191                 handler: this.reset,
32192                 clickEvent:'mousedown',
32193                 text: this.labels.clear
32194             },
32195             {
32196                  xtype : 'Fill',
32197                  xns: Roo.Toolbar
32198             }, 
32199             {
32200                 cls : '  x-signature-btn x-signature-'+id,
32201                 scope: editor, // was editor...
32202                 handler: this.confirmHandler,
32203                 clickEvent:'mousedown',
32204                 text: this.labels.confirm
32205             }
32206         );
32207     
32208     },
32209     //public
32210     /**
32211      * when user is clicked confirm then show this image.....
32212      * 
32213      * @return {String} Image Data URI
32214      */
32215     getImageDataURI : function(){
32216         var svg = this.svgEl.dom.parentNode.innerHTML;
32217         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32218         return src; 
32219     },
32220     /**
32221      * 
32222      * @return {Boolean} this.isConfirmed
32223      */
32224     getConfirmed : function(){
32225         return this.isConfirmed;
32226     },
32227     /**
32228      * 
32229      * @return {Number} this.width
32230      */
32231     getWidth : function(){
32232         return this.width;
32233     },
32234     /**
32235      * 
32236      * @return {Number} this.height
32237      */
32238     getHeight : function(){
32239         return this.height;
32240     },
32241     // private
32242     getSignature : function(){
32243         return this.signatureTmp;
32244     },
32245     // private
32246     reset : function(){
32247         this.signatureTmp = '';
32248         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32249         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32250         this.isConfirmed = false;
32251         Roo.form.Signature.superclass.reset.call(this);
32252     },
32253     setSignature : function(s){
32254         this.signatureTmp = s;
32255         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32256         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32257         this.setValue(s);
32258         this.isConfirmed = false;
32259         Roo.form.Signature.superclass.reset.call(this);
32260     }, 
32261     test : function(){
32262 //        Roo.log(this.signPanel.dom.contentWindow.up())
32263     },
32264     //private
32265     setConfirmed : function(){
32266         
32267         
32268         
32269 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32270     },
32271     // private
32272     confirmHandler : function(){
32273         if(!this.getSignature()){
32274             return;
32275         }
32276         
32277         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32278         this.setValue(this.getSignature());
32279         this.isConfirmed = true;
32280         
32281         this.fireEvent('confirm', this);
32282     },
32283     // private
32284     // Subclasses should provide the validation implementation by overriding this
32285     validateValue : function(value){
32286         if(this.allowBlank){
32287             return true;
32288         }
32289         
32290         if(this.isConfirmed){
32291             return true;
32292         }
32293         return false;
32294     }
32295 });/*
32296  * Based on:
32297  * Ext JS Library 1.1.1
32298  * Copyright(c) 2006-2007, Ext JS, LLC.
32299  *
32300  * Originally Released Under LGPL - original licence link has changed is not relivant.
32301  *
32302  * Fork - LGPL
32303  * <script type="text/javascript">
32304  */
32305  
32306
32307 /**
32308  * @class Roo.form.ComboBox
32309  * @extends Roo.form.TriggerField
32310  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32311  * @constructor
32312  * Create a new ComboBox.
32313  * @param {Object} config Configuration options
32314  */
32315 Roo.form.Select = function(config){
32316     Roo.form.Select.superclass.constructor.call(this, config);
32317      
32318 };
32319
32320 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32321     /**
32322      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32323      */
32324     /**
32325      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32326      * rendering into an Roo.Editor, defaults to false)
32327      */
32328     /**
32329      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32330      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32331      */
32332     /**
32333      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32334      */
32335     /**
32336      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32337      * the dropdown list (defaults to undefined, with no header element)
32338      */
32339
32340      /**
32341      * @cfg {String/Roo.Template} tpl The template to use to render the output
32342      */
32343      
32344     // private
32345     defaultAutoCreate : {tag: "select"  },
32346     /**
32347      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32348      */
32349     listWidth: undefined,
32350     /**
32351      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32352      * mode = 'remote' or 'text' if mode = 'local')
32353      */
32354     displayField: undefined,
32355     /**
32356      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32357      * mode = 'remote' or 'value' if mode = 'local'). 
32358      * Note: use of a valueField requires the user make a selection
32359      * in order for a value to be mapped.
32360      */
32361     valueField: undefined,
32362     
32363     
32364     /**
32365      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32366      * field's data value (defaults to the underlying DOM element's name)
32367      */
32368     hiddenName: undefined,
32369     /**
32370      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32371      */
32372     listClass: '',
32373     /**
32374      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32375      */
32376     selectedClass: 'x-combo-selected',
32377     /**
32378      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32380      * which displays a downward arrow icon).
32381      */
32382     triggerClass : 'x-form-arrow-trigger',
32383     /**
32384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32385      */
32386     shadow:'sides',
32387     /**
32388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32389      * anchor positions (defaults to 'tl-bl')
32390      */
32391     listAlign: 'tl-bl?',
32392     /**
32393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32394      */
32395     maxHeight: 300,
32396     /**
32397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32398      * query specified by the allQuery config option (defaults to 'query')
32399      */
32400     triggerAction: 'query',
32401     /**
32402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32403      * (defaults to 4, does not apply if editable = false)
32404      */
32405     minChars : 4,
32406     /**
32407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32409      */
32410     typeAhead: false,
32411     /**
32412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32414      */
32415     queryDelay: 500,
32416     /**
32417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32419      */
32420     pageSize: 0,
32421     /**
32422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32423      * when editable = true (defaults to false)
32424      */
32425     selectOnFocus:false,
32426     /**
32427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32428      */
32429     queryParam: 'query',
32430     /**
32431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32432      * when mode = 'remote' (defaults to 'Loading...')
32433      */
32434     loadingText: 'Loading...',
32435     /**
32436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32437      */
32438     resizable: false,
32439     /**
32440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32441      */
32442     handleHeight : 8,
32443     /**
32444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32445      * traditional select (defaults to true)
32446      */
32447     editable: true,
32448     /**
32449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32450      */
32451     allQuery: '',
32452     /**
32453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32454      */
32455     mode: 'remote',
32456     /**
32457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32458      * listWidth has a higher value)
32459      */
32460     minListWidth : 70,
32461     /**
32462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32463      * allow the user to set arbitrary text into the field (defaults to false)
32464      */
32465     forceSelection:false,
32466     /**
32467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32468      * if typeAhead = true (defaults to 250)
32469      */
32470     typeAheadDelay : 250,
32471     /**
32472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32474      */
32475     valueNotFoundText : undefined,
32476     
32477     /**
32478      * @cfg {String} defaultValue The value displayed after loading the store.
32479      */
32480     defaultValue: '',
32481     
32482     /**
32483      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32484      */
32485     blockFocus : false,
32486     
32487     /**
32488      * @cfg {Boolean} disableClear Disable showing of clear button.
32489      */
32490     disableClear : false,
32491     /**
32492      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32493      */
32494     alwaysQuery : false,
32495     
32496     //private
32497     addicon : false,
32498     editicon: false,
32499     
32500     // element that contains real text value.. (when hidden is used..)
32501      
32502     // private
32503     onRender : function(ct, position){
32504         Roo.form.Field.prototype.onRender.call(this, ct, position);
32505         
32506         if(this.store){
32507             this.store.on('beforeload', this.onBeforeLoad, this);
32508             this.store.on('load', this.onLoad, this);
32509             this.store.on('loadexception', this.onLoadException, this);
32510             this.store.load({});
32511         }
32512         
32513         
32514         
32515     },
32516
32517     // private
32518     initEvents : function(){
32519         //Roo.form.ComboBox.superclass.initEvents.call(this);
32520  
32521     },
32522
32523     onDestroy : function(){
32524        
32525         if(this.store){
32526             this.store.un('beforeload', this.onBeforeLoad, this);
32527             this.store.un('load', this.onLoad, this);
32528             this.store.un('loadexception', this.onLoadException, this);
32529         }
32530         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32531     },
32532
32533     // private
32534     fireKey : function(e){
32535         if(e.isNavKeyPress() && !this.list.isVisible()){
32536             this.fireEvent("specialkey", this, e);
32537         }
32538     },
32539
32540     // private
32541     onResize: function(w, h){
32542         
32543         return; 
32544     
32545         
32546     },
32547
32548     /**
32549      * Allow or prevent the user from directly editing the field text.  If false is passed,
32550      * the user will only be able to select from the items defined in the dropdown list.  This method
32551      * is the runtime equivalent of setting the 'editable' config option at config time.
32552      * @param {Boolean} value True to allow the user to directly edit the field text
32553      */
32554     setEditable : function(value){
32555          
32556     },
32557
32558     // private
32559     onBeforeLoad : function(){
32560         
32561         Roo.log("Select before load");
32562         return;
32563     
32564         this.innerList.update(this.loadingText ?
32565                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32566         //this.restrictHeight();
32567         this.selectedIndex = -1;
32568     },
32569
32570     // private
32571     onLoad : function(){
32572
32573     
32574         var dom = this.el.dom;
32575         dom.innerHTML = '';
32576          var od = dom.ownerDocument;
32577          
32578         if (this.emptyText) {
32579             var op = od.createElement('option');
32580             op.setAttribute('value', '');
32581             op.innerHTML = String.format('{0}', this.emptyText);
32582             dom.appendChild(op);
32583         }
32584         if(this.store.getCount() > 0){
32585            
32586             var vf = this.valueField;
32587             var df = this.displayField;
32588             this.store.data.each(function(r) {
32589                 // which colmsn to use... testing - cdoe / title..
32590                 var op = od.createElement('option');
32591                 op.setAttribute('value', r.data[vf]);
32592                 op.innerHTML = String.format('{0}', r.data[df]);
32593                 dom.appendChild(op);
32594             });
32595             if (typeof(this.defaultValue != 'undefined')) {
32596                 this.setValue(this.defaultValue);
32597             }
32598             
32599              
32600         }else{
32601             //this.onEmptyResults();
32602         }
32603         //this.el.focus();
32604     },
32605     // private
32606     onLoadException : function()
32607     {
32608         dom.innerHTML = '';
32609             
32610         Roo.log("Select on load exception");
32611         return;
32612     
32613         this.collapse();
32614         Roo.log(this.store.reader.jsonData);
32615         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32616             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32617         }
32618         
32619         
32620     },
32621     // private
32622     onTypeAhead : function(){
32623          
32624     },
32625
32626     // private
32627     onSelect : function(record, index){
32628         Roo.log('on select?');
32629         return;
32630         if(this.fireEvent('beforeselect', this, record, index) !== false){
32631             this.setFromData(index > -1 ? record.data : false);
32632             this.collapse();
32633             this.fireEvent('select', this, record, index);
32634         }
32635     },
32636
32637     /**
32638      * Returns the currently selected field value or empty string if no value is set.
32639      * @return {String} value The selected value
32640      */
32641     getValue : function(){
32642         var dom = this.el.dom;
32643         this.value = dom.options[dom.selectedIndex].value;
32644         return this.value;
32645         
32646     },
32647
32648     /**
32649      * Clears any text/value currently set in the field
32650      */
32651     clearValue : function(){
32652         this.value = '';
32653         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32654         
32655     },
32656
32657     /**
32658      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32659      * will be displayed in the field.  If the value does not match the data value of an existing item,
32660      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32661      * Otherwise the field will be blank (although the value will still be set).
32662      * @param {String} value The value to match
32663      */
32664     setValue : function(v){
32665         var d = this.el.dom;
32666         for (var i =0; i < d.options.length;i++) {
32667             if (v == d.options[i].value) {
32668                 d.selectedIndex = i;
32669                 this.value = v;
32670                 return;
32671             }
32672         }
32673         this.clearValue();
32674     },
32675     /**
32676      * @property {Object} the last set data for the element
32677      */
32678     
32679     lastData : false,
32680     /**
32681      * Sets the value of the field based on a object which is related to the record format for the store.
32682      * @param {Object} value the value to set as. or false on reset?
32683      */
32684     setFromData : function(o){
32685         Roo.log('setfrom data?');
32686          
32687         
32688         
32689     },
32690     // private
32691     reset : function(){
32692         this.clearValue();
32693     },
32694     // private
32695     findRecord : function(prop, value){
32696         
32697         return false;
32698     
32699         var record;
32700         if(this.store.getCount() > 0){
32701             this.store.each(function(r){
32702                 if(r.data[prop] == value){
32703                     record = r;
32704                     return false;
32705                 }
32706                 return true;
32707             });
32708         }
32709         return record;
32710     },
32711     
32712     getName: function()
32713     {
32714         // returns hidden if it's set..
32715         if (!this.rendered) {return ''};
32716         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32717         
32718     },
32719      
32720
32721     
32722
32723     // private
32724     onEmptyResults : function(){
32725         Roo.log('empty results');
32726         //this.collapse();
32727     },
32728
32729     /**
32730      * Returns true if the dropdown list is expanded, else false.
32731      */
32732     isExpanded : function(){
32733         return false;
32734     },
32735
32736     /**
32737      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32738      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32739      * @param {String} value The data value of the item to select
32740      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32741      * selected item if it is not currently in view (defaults to true)
32742      * @return {Boolean} True if the value matched an item in the list, else false
32743      */
32744     selectByValue : function(v, scrollIntoView){
32745         Roo.log('select By Value');
32746         return false;
32747     
32748         if(v !== undefined && v !== null){
32749             var r = this.findRecord(this.valueField || this.displayField, v);
32750             if(r){
32751                 this.select(this.store.indexOf(r), scrollIntoView);
32752                 return true;
32753             }
32754         }
32755         return false;
32756     },
32757
32758     /**
32759      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32760      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32761      * @param {Number} index The zero-based index of the list item to select
32762      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32763      * selected item if it is not currently in view (defaults to true)
32764      */
32765     select : function(index, scrollIntoView){
32766         Roo.log('select ');
32767         return  ;
32768         
32769         this.selectedIndex = index;
32770         this.view.select(index);
32771         if(scrollIntoView !== false){
32772             var el = this.view.getNode(index);
32773             if(el){
32774                 this.innerList.scrollChildIntoView(el, false);
32775             }
32776         }
32777     },
32778
32779       
32780
32781     // private
32782     validateBlur : function(){
32783         
32784         return;
32785         
32786     },
32787
32788     // private
32789     initQuery : function(){
32790         this.doQuery(this.getRawValue());
32791     },
32792
32793     // private
32794     doForce : function(){
32795         if(this.el.dom.value.length > 0){
32796             this.el.dom.value =
32797                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32798              
32799         }
32800     },
32801
32802     /**
32803      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32804      * query allowing the query action to be canceled if needed.
32805      * @param {String} query The SQL query to execute
32806      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32807      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32808      * saved in the current store (defaults to false)
32809      */
32810     doQuery : function(q, forceAll){
32811         
32812         Roo.log('doQuery?');
32813         if(q === undefined || q === null){
32814             q = '';
32815         }
32816         var qe = {
32817             query: q,
32818             forceAll: forceAll,
32819             combo: this,
32820             cancel:false
32821         };
32822         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32823             return false;
32824         }
32825         q = qe.query;
32826         forceAll = qe.forceAll;
32827         if(forceAll === true || (q.length >= this.minChars)){
32828             if(this.lastQuery != q || this.alwaysQuery){
32829                 this.lastQuery = q;
32830                 if(this.mode == 'local'){
32831                     this.selectedIndex = -1;
32832                     if(forceAll){
32833                         this.store.clearFilter();
32834                     }else{
32835                         this.store.filter(this.displayField, q);
32836                     }
32837                     this.onLoad();
32838                 }else{
32839                     this.store.baseParams[this.queryParam] = q;
32840                     this.store.load({
32841                         params: this.getParams(q)
32842                     });
32843                     this.expand();
32844                 }
32845             }else{
32846                 this.selectedIndex = -1;
32847                 this.onLoad();   
32848             }
32849         }
32850     },
32851
32852     // private
32853     getParams : function(q){
32854         var p = {};
32855         //p[this.queryParam] = q;
32856         if(this.pageSize){
32857             p.start = 0;
32858             p.limit = this.pageSize;
32859         }
32860         return p;
32861     },
32862
32863     /**
32864      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32865      */
32866     collapse : function(){
32867         
32868     },
32869
32870     // private
32871     collapseIf : function(e){
32872         
32873     },
32874
32875     /**
32876      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32877      */
32878     expand : function(){
32879         
32880     } ,
32881
32882     // private
32883      
32884
32885     /** 
32886     * @cfg {Boolean} grow 
32887     * @hide 
32888     */
32889     /** 
32890     * @cfg {Number} growMin 
32891     * @hide 
32892     */
32893     /** 
32894     * @cfg {Number} growMax 
32895     * @hide 
32896     */
32897     /**
32898      * @hide
32899      * @method autoSize
32900      */
32901     
32902     setWidth : function()
32903     {
32904         
32905     },
32906     getResizeEl : function(){
32907         return this.el;
32908     }
32909 });//<script type="text/javasscript">
32910  
32911
32912 /**
32913  * @class Roo.DDView
32914  * A DnD enabled version of Roo.View.
32915  * @param {Element/String} container The Element in which to create the View.
32916  * @param {String} tpl The template string used to create the markup for each element of the View
32917  * @param {Object} config The configuration properties. These include all the config options of
32918  * {@link Roo.View} plus some specific to this class.<br>
32919  * <p>
32920  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32921  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32922  * <p>
32923  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32924 .x-view-drag-insert-above {
32925         border-top:1px dotted #3366cc;
32926 }
32927 .x-view-drag-insert-below {
32928         border-bottom:1px dotted #3366cc;
32929 }
32930 </code></pre>
32931  * 
32932  */
32933  
32934 Roo.DDView = function(container, tpl, config) {
32935     Roo.DDView.superclass.constructor.apply(this, arguments);
32936     this.getEl().setStyle("outline", "0px none");
32937     this.getEl().unselectable();
32938     if (this.dragGroup) {
32939         this.setDraggable(this.dragGroup.split(","));
32940     }
32941     if (this.dropGroup) {
32942         this.setDroppable(this.dropGroup.split(","));
32943     }
32944     if (this.deletable) {
32945         this.setDeletable();
32946     }
32947     this.isDirtyFlag = false;
32948         this.addEvents({
32949                 "drop" : true
32950         });
32951 };
32952
32953 Roo.extend(Roo.DDView, Roo.View, {
32954 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32955 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32956 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32957 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32958
32959         isFormField: true,
32960
32961         reset: Roo.emptyFn,
32962         
32963         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32964
32965         validate: function() {
32966                 return true;
32967         },
32968         
32969         destroy: function() {
32970                 this.purgeListeners();
32971                 this.getEl.removeAllListeners();
32972                 this.getEl().remove();
32973                 if (this.dragZone) {
32974                         if (this.dragZone.destroy) {
32975                                 this.dragZone.destroy();
32976                         }
32977                 }
32978                 if (this.dropZone) {
32979                         if (this.dropZone.destroy) {
32980                                 this.dropZone.destroy();
32981                         }
32982                 }
32983         },
32984
32985 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32986         getName: function() {
32987                 return this.name;
32988         },
32989
32990 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32991         setValue: function(v) {
32992                 if (!this.store) {
32993                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32994                 }
32995                 var data = {};
32996                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32997                 this.store.proxy = new Roo.data.MemoryProxy(data);
32998                 this.store.load();
32999         },
33000
33001 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33002         getValue: function() {
33003                 var result = '(';
33004                 this.store.each(function(rec) {
33005                         result += rec.id + ',';
33006                 });
33007                 return result.substr(0, result.length - 1) + ')';
33008         },
33009         
33010         getIds: function() {
33011                 var i = 0, result = new Array(this.store.getCount());
33012                 this.store.each(function(rec) {
33013                         result[i++] = rec.id;
33014                 });
33015                 return result;
33016         },
33017         
33018         isDirty: function() {
33019                 return this.isDirtyFlag;
33020         },
33021
33022 /**
33023  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33024  *      whole Element becomes the target, and this causes the drop gesture to append.
33025  */
33026     getTargetFromEvent : function(e) {
33027                 var target = e.getTarget();
33028                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33029                 target = target.parentNode;
33030                 }
33031                 if (!target) {
33032                         target = this.el.dom.lastChild || this.el.dom;
33033                 }
33034                 return target;
33035     },
33036
33037 /**
33038  *      Create the drag data which consists of an object which has the property "ddel" as
33039  *      the drag proxy element. 
33040  */
33041     getDragData : function(e) {
33042         var target = this.findItemFromChild(e.getTarget());
33043                 if(target) {
33044                         this.handleSelection(e);
33045                         var selNodes = this.getSelectedNodes();
33046             var dragData = {
33047                 source: this,
33048                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33049                 nodes: selNodes,
33050                 records: []
33051                         };
33052                         var selectedIndices = this.getSelectedIndexes();
33053                         for (var i = 0; i < selectedIndices.length; i++) {
33054                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33055                         }
33056                         if (selNodes.length == 1) {
33057                                 dragData.ddel = target.cloneNode(true); // the div element
33058                         } else {
33059                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33060                                 div.className = 'multi-proxy';
33061                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33062                                         div.appendChild(selNodes[i].cloneNode(true));
33063                                 }
33064                                 dragData.ddel = div;
33065                         }
33066             //console.log(dragData)
33067             //console.log(dragData.ddel.innerHTML)
33068                         return dragData;
33069                 }
33070         //console.log('nodragData')
33071                 return false;
33072     },
33073     
33074 /**     Specify to which ddGroup items in this DDView may be dragged. */
33075     setDraggable: function(ddGroup) {
33076         if (ddGroup instanceof Array) {
33077                 Roo.each(ddGroup, this.setDraggable, this);
33078                 return;
33079         }
33080         if (this.dragZone) {
33081                 this.dragZone.addToGroup(ddGroup);
33082         } else {
33083                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33084                                 containerScroll: true,
33085                                 ddGroup: ddGroup 
33086
33087                         });
33088 //                      Draggability implies selection. DragZone's mousedown selects the element.
33089                         if (!this.multiSelect) { this.singleSelect = true; }
33090
33091 //                      Wire the DragZone's handlers up to methods in *this*
33092                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33093                 }
33094     },
33095
33096 /**     Specify from which ddGroup this DDView accepts drops. */
33097     setDroppable: function(ddGroup) {
33098         if (ddGroup instanceof Array) {
33099                 Roo.each(ddGroup, this.setDroppable, this);
33100                 return;
33101         }
33102         if (this.dropZone) {
33103                 this.dropZone.addToGroup(ddGroup);
33104         } else {
33105                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33106                                 containerScroll: true,
33107                                 ddGroup: ddGroup
33108                         });
33109
33110 //                      Wire the DropZone's handlers up to methods in *this*
33111                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33112                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33113                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33114                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33115                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33116                 }
33117     },
33118
33119 /**     Decide whether to drop above or below a View node. */
33120     getDropPoint : function(e, n, dd){
33121         if (n == this.el.dom) { return "above"; }
33122                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33123                 var c = t + (b - t) / 2;
33124                 var y = Roo.lib.Event.getPageY(e);
33125                 if(y <= c) {
33126                         return "above";
33127                 }else{
33128                         return "below";
33129                 }
33130     },
33131
33132     onNodeEnter : function(n, dd, e, data){
33133                 return false;
33134     },
33135     
33136     onNodeOver : function(n, dd, e, data){
33137                 var pt = this.getDropPoint(e, n, dd);
33138                 // set the insert point style on the target node
33139                 var dragElClass = this.dropNotAllowed;
33140                 if (pt) {
33141                         var targetElClass;
33142                         if (pt == "above"){
33143                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33144                                 targetElClass = "x-view-drag-insert-above";
33145                         } else {
33146                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33147                                 targetElClass = "x-view-drag-insert-below";
33148                         }
33149                         if (this.lastInsertClass != targetElClass){
33150                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33151                                 this.lastInsertClass = targetElClass;
33152                         }
33153                 }
33154                 return dragElClass;
33155         },
33156
33157     onNodeOut : function(n, dd, e, data){
33158                 this.removeDropIndicators(n);
33159     },
33160
33161     onNodeDrop : function(n, dd, e, data){
33162         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33163                 return false;
33164         }
33165         var pt = this.getDropPoint(e, n, dd);
33166                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33167                 if (pt == "below") { insertAt++; }
33168                 for (var i = 0; i < data.records.length; i++) {
33169                         var r = data.records[i];
33170                         var dup = this.store.getById(r.id);
33171                         if (dup && (dd != this.dragZone)) {
33172                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33173                         } else {
33174                                 if (data.copy) {
33175                                         this.store.insert(insertAt++, r.copy());
33176                                 } else {
33177                                         data.source.isDirtyFlag = true;
33178                                         r.store.remove(r);
33179                                         this.store.insert(insertAt++, r);
33180                                 }
33181                                 this.isDirtyFlag = true;
33182                         }
33183                 }
33184                 this.dragZone.cachedTarget = null;
33185                 return true;
33186     },
33187
33188     removeDropIndicators : function(n){
33189                 if(n){
33190                         Roo.fly(n).removeClass([
33191                                 "x-view-drag-insert-above",
33192                                 "x-view-drag-insert-below"]);
33193                         this.lastInsertClass = "_noclass";
33194                 }
33195     },
33196
33197 /**
33198  *      Utility method. Add a delete option to the DDView's context menu.
33199  *      @param {String} imageUrl The URL of the "delete" icon image.
33200  */
33201         setDeletable: function(imageUrl) {
33202                 if (!this.singleSelect && !this.multiSelect) {
33203                         this.singleSelect = true;
33204                 }
33205                 var c = this.getContextMenu();
33206                 this.contextMenu.on("itemclick", function(item) {
33207                         switch (item.id) {
33208                                 case "delete":
33209                                         this.remove(this.getSelectedIndexes());
33210                                         break;
33211                         }
33212                 }, this);
33213                 this.contextMenu.add({
33214                         icon: imageUrl,
33215                         id: "delete",
33216                         text: 'Delete'
33217                 });
33218         },
33219         
33220 /**     Return the context menu for this DDView. */
33221         getContextMenu: function() {
33222                 if (!this.contextMenu) {
33223 //                      Create the View's context menu
33224                         this.contextMenu = new Roo.menu.Menu({
33225                                 id: this.id + "-contextmenu"
33226                         });
33227                         this.el.on("contextmenu", this.showContextMenu, this);
33228                 }
33229                 return this.contextMenu;
33230         },
33231         
33232         disableContextMenu: function() {
33233                 if (this.contextMenu) {
33234                         this.el.un("contextmenu", this.showContextMenu, this);
33235                 }
33236         },
33237
33238         showContextMenu: function(e, item) {
33239         item = this.findItemFromChild(e.getTarget());
33240                 if (item) {
33241                         e.stopEvent();
33242                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33243                         this.contextMenu.showAt(e.getXY());
33244             }
33245     },
33246
33247 /**
33248  *      Remove {@link Roo.data.Record}s at the specified indices.
33249  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33250  */
33251     remove: function(selectedIndices) {
33252                 selectedIndices = [].concat(selectedIndices);
33253                 for (var i = 0; i < selectedIndices.length; i++) {
33254                         var rec = this.store.getAt(selectedIndices[i]);
33255                         this.store.remove(rec);
33256                 }
33257     },
33258
33259 /**
33260  *      Double click fires the event, but also, if this is draggable, and there is only one other
33261  *      related DropZone, it transfers the selected node.
33262  */
33263     onDblClick : function(e){
33264         var item = this.findItemFromChild(e.getTarget());
33265         if(item){
33266             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33267                 return false;
33268             }
33269             if (this.dragGroup) {
33270                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33271                     while (targets.indexOf(this.dropZone) > -1) {
33272                             targets.remove(this.dropZone);
33273                                 }
33274                     if (targets.length == 1) {
33275                                         this.dragZone.cachedTarget = null;
33276                         var el = Roo.get(targets[0].getEl());
33277                         var box = el.getBox(true);
33278                         targets[0].onNodeDrop(el.dom, {
33279                                 target: el.dom,
33280                                 xy: [box.x, box.y + box.height - 1]
33281                         }, null, this.getDragData(e));
33282                     }
33283                 }
33284         }
33285     },
33286     
33287     handleSelection: function(e) {
33288                 this.dragZone.cachedTarget = null;
33289         var item = this.findItemFromChild(e.getTarget());
33290         if (!item) {
33291                 this.clearSelections(true);
33292                 return;
33293         }
33294                 if (item && (this.multiSelect || this.singleSelect)){
33295                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33296                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33297                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33298                                 this.unselect(item);
33299                         } else {
33300                                 this.select(item, this.multiSelect && e.ctrlKey);
33301                                 this.lastSelection = item;
33302                         }
33303                 }
33304     },
33305
33306     onItemClick : function(item, index, e){
33307                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33308                         return false;
33309                 }
33310                 return true;
33311     },
33312
33313     unselect : function(nodeInfo, suppressEvent){
33314                 var node = this.getNode(nodeInfo);
33315                 if(node && this.isSelected(node)){
33316                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33317                                 Roo.fly(node).removeClass(this.selectedClass);
33318                                 this.selections.remove(node);
33319                                 if(!suppressEvent){
33320                                         this.fireEvent("selectionchange", this, this.selections);
33321                                 }
33322                         }
33323                 }
33324     }
33325 });
33326 /*
33327  * Based on:
33328  * Ext JS Library 1.1.1
33329  * Copyright(c) 2006-2007, Ext JS, LLC.
33330  *
33331  * Originally Released Under LGPL - original licence link has changed is not relivant.
33332  *
33333  * Fork - LGPL
33334  * <script type="text/javascript">
33335  */
33336  
33337 /**
33338  * @class Roo.LayoutManager
33339  * @extends Roo.util.Observable
33340  * Base class for layout managers.
33341  */
33342 Roo.LayoutManager = function(container, config){
33343     Roo.LayoutManager.superclass.constructor.call(this);
33344     this.el = Roo.get(container);
33345     // ie scrollbar fix
33346     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33347         document.body.scroll = "no";
33348     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33349         this.el.position('relative');
33350     }
33351     this.id = this.el.id;
33352     this.el.addClass("x-layout-container");
33353     /** false to disable window resize monitoring @type Boolean */
33354     this.monitorWindowResize = true;
33355     this.regions = {};
33356     this.addEvents({
33357         /**
33358          * @event layout
33359          * Fires when a layout is performed. 
33360          * @param {Roo.LayoutManager} this
33361          */
33362         "layout" : true,
33363         /**
33364          * @event regionresized
33365          * Fires when the user resizes a region. 
33366          * @param {Roo.LayoutRegion} region The resized region
33367          * @param {Number} newSize The new size (width for east/west, height for north/south)
33368          */
33369         "regionresized" : true,
33370         /**
33371          * @event regioncollapsed
33372          * Fires when a region is collapsed. 
33373          * @param {Roo.LayoutRegion} region The collapsed region
33374          */
33375         "regioncollapsed" : true,
33376         /**
33377          * @event regionexpanded
33378          * Fires when a region is expanded.  
33379          * @param {Roo.LayoutRegion} region The expanded region
33380          */
33381         "regionexpanded" : true
33382     });
33383     this.updating = false;
33384     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33385 };
33386
33387 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33388     /**
33389      * Returns true if this layout is currently being updated
33390      * @return {Boolean}
33391      */
33392     isUpdating : function(){
33393         return this.updating; 
33394     },
33395     
33396     /**
33397      * Suspend the LayoutManager from doing auto-layouts while
33398      * making multiple add or remove calls
33399      */
33400     beginUpdate : function(){
33401         this.updating = true;    
33402     },
33403     
33404     /**
33405      * Restore auto-layouts and optionally disable the manager from performing a layout
33406      * @param {Boolean} noLayout true to disable a layout update 
33407      */
33408     endUpdate : function(noLayout){
33409         this.updating = false;
33410         if(!noLayout){
33411             this.layout();
33412         }    
33413     },
33414     
33415     layout: function(){
33416         
33417     },
33418     
33419     onRegionResized : function(region, newSize){
33420         this.fireEvent("regionresized", region, newSize);
33421         this.layout();
33422     },
33423     
33424     onRegionCollapsed : function(region){
33425         this.fireEvent("regioncollapsed", region);
33426     },
33427     
33428     onRegionExpanded : function(region){
33429         this.fireEvent("regionexpanded", region);
33430     },
33431         
33432     /**
33433      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33434      * performs box-model adjustments.
33435      * @return {Object} The size as an object {width: (the width), height: (the height)}
33436      */
33437     getViewSize : function(){
33438         var size;
33439         if(this.el.dom != document.body){
33440             size = this.el.getSize();
33441         }else{
33442             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33443         }
33444         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33445         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33446         return size;
33447     },
33448     
33449     /**
33450      * Returns the Element this layout is bound to.
33451      * @return {Roo.Element}
33452      */
33453     getEl : function(){
33454         return this.el;
33455     },
33456     
33457     /**
33458      * Returns the specified region.
33459      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33460      * @return {Roo.LayoutRegion}
33461      */
33462     getRegion : function(target){
33463         return this.regions[target.toLowerCase()];
33464     },
33465     
33466     onWindowResize : function(){
33467         if(this.monitorWindowResize){
33468             this.layout();
33469         }
33470     }
33471 });/*
33472  * Based on:
33473  * Ext JS Library 1.1.1
33474  * Copyright(c) 2006-2007, Ext JS, LLC.
33475  *
33476  * Originally Released Under LGPL - original licence link has changed is not relivant.
33477  *
33478  * Fork - LGPL
33479  * <script type="text/javascript">
33480  */
33481 /**
33482  * @class Roo.BorderLayout
33483  * @extends Roo.LayoutManager
33484  * @children Roo.ContentPanel
33485  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33486  * please see: <br><br>
33487  * <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>
33488  * <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>
33489  * Example:
33490  <pre><code>
33491  var layout = new Roo.BorderLayout(document.body, {
33492     north: {
33493         initialSize: 25,
33494         titlebar: false
33495     },
33496     west: {
33497         split:true,
33498         initialSize: 200,
33499         minSize: 175,
33500         maxSize: 400,
33501         titlebar: true,
33502         collapsible: true
33503     },
33504     east: {
33505         split:true,
33506         initialSize: 202,
33507         minSize: 175,
33508         maxSize: 400,
33509         titlebar: true,
33510         collapsible: true
33511     },
33512     south: {
33513         split:true,
33514         initialSize: 100,
33515         minSize: 100,
33516         maxSize: 200,
33517         titlebar: true,
33518         collapsible: true
33519     },
33520     center: {
33521         titlebar: true,
33522         autoScroll:true,
33523         resizeTabs: true,
33524         minTabWidth: 50,
33525         preferredTabWidth: 150
33526     }
33527 });
33528
33529 // shorthand
33530 var CP = Roo.ContentPanel;
33531
33532 layout.beginUpdate();
33533 layout.add("north", new CP("north", "North"));
33534 layout.add("south", new CP("south", {title: "South", closable: true}));
33535 layout.add("west", new CP("west", {title: "West"}));
33536 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33537 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33538 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33539 layout.getRegion("center").showPanel("center1");
33540 layout.endUpdate();
33541 </code></pre>
33542
33543 <b>The container the layout is rendered into can be either the body element or any other element.
33544 If it is not the body element, the container needs to either be an absolute positioned element,
33545 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33546 the container size if it is not the body element.</b>
33547
33548 * @constructor
33549 * Create a new BorderLayout
33550 * @param {String/HTMLElement/Element} container The container this layout is bound to
33551 * @param {Object} config Configuration options
33552  */
33553 Roo.BorderLayout = function(container, config){
33554     config = config || {};
33555     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33556     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33557     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33558         var target = this.factory.validRegions[i];
33559         if(config[target]){
33560             this.addRegion(target, config[target]);
33561         }
33562     }
33563 };
33564
33565 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33566         
33567         /**
33568          * @cfg {Roo.LayoutRegion} east
33569          */
33570         /**
33571          * @cfg {Roo.LayoutRegion} west
33572          */
33573         /**
33574          * @cfg {Roo.LayoutRegion} north
33575          */
33576         /**
33577          * @cfg {Roo.LayoutRegion} south
33578          */
33579         /**
33580          * @cfg {Roo.LayoutRegion} center
33581          */
33582     /**
33583      * Creates and adds a new region if it doesn't already exist.
33584      * @param {String} target The target region key (north, south, east, west or center).
33585      * @param {Object} config The regions config object
33586      * @return {BorderLayoutRegion} The new region
33587      */
33588     addRegion : function(target, config){
33589         if(!this.regions[target]){
33590             var r = this.factory.create(target, this, config);
33591             this.bindRegion(target, r);
33592         }
33593         return this.regions[target];
33594     },
33595
33596     // private (kinda)
33597     bindRegion : function(name, r){
33598         this.regions[name] = r;
33599         r.on("visibilitychange", this.layout, this);
33600         r.on("paneladded", this.layout, this);
33601         r.on("panelremoved", this.layout, this);
33602         r.on("invalidated", this.layout, this);
33603         r.on("resized", this.onRegionResized, this);
33604         r.on("collapsed", this.onRegionCollapsed, this);
33605         r.on("expanded", this.onRegionExpanded, this);
33606     },
33607
33608     /**
33609      * Performs a layout update.
33610      */
33611     layout : function(){
33612         if(this.updating) {
33613             return;
33614         }
33615         var size = this.getViewSize();
33616         var w = size.width;
33617         var h = size.height;
33618         var centerW = w;
33619         var centerH = h;
33620         var centerY = 0;
33621         var centerX = 0;
33622         //var x = 0, y = 0;
33623
33624         var rs = this.regions;
33625         var north = rs["north"];
33626         var south = rs["south"]; 
33627         var west = rs["west"];
33628         var east = rs["east"];
33629         var center = rs["center"];
33630         //if(this.hideOnLayout){ // not supported anymore
33631             //c.el.setStyle("display", "none");
33632         //}
33633         if(north && north.isVisible()){
33634             var b = north.getBox();
33635             var m = north.getMargins();
33636             b.width = w - (m.left+m.right);
33637             b.x = m.left;
33638             b.y = m.top;
33639             centerY = b.height + b.y + m.bottom;
33640             centerH -= centerY;
33641             north.updateBox(this.safeBox(b));
33642         }
33643         if(south && south.isVisible()){
33644             var b = south.getBox();
33645             var m = south.getMargins();
33646             b.width = w - (m.left+m.right);
33647             b.x = m.left;
33648             var totalHeight = (b.height + m.top + m.bottom);
33649             b.y = h - totalHeight + m.top;
33650             centerH -= totalHeight;
33651             south.updateBox(this.safeBox(b));
33652         }
33653         if(west && west.isVisible()){
33654             var b = west.getBox();
33655             var m = west.getMargins();
33656             b.height = centerH - (m.top+m.bottom);
33657             b.x = m.left;
33658             b.y = centerY + m.top;
33659             var totalWidth = (b.width + m.left + m.right);
33660             centerX += totalWidth;
33661             centerW -= totalWidth;
33662             west.updateBox(this.safeBox(b));
33663         }
33664         if(east && east.isVisible()){
33665             var b = east.getBox();
33666             var m = east.getMargins();
33667             b.height = centerH - (m.top+m.bottom);
33668             var totalWidth = (b.width + m.left + m.right);
33669             b.x = w - totalWidth + m.left;
33670             b.y = centerY + m.top;
33671             centerW -= totalWidth;
33672             east.updateBox(this.safeBox(b));
33673         }
33674         if(center){
33675             var m = center.getMargins();
33676             var centerBox = {
33677                 x: centerX + m.left,
33678                 y: centerY + m.top,
33679                 width: centerW - (m.left+m.right),
33680                 height: centerH - (m.top+m.bottom)
33681             };
33682             //if(this.hideOnLayout){
33683                 //center.el.setStyle("display", "block");
33684             //}
33685             center.updateBox(this.safeBox(centerBox));
33686         }
33687         this.el.repaint();
33688         this.fireEvent("layout", this);
33689     },
33690
33691     // private
33692     safeBox : function(box){
33693         box.width = Math.max(0, box.width);
33694         box.height = Math.max(0, box.height);
33695         return box;
33696     },
33697
33698     /**
33699      * Adds a ContentPanel (or subclass) to this layout.
33700      * @param {String} target The target region key (north, south, east, west or center).
33701      * @param {Roo.ContentPanel} panel The panel to add
33702      * @return {Roo.ContentPanel} The added panel
33703      */
33704     add : function(target, panel){
33705          
33706         target = target.toLowerCase();
33707         return this.regions[target].add(panel);
33708     },
33709
33710     /**
33711      * Remove a ContentPanel (or subclass) to this layout.
33712      * @param {String} target The target region key (north, south, east, west or center).
33713      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33714      * @return {Roo.ContentPanel} The removed panel
33715      */
33716     remove : function(target, panel){
33717         target = target.toLowerCase();
33718         return this.regions[target].remove(panel);
33719     },
33720
33721     /**
33722      * Searches all regions for a panel with the specified id
33723      * @param {String} panelId
33724      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33725      */
33726     findPanel : function(panelId){
33727         var rs = this.regions;
33728         for(var target in rs){
33729             if(typeof rs[target] != "function"){
33730                 var p = rs[target].getPanel(panelId);
33731                 if(p){
33732                     return p;
33733                 }
33734             }
33735         }
33736         return null;
33737     },
33738
33739     /**
33740      * Searches all regions for a panel with the specified id and activates (shows) it.
33741      * @param {String/ContentPanel} panelId The panels id or the panel itself
33742      * @return {Roo.ContentPanel} The shown panel or null
33743      */
33744     showPanel : function(panelId) {
33745       var rs = this.regions;
33746       for(var target in rs){
33747          var r = rs[target];
33748          if(typeof r != "function"){
33749             if(r.hasPanel(panelId)){
33750                return r.showPanel(panelId);
33751             }
33752          }
33753       }
33754       return null;
33755    },
33756
33757    /**
33758      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33759      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33760      */
33761     restoreState : function(provider){
33762         if(!provider){
33763             provider = Roo.state.Manager;
33764         }
33765         var sm = new Roo.LayoutStateManager();
33766         sm.init(this, provider);
33767     },
33768
33769     /**
33770      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33771      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33772      * a valid ContentPanel config object.  Example:
33773      * <pre><code>
33774 // Create the main layout
33775 var layout = new Roo.BorderLayout('main-ct', {
33776     west: {
33777         split:true,
33778         minSize: 175,
33779         titlebar: true
33780     },
33781     center: {
33782         title:'Components'
33783     }
33784 }, 'main-ct');
33785
33786 // Create and add multiple ContentPanels at once via configs
33787 layout.batchAdd({
33788    west: {
33789        id: 'source-files',
33790        autoCreate:true,
33791        title:'Ext Source Files',
33792        autoScroll:true,
33793        fitToFrame:true
33794    },
33795    center : {
33796        el: cview,
33797        autoScroll:true,
33798        fitToFrame:true,
33799        toolbar: tb,
33800        resizeEl:'cbody'
33801    }
33802 });
33803 </code></pre>
33804      * @param {Object} regions An object containing ContentPanel configs by region name
33805      */
33806     batchAdd : function(regions){
33807         this.beginUpdate();
33808         for(var rname in regions){
33809             var lr = this.regions[rname];
33810             if(lr){
33811                 this.addTypedPanels(lr, regions[rname]);
33812             }
33813         }
33814         this.endUpdate();
33815     },
33816
33817     // private
33818     addTypedPanels : function(lr, ps){
33819         if(typeof ps == 'string'){
33820             lr.add(new Roo.ContentPanel(ps));
33821         }
33822         else if(ps instanceof Array){
33823             for(var i =0, len = ps.length; i < len; i++){
33824                 this.addTypedPanels(lr, ps[i]);
33825             }
33826         }
33827         else if(!ps.events){ // raw config?
33828             var el = ps.el;
33829             delete ps.el; // prevent conflict
33830             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33831         }
33832         else {  // panel object assumed!
33833             lr.add(ps);
33834         }
33835     },
33836     /**
33837      * Adds a xtype elements to the layout.
33838      * <pre><code>
33839
33840 layout.addxtype({
33841        xtype : 'ContentPanel',
33842        region: 'west',
33843        items: [ .... ]
33844    }
33845 );
33846
33847 layout.addxtype({
33848         xtype : 'NestedLayoutPanel',
33849         region: 'west',
33850         layout: {
33851            center: { },
33852            west: { }   
33853         },
33854         items : [ ... list of content panels or nested layout panels.. ]
33855    }
33856 );
33857 </code></pre>
33858      * @param {Object} cfg Xtype definition of item to add.
33859      */
33860     addxtype : function(cfg)
33861     {
33862         // basically accepts a pannel...
33863         // can accept a layout region..!?!?
33864         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33865         
33866         if (!cfg.xtype.match(/Panel$/)) {
33867             return false;
33868         }
33869         var ret = false;
33870         
33871         if (typeof(cfg.region) == 'undefined') {
33872             Roo.log("Failed to add Panel, region was not set");
33873             Roo.log(cfg);
33874             return false;
33875         }
33876         var region = cfg.region;
33877         delete cfg.region;
33878         
33879           
33880         var xitems = [];
33881         if (cfg.items) {
33882             xitems = cfg.items;
33883             delete cfg.items;
33884         }
33885         var nb = false;
33886         
33887         switch(cfg.xtype) 
33888         {
33889             case 'ContentPanel':  // ContentPanel (el, cfg)
33890             case 'ScrollPanel':  // ContentPanel (el, cfg)
33891             case 'ViewPanel': 
33892                 if(cfg.autoCreate) {
33893                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33894                 } else {
33895                     var el = this.el.createChild();
33896                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33897                 }
33898                 
33899                 this.add(region, ret);
33900                 break;
33901             
33902             
33903             case 'TreePanel': // our new panel!
33904                 cfg.el = this.el.createChild();
33905                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33906                 this.add(region, ret);
33907                 break;
33908             
33909             case 'NestedLayoutPanel': 
33910                 // create a new Layout (which is  a Border Layout...
33911                 var el = this.el.createChild();
33912                 var clayout = cfg.layout;
33913                 delete cfg.layout;
33914                 clayout.items   = clayout.items  || [];
33915                 // replace this exitems with the clayout ones..
33916                 xitems = clayout.items;
33917                  
33918                 
33919                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33920                     cfg.background = false;
33921                 }
33922                 var layout = new Roo.BorderLayout(el, clayout);
33923                 
33924                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33925                 //console.log('adding nested layout panel '  + cfg.toSource());
33926                 this.add(region, ret);
33927                 nb = {}; /// find first...
33928                 break;
33929                 
33930             case 'GridPanel': 
33931             
33932                 // needs grid and region
33933                 
33934                 //var el = this.getRegion(region).el.createChild();
33935                 var el = this.el.createChild();
33936                 // create the grid first...
33937                 
33938                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33939                 delete cfg.grid;
33940                 if (region == 'center' && this.active ) {
33941                     cfg.background = false;
33942                 }
33943                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33944                 
33945                 this.add(region, ret);
33946                 if (cfg.background) {
33947                     ret.on('activate', function(gp) {
33948                         if (!gp.grid.rendered) {
33949                             gp.grid.render();
33950                         }
33951                     });
33952                 } else {
33953                     grid.render();
33954                 }
33955                 break;
33956            
33957            
33958            
33959                 
33960                 
33961                 
33962             default:
33963                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33964                     
33965                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33966                     this.add(region, ret);
33967                 } else {
33968                 
33969                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33970                     return null;
33971                 }
33972                 
33973              // GridPanel (grid, cfg)
33974             
33975         }
33976         this.beginUpdate();
33977         // add children..
33978         var region = '';
33979         var abn = {};
33980         Roo.each(xitems, function(i)  {
33981             region = nb && i.region ? i.region : false;
33982             
33983             var add = ret.addxtype(i);
33984            
33985             if (region) {
33986                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33987                 if (!i.background) {
33988                     abn[region] = nb[region] ;
33989                 }
33990             }
33991             
33992         });
33993         this.endUpdate();
33994
33995         // make the last non-background panel active..
33996         //if (nb) { Roo.log(abn); }
33997         if (nb) {
33998             
33999             for(var r in abn) {
34000                 region = this.getRegion(r);
34001                 if (region) {
34002                     // tried using nb[r], but it does not work..
34003                      
34004                     region.showPanel(abn[r]);
34005                    
34006                 }
34007             }
34008         }
34009         return ret;
34010         
34011     }
34012 });
34013
34014 /**
34015  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34016  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34017  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34018  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34019  * <pre><code>
34020 // shorthand
34021 var CP = Roo.ContentPanel;
34022
34023 var layout = Roo.BorderLayout.create({
34024     north: {
34025         initialSize: 25,
34026         titlebar: false,
34027         panels: [new CP("north", "North")]
34028     },
34029     west: {
34030         split:true,
34031         initialSize: 200,
34032         minSize: 175,
34033         maxSize: 400,
34034         titlebar: true,
34035         collapsible: true,
34036         panels: [new CP("west", {title: "West"})]
34037     },
34038     east: {
34039         split:true,
34040         initialSize: 202,
34041         minSize: 175,
34042         maxSize: 400,
34043         titlebar: true,
34044         collapsible: true,
34045         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34046     },
34047     south: {
34048         split:true,
34049         initialSize: 100,
34050         minSize: 100,
34051         maxSize: 200,
34052         titlebar: true,
34053         collapsible: true,
34054         panels: [new CP("south", {title: "South", closable: true})]
34055     },
34056     center: {
34057         titlebar: true,
34058         autoScroll:true,
34059         resizeTabs: true,
34060         minTabWidth: 50,
34061         preferredTabWidth: 150,
34062         panels: [
34063             new CP("center1", {title: "Close Me", closable: true}),
34064             new CP("center2", {title: "Center Panel", closable: false})
34065         ]
34066     }
34067 }, document.body);
34068
34069 layout.getRegion("center").showPanel("center1");
34070 </code></pre>
34071  * @param config
34072  * @param targetEl
34073  */
34074 Roo.BorderLayout.create = function(config, targetEl){
34075     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34076     layout.beginUpdate();
34077     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34078     for(var j = 0, jlen = regions.length; j < jlen; j++){
34079         var lr = regions[j];
34080         if(layout.regions[lr] && config[lr].panels){
34081             var r = layout.regions[lr];
34082             var ps = config[lr].panels;
34083             layout.addTypedPanels(r, ps);
34084         }
34085     }
34086     layout.endUpdate();
34087     return layout;
34088 };
34089
34090 // private
34091 Roo.BorderLayout.RegionFactory = {
34092     // private
34093     validRegions : ["north","south","east","west","center"],
34094
34095     // private
34096     create : function(target, mgr, config){
34097         target = target.toLowerCase();
34098         if(config.lightweight || config.basic){
34099             return new Roo.BasicLayoutRegion(mgr, config, target);
34100         }
34101         switch(target){
34102             case "north":
34103                 return new Roo.NorthLayoutRegion(mgr, config);
34104             case "south":
34105                 return new Roo.SouthLayoutRegion(mgr, config);
34106             case "east":
34107                 return new Roo.EastLayoutRegion(mgr, config);
34108             case "west":
34109                 return new Roo.WestLayoutRegion(mgr, config);
34110             case "center":
34111                 return new Roo.CenterLayoutRegion(mgr, config);
34112         }
34113         throw 'Layout region "'+target+'" not supported.';
34114     }
34115 };/*
34116  * Based on:
34117  * Ext JS Library 1.1.1
34118  * Copyright(c) 2006-2007, Ext JS, LLC.
34119  *
34120  * Originally Released Under LGPL - original licence link has changed is not relivant.
34121  *
34122  * Fork - LGPL
34123  * <script type="text/javascript">
34124  */
34125  
34126 /**
34127  * @class Roo.BasicLayoutRegion
34128  * @extends Roo.util.Observable
34129  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34130  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34131  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34132  */
34133 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34134     this.mgr = mgr;
34135     this.position  = pos;
34136     this.events = {
34137         /**
34138          * @scope Roo.BasicLayoutRegion
34139          */
34140         
34141         /**
34142          * @event beforeremove
34143          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34144          * @param {Roo.LayoutRegion} this
34145          * @param {Roo.ContentPanel} panel The panel
34146          * @param {Object} e The cancel event object
34147          */
34148         "beforeremove" : true,
34149         /**
34150          * @event invalidated
34151          * Fires when the layout for this region is changed.
34152          * @param {Roo.LayoutRegion} this
34153          */
34154         "invalidated" : true,
34155         /**
34156          * @event visibilitychange
34157          * Fires when this region is shown or hidden 
34158          * @param {Roo.LayoutRegion} this
34159          * @param {Boolean} visibility true or false
34160          */
34161         "visibilitychange" : true,
34162         /**
34163          * @event paneladded
34164          * Fires when a panel is added. 
34165          * @param {Roo.LayoutRegion} this
34166          * @param {Roo.ContentPanel} panel The panel
34167          */
34168         "paneladded" : true,
34169         /**
34170          * @event panelremoved
34171          * Fires when a panel is removed. 
34172          * @param {Roo.LayoutRegion} this
34173          * @param {Roo.ContentPanel} panel The panel
34174          */
34175         "panelremoved" : true,
34176         /**
34177          * @event beforecollapse
34178          * Fires when this region before collapse.
34179          * @param {Roo.LayoutRegion} this
34180          */
34181         "beforecollapse" : true,
34182         /**
34183          * @event collapsed
34184          * Fires when this region is collapsed.
34185          * @param {Roo.LayoutRegion} this
34186          */
34187         "collapsed" : true,
34188         /**
34189          * @event expanded
34190          * Fires when this region is expanded.
34191          * @param {Roo.LayoutRegion} this
34192          */
34193         "expanded" : true,
34194         /**
34195          * @event slideshow
34196          * Fires when this region is slid into view.
34197          * @param {Roo.LayoutRegion} this
34198          */
34199         "slideshow" : true,
34200         /**
34201          * @event slidehide
34202          * Fires when this region slides out of view. 
34203          * @param {Roo.LayoutRegion} this
34204          */
34205         "slidehide" : true,
34206         /**
34207          * @event panelactivated
34208          * Fires when a panel is activated. 
34209          * @param {Roo.LayoutRegion} this
34210          * @param {Roo.ContentPanel} panel The activated panel
34211          */
34212         "panelactivated" : true,
34213         /**
34214          * @event resized
34215          * Fires when the user resizes this region. 
34216          * @param {Roo.LayoutRegion} this
34217          * @param {Number} newSize The new size (width for east/west, height for north/south)
34218          */
34219         "resized" : true
34220     };
34221     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34222     this.panels = new Roo.util.MixedCollection();
34223     this.panels.getKey = this.getPanelId.createDelegate(this);
34224     this.box = null;
34225     this.activePanel = null;
34226     // ensure listeners are added...
34227     
34228     if (config.listeners || config.events) {
34229         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34230             listeners : config.listeners || {},
34231             events : config.events || {}
34232         });
34233     }
34234     
34235     if(skipConfig !== true){
34236         this.applyConfig(config);
34237     }
34238 };
34239
34240 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34241     getPanelId : function(p){
34242         return p.getId();
34243     },
34244     
34245     applyConfig : function(config){
34246         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34247         this.config = config;
34248         
34249     },
34250     
34251     /**
34252      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34253      * the width, for horizontal (north, south) the height.
34254      * @param {Number} newSize The new width or height
34255      */
34256     resizeTo : function(newSize){
34257         var el = this.el ? this.el :
34258                  (this.activePanel ? this.activePanel.getEl() : null);
34259         if(el){
34260             switch(this.position){
34261                 case "east":
34262                 case "west":
34263                     el.setWidth(newSize);
34264                     this.fireEvent("resized", this, newSize);
34265                 break;
34266                 case "north":
34267                 case "south":
34268                     el.setHeight(newSize);
34269                     this.fireEvent("resized", this, newSize);
34270                 break;                
34271             }
34272         }
34273     },
34274     
34275     getBox : function(){
34276         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34277     },
34278     
34279     getMargins : function(){
34280         return this.margins;
34281     },
34282     
34283     updateBox : function(box){
34284         this.box = box;
34285         var el = this.activePanel.getEl();
34286         el.dom.style.left = box.x + "px";
34287         el.dom.style.top = box.y + "px";
34288         this.activePanel.setSize(box.width, box.height);
34289     },
34290     
34291     /**
34292      * Returns the container element for this region.
34293      * @return {Roo.Element}
34294      */
34295     getEl : function(){
34296         return this.activePanel;
34297     },
34298     
34299     /**
34300      * Returns true if this region is currently visible.
34301      * @return {Boolean}
34302      */
34303     isVisible : function(){
34304         return this.activePanel ? true : false;
34305     },
34306     
34307     setActivePanel : function(panel){
34308         panel = this.getPanel(panel);
34309         if(this.activePanel && this.activePanel != panel){
34310             this.activePanel.setActiveState(false);
34311             this.activePanel.getEl().setLeftTop(-10000,-10000);
34312         }
34313         this.activePanel = panel;
34314         panel.setActiveState(true);
34315         if(this.box){
34316             panel.setSize(this.box.width, this.box.height);
34317         }
34318         this.fireEvent("panelactivated", this, panel);
34319         this.fireEvent("invalidated");
34320     },
34321     
34322     /**
34323      * Show the specified panel.
34324      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34325      * @return {Roo.ContentPanel} The shown panel or null
34326      */
34327     showPanel : function(panel){
34328         if(panel = this.getPanel(panel)){
34329             this.setActivePanel(panel);
34330         }
34331         return panel;
34332     },
34333     
34334     /**
34335      * Get the active panel for this region.
34336      * @return {Roo.ContentPanel} The active panel or null
34337      */
34338     getActivePanel : function(){
34339         return this.activePanel;
34340     },
34341     
34342     /**
34343      * Add the passed ContentPanel(s)
34344      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34345      * @return {Roo.ContentPanel} The panel added (if only one was added)
34346      */
34347     add : function(panel){
34348         if(arguments.length > 1){
34349             for(var i = 0, len = arguments.length; i < len; i++) {
34350                 this.add(arguments[i]);
34351             }
34352             return null;
34353         }
34354         if(this.hasPanel(panel)){
34355             this.showPanel(panel);
34356             return panel;
34357         }
34358         var el = panel.getEl();
34359         if(el.dom.parentNode != this.mgr.el.dom){
34360             this.mgr.el.dom.appendChild(el.dom);
34361         }
34362         if(panel.setRegion){
34363             panel.setRegion(this);
34364         }
34365         this.panels.add(panel);
34366         el.setStyle("position", "absolute");
34367         if(!panel.background){
34368             this.setActivePanel(panel);
34369             if(this.config.initialSize && this.panels.getCount()==1){
34370                 this.resizeTo(this.config.initialSize);
34371             }
34372         }
34373         this.fireEvent("paneladded", this, panel);
34374         return panel;
34375     },
34376     
34377     /**
34378      * Returns true if the panel is in this region.
34379      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34380      * @return {Boolean}
34381      */
34382     hasPanel : function(panel){
34383         if(typeof panel == "object"){ // must be panel obj
34384             panel = panel.getId();
34385         }
34386         return this.getPanel(panel) ? true : false;
34387     },
34388     
34389     /**
34390      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34391      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34392      * @param {Boolean} preservePanel Overrides the config preservePanel option
34393      * @return {Roo.ContentPanel} The panel that was removed
34394      */
34395     remove : function(panel, preservePanel){
34396         panel = this.getPanel(panel);
34397         if(!panel){
34398             return null;
34399         }
34400         var e = {};
34401         this.fireEvent("beforeremove", this, panel, e);
34402         if(e.cancel === true){
34403             return null;
34404         }
34405         var panelId = panel.getId();
34406         this.panels.removeKey(panelId);
34407         return panel;
34408     },
34409     
34410     /**
34411      * Returns the panel specified or null if it's not in this region.
34412      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34413      * @return {Roo.ContentPanel}
34414      */
34415     getPanel : function(id){
34416         if(typeof id == "object"){ // must be panel obj
34417             return id;
34418         }
34419         return this.panels.get(id);
34420     },
34421     
34422     /**
34423      * Returns this regions position (north/south/east/west/center).
34424      * @return {String} 
34425      */
34426     getPosition: function(){
34427         return this.position;    
34428     }
34429 });/*
34430  * Based on:
34431  * Ext JS Library 1.1.1
34432  * Copyright(c) 2006-2007, Ext JS, LLC.
34433  *
34434  * Originally Released Under LGPL - original licence link has changed is not relivant.
34435  *
34436  * Fork - LGPL
34437  * <script type="text/javascript">
34438  */
34439  
34440 /**
34441  * @class Roo.LayoutRegion
34442  * @extends Roo.BasicLayoutRegion
34443  * This class represents a region in a layout manager.
34444  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34445  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34446  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34447  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34448  * @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})
34449  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34450  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34451  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34452  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34453  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34454  * @cfg {String}    title           The title for the region (overrides panel titles)
34455  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34456  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34457  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34458  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34459  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34460  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34461  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34462  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34463  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34464  * @cfg {Boolean}   showPin         True to show a pin button
34465  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34466  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34467  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34468  * @cfg {Number}    width           For East/West panels
34469  * @cfg {Number}    height          For North/South panels
34470  * @cfg {Boolean}   split           To show the splitter
34471  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34472  */
34473 Roo.LayoutRegion = function(mgr, config, pos){
34474     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34475     var dh = Roo.DomHelper;
34476     /** This region's container element 
34477     * @type Roo.Element */
34478     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34479     /** This region's title element 
34480     * @type Roo.Element */
34481
34482     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34483         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34484         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34485     ]}, true);
34486     this.titleEl.enableDisplayMode();
34487     /** This region's title text element 
34488     * @type HTMLElement */
34489     this.titleTextEl = this.titleEl.dom.firstChild;
34490     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34491     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34492     this.closeBtn.enableDisplayMode();
34493     this.closeBtn.on("click", this.closeClicked, this);
34494     this.closeBtn.hide();
34495
34496     this.createBody(config);
34497     this.visible = true;
34498     this.collapsed = false;
34499
34500     if(config.hideWhenEmpty){
34501         this.hide();
34502         this.on("paneladded", this.validateVisibility, this);
34503         this.on("panelremoved", this.validateVisibility, this);
34504     }
34505     this.applyConfig(config);
34506 };
34507
34508 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34509
34510     createBody : function(){
34511         /** This region's body element 
34512         * @type Roo.Element */
34513         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34514     },
34515
34516     applyConfig : function(c){
34517         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34518             var dh = Roo.DomHelper;
34519             if(c.titlebar !== false){
34520                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34521                 this.collapseBtn.on("click", this.collapse, this);
34522                 this.collapseBtn.enableDisplayMode();
34523
34524                 if(c.showPin === true || this.showPin){
34525                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34526                     this.stickBtn.enableDisplayMode();
34527                     this.stickBtn.on("click", this.expand, this);
34528                     this.stickBtn.hide();
34529                 }
34530             }
34531             /** This region's collapsed element
34532             * @type Roo.Element */
34533             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34534                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34535             ]}, true);
34536             if(c.floatable !== false){
34537                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34538                this.collapsedEl.on("click", this.collapseClick, this);
34539             }
34540
34541             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34542                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34543                    id: "message", unselectable: "on", style:{"float":"left"}});
34544                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34545              }
34546             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34547             this.expandBtn.on("click", this.expand, this);
34548         }
34549         if(this.collapseBtn){
34550             this.collapseBtn.setVisible(c.collapsible == true);
34551         }
34552         this.cmargins = c.cmargins || this.cmargins ||
34553                          (this.position == "west" || this.position == "east" ?
34554                              {top: 0, left: 2, right:2, bottom: 0} :
34555                              {top: 2, left: 0, right:0, bottom: 2});
34556         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34557         this.bottomTabs = c.tabPosition != "top";
34558         this.autoScroll = c.autoScroll || false;
34559         if(this.autoScroll){
34560             this.bodyEl.setStyle("overflow", "auto");
34561         }else{
34562             this.bodyEl.setStyle("overflow", "hidden");
34563         }
34564         //if(c.titlebar !== false){
34565             if((!c.titlebar && !c.title) || c.titlebar === false){
34566                 this.titleEl.hide();
34567             }else{
34568                 this.titleEl.show();
34569                 if(c.title){
34570                     this.titleTextEl.innerHTML = c.title;
34571                 }
34572             }
34573         //}
34574         this.duration = c.duration || .30;
34575         this.slideDuration = c.slideDuration || .45;
34576         this.config = c;
34577         if(c.collapsed){
34578             this.collapse(true);
34579         }
34580         if(c.hidden){
34581             this.hide();
34582         }
34583     },
34584     /**
34585      * Returns true if this region is currently visible.
34586      * @return {Boolean}
34587      */
34588     isVisible : function(){
34589         return this.visible;
34590     },
34591
34592     /**
34593      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34594      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34595      */
34596     setCollapsedTitle : function(title){
34597         title = title || "&#160;";
34598         if(this.collapsedTitleTextEl){
34599             this.collapsedTitleTextEl.innerHTML = title;
34600         }
34601     },
34602
34603     getBox : function(){
34604         var b;
34605         if(!this.collapsed){
34606             b = this.el.getBox(false, true);
34607         }else{
34608             b = this.collapsedEl.getBox(false, true);
34609         }
34610         return b;
34611     },
34612
34613     getMargins : function(){
34614         return this.collapsed ? this.cmargins : this.margins;
34615     },
34616
34617     highlight : function(){
34618         this.el.addClass("x-layout-panel-dragover");
34619     },
34620
34621     unhighlight : function(){
34622         this.el.removeClass("x-layout-panel-dragover");
34623     },
34624
34625     updateBox : function(box){
34626         this.box = box;
34627         if(!this.collapsed){
34628             this.el.dom.style.left = box.x + "px";
34629             this.el.dom.style.top = box.y + "px";
34630             this.updateBody(box.width, box.height);
34631         }else{
34632             this.collapsedEl.dom.style.left = box.x + "px";
34633             this.collapsedEl.dom.style.top = box.y + "px";
34634             this.collapsedEl.setSize(box.width, box.height);
34635         }
34636         if(this.tabs){
34637             this.tabs.autoSizeTabs();
34638         }
34639     },
34640
34641     updateBody : function(w, h){
34642         if(w !== null){
34643             this.el.setWidth(w);
34644             w -= this.el.getBorderWidth("rl");
34645             if(this.config.adjustments){
34646                 w += this.config.adjustments[0];
34647             }
34648         }
34649         if(h !== null){
34650             this.el.setHeight(h);
34651             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34652             h -= this.el.getBorderWidth("tb");
34653             if(this.config.adjustments){
34654                 h += this.config.adjustments[1];
34655             }
34656             this.bodyEl.setHeight(h);
34657             if(this.tabs){
34658                 h = this.tabs.syncHeight(h);
34659             }
34660         }
34661         if(this.panelSize){
34662             w = w !== null ? w : this.panelSize.width;
34663             h = h !== null ? h : this.panelSize.height;
34664         }
34665         if(this.activePanel){
34666             var el = this.activePanel.getEl();
34667             w = w !== null ? w : el.getWidth();
34668             h = h !== null ? h : el.getHeight();
34669             this.panelSize = {width: w, height: h};
34670             this.activePanel.setSize(w, h);
34671         }
34672         if(Roo.isIE && this.tabs){
34673             this.tabs.el.repaint();
34674         }
34675     },
34676
34677     /**
34678      * Returns the container element for this region.
34679      * @return {Roo.Element}
34680      */
34681     getEl : function(){
34682         return this.el;
34683     },
34684
34685     /**
34686      * Hides this region.
34687      */
34688     hide : function(){
34689         if(!this.collapsed){
34690             this.el.dom.style.left = "-2000px";
34691             this.el.hide();
34692         }else{
34693             this.collapsedEl.dom.style.left = "-2000px";
34694             this.collapsedEl.hide();
34695         }
34696         this.visible = false;
34697         this.fireEvent("visibilitychange", this, false);
34698     },
34699
34700     /**
34701      * Shows this region if it was previously hidden.
34702      */
34703     show : function(){
34704         if(!this.collapsed){
34705             this.el.show();
34706         }else{
34707             this.collapsedEl.show();
34708         }
34709         this.visible = true;
34710         this.fireEvent("visibilitychange", this, true);
34711     },
34712
34713     closeClicked : function(){
34714         if(this.activePanel){
34715             this.remove(this.activePanel);
34716         }
34717     },
34718
34719     collapseClick : function(e){
34720         if(this.isSlid){
34721            e.stopPropagation();
34722            this.slideIn();
34723         }else{
34724            e.stopPropagation();
34725            this.slideOut();
34726         }
34727     },
34728
34729     /**
34730      * Collapses this region.
34731      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34732      */
34733     collapse : function(skipAnim, skipCheck){
34734         if(this.collapsed) {
34735             return;
34736         }
34737         
34738         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34739             
34740             this.collapsed = true;
34741             if(this.split){
34742                 this.split.el.hide();
34743             }
34744             if(this.config.animate && skipAnim !== true){
34745                 this.fireEvent("invalidated", this);
34746                 this.animateCollapse();
34747             }else{
34748                 this.el.setLocation(-20000,-20000);
34749                 this.el.hide();
34750                 this.collapsedEl.show();
34751                 this.fireEvent("collapsed", this);
34752                 this.fireEvent("invalidated", this);
34753             }
34754         }
34755         
34756     },
34757
34758     animateCollapse : function(){
34759         // overridden
34760     },
34761
34762     /**
34763      * Expands this region if it was previously collapsed.
34764      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34765      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34766      */
34767     expand : function(e, skipAnim){
34768         if(e) {
34769             e.stopPropagation();
34770         }
34771         if(!this.collapsed || this.el.hasActiveFx()) {
34772             return;
34773         }
34774         if(this.isSlid){
34775             this.afterSlideIn();
34776             skipAnim = true;
34777         }
34778         this.collapsed = false;
34779         if(this.config.animate && skipAnim !== true){
34780             this.animateExpand();
34781         }else{
34782             this.el.show();
34783             if(this.split){
34784                 this.split.el.show();
34785             }
34786             this.collapsedEl.setLocation(-2000,-2000);
34787             this.collapsedEl.hide();
34788             this.fireEvent("invalidated", this);
34789             this.fireEvent("expanded", this);
34790         }
34791     },
34792
34793     animateExpand : function(){
34794         // overridden
34795     },
34796
34797     initTabs : function()
34798     {
34799         this.bodyEl.setStyle("overflow", "hidden");
34800         var ts = new Roo.TabPanel(
34801                 this.bodyEl.dom,
34802                 {
34803                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34804                     disableTooltips: this.config.disableTabTips,
34805                     toolbar : this.config.toolbar
34806                 }
34807         );
34808         if(this.config.hideTabs){
34809             ts.stripWrap.setDisplayed(false);
34810         }
34811         this.tabs = ts;
34812         ts.resizeTabs = this.config.resizeTabs === true;
34813         ts.minTabWidth = this.config.minTabWidth || 40;
34814         ts.maxTabWidth = this.config.maxTabWidth || 250;
34815         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34816         ts.monitorResize = false;
34817         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34818         ts.bodyEl.addClass('x-layout-tabs-body');
34819         this.panels.each(this.initPanelAsTab, this);
34820     },
34821
34822     initPanelAsTab : function(panel){
34823         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34824                     this.config.closeOnTab && panel.isClosable());
34825         if(panel.tabTip !== undefined){
34826             ti.setTooltip(panel.tabTip);
34827         }
34828         ti.on("activate", function(){
34829               this.setActivePanel(panel);
34830         }, this);
34831         if(this.config.closeOnTab){
34832             ti.on("beforeclose", function(t, e){
34833                 e.cancel = true;
34834                 this.remove(panel);
34835             }, this);
34836         }
34837         return ti;
34838     },
34839
34840     updatePanelTitle : function(panel, title){
34841         if(this.activePanel == panel){
34842             this.updateTitle(title);
34843         }
34844         if(this.tabs){
34845             var ti = this.tabs.getTab(panel.getEl().id);
34846             ti.setText(title);
34847             if(panel.tabTip !== undefined){
34848                 ti.setTooltip(panel.tabTip);
34849             }
34850         }
34851     },
34852
34853     updateTitle : function(title){
34854         if(this.titleTextEl && !this.config.title){
34855             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34856         }
34857     },
34858
34859     setActivePanel : function(panel){
34860         panel = this.getPanel(panel);
34861         if(this.activePanel && this.activePanel != panel){
34862             this.activePanel.setActiveState(false);
34863         }
34864         this.activePanel = panel;
34865         panel.setActiveState(true);
34866         if(this.panelSize){
34867             panel.setSize(this.panelSize.width, this.panelSize.height);
34868         }
34869         if(this.closeBtn){
34870             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34871         }
34872         this.updateTitle(panel.getTitle());
34873         if(this.tabs){
34874             this.fireEvent("invalidated", this);
34875         }
34876         this.fireEvent("panelactivated", this, panel);
34877     },
34878
34879     /**
34880      * Shows the specified panel.
34881      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34882      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34883      */
34884     showPanel : function(panel)
34885     {
34886         panel = this.getPanel(panel);
34887         if(panel){
34888             if(this.tabs){
34889                 var tab = this.tabs.getTab(panel.getEl().id);
34890                 if(tab.isHidden()){
34891                     this.tabs.unhideTab(tab.id);
34892                 }
34893                 tab.activate();
34894             }else{
34895                 this.setActivePanel(panel);
34896             }
34897         }
34898         return panel;
34899     },
34900
34901     /**
34902      * Get the active panel for this region.
34903      * @return {Roo.ContentPanel} The active panel or null
34904      */
34905     getActivePanel : function(){
34906         return this.activePanel;
34907     },
34908
34909     validateVisibility : function(){
34910         if(this.panels.getCount() < 1){
34911             this.updateTitle("&#160;");
34912             this.closeBtn.hide();
34913             this.hide();
34914         }else{
34915             if(!this.isVisible()){
34916                 this.show();
34917             }
34918         }
34919     },
34920
34921     /**
34922      * Adds the passed ContentPanel(s) to this region.
34923      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34924      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34925      */
34926     add : function(panel){
34927         if(arguments.length > 1){
34928             for(var i = 0, len = arguments.length; i < len; i++) {
34929                 this.add(arguments[i]);
34930             }
34931             return null;
34932         }
34933         if(this.hasPanel(panel)){
34934             this.showPanel(panel);
34935             return panel;
34936         }
34937         panel.setRegion(this);
34938         this.panels.add(panel);
34939         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34940             this.bodyEl.dom.appendChild(panel.getEl().dom);
34941             if(panel.background !== true){
34942                 this.setActivePanel(panel);
34943             }
34944             this.fireEvent("paneladded", this, panel);
34945             return panel;
34946         }
34947         if(!this.tabs){
34948             this.initTabs();
34949         }else{
34950             this.initPanelAsTab(panel);
34951         }
34952         if(panel.background !== true){
34953             this.tabs.activate(panel.getEl().id);
34954         }
34955         this.fireEvent("paneladded", this, panel);
34956         return panel;
34957     },
34958
34959     /**
34960      * Hides the tab for the specified panel.
34961      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34962      */
34963     hidePanel : function(panel){
34964         if(this.tabs && (panel = this.getPanel(panel))){
34965             this.tabs.hideTab(panel.getEl().id);
34966         }
34967     },
34968
34969     /**
34970      * Unhides the tab for a previously hidden panel.
34971      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34972      */
34973     unhidePanel : function(panel){
34974         if(this.tabs && (panel = this.getPanel(panel))){
34975             this.tabs.unhideTab(panel.getEl().id);
34976         }
34977     },
34978
34979     clearPanels : function(){
34980         while(this.panels.getCount() > 0){
34981              this.remove(this.panels.first());
34982         }
34983     },
34984
34985     /**
34986      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34987      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34988      * @param {Boolean} preservePanel Overrides the config preservePanel option
34989      * @return {Roo.ContentPanel} The panel that was removed
34990      */
34991     remove : function(panel, preservePanel){
34992         panel = this.getPanel(panel);
34993         if(!panel){
34994             return null;
34995         }
34996         var e = {};
34997         this.fireEvent("beforeremove", this, panel, e);
34998         if(e.cancel === true){
34999             return null;
35000         }
35001         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35002         var panelId = panel.getId();
35003         this.panels.removeKey(panelId);
35004         if(preservePanel){
35005             document.body.appendChild(panel.getEl().dom);
35006         }
35007         if(this.tabs){
35008             this.tabs.removeTab(panel.getEl().id);
35009         }else if (!preservePanel){
35010             this.bodyEl.dom.removeChild(panel.getEl().dom);
35011         }
35012         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35013             var p = this.panels.first();
35014             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35015             tempEl.appendChild(p.getEl().dom);
35016             this.bodyEl.update("");
35017             this.bodyEl.dom.appendChild(p.getEl().dom);
35018             tempEl = null;
35019             this.updateTitle(p.getTitle());
35020             this.tabs = null;
35021             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35022             this.setActivePanel(p);
35023         }
35024         panel.setRegion(null);
35025         if(this.activePanel == panel){
35026             this.activePanel = null;
35027         }
35028         if(this.config.autoDestroy !== false && preservePanel !== true){
35029             try{panel.destroy();}catch(e){}
35030         }
35031         this.fireEvent("panelremoved", this, panel);
35032         return panel;
35033     },
35034
35035     /**
35036      * Returns the TabPanel component used by this region
35037      * @return {Roo.TabPanel}
35038      */
35039     getTabs : function(){
35040         return this.tabs;
35041     },
35042
35043     createTool : function(parentEl, className){
35044         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35045             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35046         btn.addClassOnOver("x-layout-tools-button-over");
35047         return btn;
35048     }
35049 });/*
35050  * Based on:
35051  * Ext JS Library 1.1.1
35052  * Copyright(c) 2006-2007, Ext JS, LLC.
35053  *
35054  * Originally Released Under LGPL - original licence link has changed is not relivant.
35055  *
35056  * Fork - LGPL
35057  * <script type="text/javascript">
35058  */
35059  
35060
35061
35062 /**
35063  * @class Roo.SplitLayoutRegion
35064  * @extends Roo.LayoutRegion
35065  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35066  */
35067 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35068     this.cursor = cursor;
35069     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35070 };
35071
35072 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35073     splitTip : "Drag to resize.",
35074     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35075     useSplitTips : false,
35076
35077     applyConfig : function(config){
35078         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35079         if(config.split){
35080             if(!this.split){
35081                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35082                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35083                 /** The SplitBar for this region 
35084                 * @type Roo.SplitBar */
35085                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35086                 this.split.on("moved", this.onSplitMove, this);
35087                 this.split.useShim = config.useShim === true;
35088                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35089                 if(this.useSplitTips){
35090                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35091                 }
35092                 if(config.collapsible){
35093                     this.split.el.on("dblclick", this.collapse,  this);
35094                 }
35095             }
35096             if(typeof config.minSize != "undefined"){
35097                 this.split.minSize = config.minSize;
35098             }
35099             if(typeof config.maxSize != "undefined"){
35100                 this.split.maxSize = config.maxSize;
35101             }
35102             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35103                 this.hideSplitter();
35104             }
35105         }
35106     },
35107
35108     getHMaxSize : function(){
35109          var cmax = this.config.maxSize || 10000;
35110          var center = this.mgr.getRegion("center");
35111          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35112     },
35113
35114     getVMaxSize : function(){
35115          var cmax = this.config.maxSize || 10000;
35116          var center = this.mgr.getRegion("center");
35117          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35118     },
35119
35120     onSplitMove : function(split, newSize){
35121         this.fireEvent("resized", this, newSize);
35122     },
35123     
35124     /** 
35125      * Returns the {@link Roo.SplitBar} for this region.
35126      * @return {Roo.SplitBar}
35127      */
35128     getSplitBar : function(){
35129         return this.split;
35130     },
35131     
35132     hide : function(){
35133         this.hideSplitter();
35134         Roo.SplitLayoutRegion.superclass.hide.call(this);
35135     },
35136
35137     hideSplitter : function(){
35138         if(this.split){
35139             this.split.el.setLocation(-2000,-2000);
35140             this.split.el.hide();
35141         }
35142     },
35143
35144     show : function(){
35145         if(this.split){
35146             this.split.el.show();
35147         }
35148         Roo.SplitLayoutRegion.superclass.show.call(this);
35149     },
35150     
35151     beforeSlide: function(){
35152         if(Roo.isGecko){// firefox overflow auto bug workaround
35153             this.bodyEl.clip();
35154             if(this.tabs) {
35155                 this.tabs.bodyEl.clip();
35156             }
35157             if(this.activePanel){
35158                 this.activePanel.getEl().clip();
35159                 
35160                 if(this.activePanel.beforeSlide){
35161                     this.activePanel.beforeSlide();
35162                 }
35163             }
35164         }
35165     },
35166     
35167     afterSlide : function(){
35168         if(Roo.isGecko){// firefox overflow auto bug workaround
35169             this.bodyEl.unclip();
35170             if(this.tabs) {
35171                 this.tabs.bodyEl.unclip();
35172             }
35173             if(this.activePanel){
35174                 this.activePanel.getEl().unclip();
35175                 if(this.activePanel.afterSlide){
35176                     this.activePanel.afterSlide();
35177                 }
35178             }
35179         }
35180     },
35181
35182     initAutoHide : function(){
35183         if(this.autoHide !== false){
35184             if(!this.autoHideHd){
35185                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35186                 this.autoHideHd = {
35187                     "mouseout": function(e){
35188                         if(!e.within(this.el, true)){
35189                             st.delay(500);
35190                         }
35191                     },
35192                     "mouseover" : function(e){
35193                         st.cancel();
35194                     },
35195                     scope : this
35196                 };
35197             }
35198             this.el.on(this.autoHideHd);
35199         }
35200     },
35201
35202     clearAutoHide : function(){
35203         if(this.autoHide !== false){
35204             this.el.un("mouseout", this.autoHideHd.mouseout);
35205             this.el.un("mouseover", this.autoHideHd.mouseover);
35206         }
35207     },
35208
35209     clearMonitor : function(){
35210         Roo.get(document).un("click", this.slideInIf, this);
35211     },
35212
35213     // these names are backwards but not changed for compat
35214     slideOut : function(){
35215         if(this.isSlid || this.el.hasActiveFx()){
35216             return;
35217         }
35218         this.isSlid = true;
35219         if(this.collapseBtn){
35220             this.collapseBtn.hide();
35221         }
35222         this.closeBtnState = this.closeBtn.getStyle('display');
35223         this.closeBtn.hide();
35224         if(this.stickBtn){
35225             this.stickBtn.show();
35226         }
35227         this.el.show();
35228         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35229         this.beforeSlide();
35230         this.el.setStyle("z-index", 10001);
35231         this.el.slideIn(this.getSlideAnchor(), {
35232             callback: function(){
35233                 this.afterSlide();
35234                 this.initAutoHide();
35235                 Roo.get(document).on("click", this.slideInIf, this);
35236                 this.fireEvent("slideshow", this);
35237             },
35238             scope: this,
35239             block: true
35240         });
35241     },
35242
35243     afterSlideIn : function(){
35244         this.clearAutoHide();
35245         this.isSlid = false;
35246         this.clearMonitor();
35247         this.el.setStyle("z-index", "");
35248         if(this.collapseBtn){
35249             this.collapseBtn.show();
35250         }
35251         this.closeBtn.setStyle('display', this.closeBtnState);
35252         if(this.stickBtn){
35253             this.stickBtn.hide();
35254         }
35255         this.fireEvent("slidehide", this);
35256     },
35257
35258     slideIn : function(cb){
35259         if(!this.isSlid || this.el.hasActiveFx()){
35260             Roo.callback(cb);
35261             return;
35262         }
35263         this.isSlid = false;
35264         this.beforeSlide();
35265         this.el.slideOut(this.getSlideAnchor(), {
35266             callback: function(){
35267                 this.el.setLeftTop(-10000, -10000);
35268                 this.afterSlide();
35269                 this.afterSlideIn();
35270                 Roo.callback(cb);
35271             },
35272             scope: this,
35273             block: true
35274         });
35275     },
35276     
35277     slideInIf : function(e){
35278         if(!e.within(this.el)){
35279             this.slideIn();
35280         }
35281     },
35282
35283     animateCollapse : function(){
35284         this.beforeSlide();
35285         this.el.setStyle("z-index", 20000);
35286         var anchor = this.getSlideAnchor();
35287         this.el.slideOut(anchor, {
35288             callback : function(){
35289                 this.el.setStyle("z-index", "");
35290                 this.collapsedEl.slideIn(anchor, {duration:.3});
35291                 this.afterSlide();
35292                 this.el.setLocation(-10000,-10000);
35293                 this.el.hide();
35294                 this.fireEvent("collapsed", this);
35295             },
35296             scope: this,
35297             block: true
35298         });
35299     },
35300
35301     animateExpand : function(){
35302         this.beforeSlide();
35303         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35304         this.el.setStyle("z-index", 20000);
35305         this.collapsedEl.hide({
35306             duration:.1
35307         });
35308         this.el.slideIn(this.getSlideAnchor(), {
35309             callback : function(){
35310                 this.el.setStyle("z-index", "");
35311                 this.afterSlide();
35312                 if(this.split){
35313                     this.split.el.show();
35314                 }
35315                 this.fireEvent("invalidated", this);
35316                 this.fireEvent("expanded", this);
35317             },
35318             scope: this,
35319             block: true
35320         });
35321     },
35322
35323     anchors : {
35324         "west" : "left",
35325         "east" : "right",
35326         "north" : "top",
35327         "south" : "bottom"
35328     },
35329
35330     sanchors : {
35331         "west" : "l",
35332         "east" : "r",
35333         "north" : "t",
35334         "south" : "b"
35335     },
35336
35337     canchors : {
35338         "west" : "tl-tr",
35339         "east" : "tr-tl",
35340         "north" : "tl-bl",
35341         "south" : "bl-tl"
35342     },
35343
35344     getAnchor : function(){
35345         return this.anchors[this.position];
35346     },
35347
35348     getCollapseAnchor : function(){
35349         return this.canchors[this.position];
35350     },
35351
35352     getSlideAnchor : function(){
35353         return this.sanchors[this.position];
35354     },
35355
35356     getAlignAdj : function(){
35357         var cm = this.cmargins;
35358         switch(this.position){
35359             case "west":
35360                 return [0, 0];
35361             break;
35362             case "east":
35363                 return [0, 0];
35364             break;
35365             case "north":
35366                 return [0, 0];
35367             break;
35368             case "south":
35369                 return [0, 0];
35370             break;
35371         }
35372     },
35373
35374     getExpandAdj : function(){
35375         var c = this.collapsedEl, cm = this.cmargins;
35376         switch(this.position){
35377             case "west":
35378                 return [-(cm.right+c.getWidth()+cm.left), 0];
35379             break;
35380             case "east":
35381                 return [cm.right+c.getWidth()+cm.left, 0];
35382             break;
35383             case "north":
35384                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35385             break;
35386             case "south":
35387                 return [0, cm.top+cm.bottom+c.getHeight()];
35388             break;
35389         }
35390     }
35391 });/*
35392  * Based on:
35393  * Ext JS Library 1.1.1
35394  * Copyright(c) 2006-2007, Ext JS, LLC.
35395  *
35396  * Originally Released Under LGPL - original licence link has changed is not relivant.
35397  *
35398  * Fork - LGPL
35399  * <script type="text/javascript">
35400  */
35401 /*
35402  * These classes are private internal classes
35403  */
35404 Roo.CenterLayoutRegion = function(mgr, config){
35405     Roo.LayoutRegion.call(this, mgr, config, "center");
35406     this.visible = true;
35407     this.minWidth = config.minWidth || 20;
35408     this.minHeight = config.minHeight || 20;
35409 };
35410
35411 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35412     hide : function(){
35413         // center panel can't be hidden
35414     },
35415     
35416     show : function(){
35417         // center panel can't be hidden
35418     },
35419     
35420     getMinWidth: function(){
35421         return this.minWidth;
35422     },
35423     
35424     getMinHeight: function(){
35425         return this.minHeight;
35426     }
35427 });
35428
35429
35430 Roo.NorthLayoutRegion = function(mgr, config){
35431     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35432     if(this.split){
35433         this.split.placement = Roo.SplitBar.TOP;
35434         this.split.orientation = Roo.SplitBar.VERTICAL;
35435         this.split.el.addClass("x-layout-split-v");
35436     }
35437     var size = config.initialSize || config.height;
35438     if(typeof size != "undefined"){
35439         this.el.setHeight(size);
35440     }
35441 };
35442 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35443     orientation: Roo.SplitBar.VERTICAL,
35444     getBox : function(){
35445         if(this.collapsed){
35446             return this.collapsedEl.getBox();
35447         }
35448         var box = this.el.getBox();
35449         if(this.split){
35450             box.height += this.split.el.getHeight();
35451         }
35452         return box;
35453     },
35454     
35455     updateBox : function(box){
35456         if(this.split && !this.collapsed){
35457             box.height -= this.split.el.getHeight();
35458             this.split.el.setLeft(box.x);
35459             this.split.el.setTop(box.y+box.height);
35460             this.split.el.setWidth(box.width);
35461         }
35462         if(this.collapsed){
35463             this.updateBody(box.width, null);
35464         }
35465         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35466     }
35467 });
35468
35469 Roo.SouthLayoutRegion = function(mgr, config){
35470     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35471     if(this.split){
35472         this.split.placement = Roo.SplitBar.BOTTOM;
35473         this.split.orientation = Roo.SplitBar.VERTICAL;
35474         this.split.el.addClass("x-layout-split-v");
35475     }
35476     var size = config.initialSize || config.height;
35477     if(typeof size != "undefined"){
35478         this.el.setHeight(size);
35479     }
35480 };
35481 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35482     orientation: Roo.SplitBar.VERTICAL,
35483     getBox : function(){
35484         if(this.collapsed){
35485             return this.collapsedEl.getBox();
35486         }
35487         var box = this.el.getBox();
35488         if(this.split){
35489             var sh = this.split.el.getHeight();
35490             box.height += sh;
35491             box.y -= sh;
35492         }
35493         return box;
35494     },
35495     
35496     updateBox : function(box){
35497         if(this.split && !this.collapsed){
35498             var sh = this.split.el.getHeight();
35499             box.height -= sh;
35500             box.y += sh;
35501             this.split.el.setLeft(box.x);
35502             this.split.el.setTop(box.y-sh);
35503             this.split.el.setWidth(box.width);
35504         }
35505         if(this.collapsed){
35506             this.updateBody(box.width, null);
35507         }
35508         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35509     }
35510 });
35511
35512 Roo.EastLayoutRegion = function(mgr, config){
35513     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35514     if(this.split){
35515         this.split.placement = Roo.SplitBar.RIGHT;
35516         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35517         this.split.el.addClass("x-layout-split-h");
35518     }
35519     var size = config.initialSize || config.width;
35520     if(typeof size != "undefined"){
35521         this.el.setWidth(size);
35522     }
35523 };
35524 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35525     orientation: Roo.SplitBar.HORIZONTAL,
35526     getBox : function(){
35527         if(this.collapsed){
35528             return this.collapsedEl.getBox();
35529         }
35530         var box = this.el.getBox();
35531         if(this.split){
35532             var sw = this.split.el.getWidth();
35533             box.width += sw;
35534             box.x -= sw;
35535         }
35536         return box;
35537     },
35538
35539     updateBox : function(box){
35540         if(this.split && !this.collapsed){
35541             var sw = this.split.el.getWidth();
35542             box.width -= sw;
35543             this.split.el.setLeft(box.x);
35544             this.split.el.setTop(box.y);
35545             this.split.el.setHeight(box.height);
35546             box.x += sw;
35547         }
35548         if(this.collapsed){
35549             this.updateBody(null, box.height);
35550         }
35551         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35552     }
35553 });
35554
35555 Roo.WestLayoutRegion = function(mgr, config){
35556     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35557     if(this.split){
35558         this.split.placement = Roo.SplitBar.LEFT;
35559         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35560         this.split.el.addClass("x-layout-split-h");
35561     }
35562     var size = config.initialSize || config.width;
35563     if(typeof size != "undefined"){
35564         this.el.setWidth(size);
35565     }
35566 };
35567 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35568     orientation: Roo.SplitBar.HORIZONTAL,
35569     getBox : function(){
35570         if(this.collapsed){
35571             return this.collapsedEl.getBox();
35572         }
35573         var box = this.el.getBox();
35574         if(this.split){
35575             box.width += this.split.el.getWidth();
35576         }
35577         return box;
35578     },
35579     
35580     updateBox : function(box){
35581         if(this.split && !this.collapsed){
35582             var sw = this.split.el.getWidth();
35583             box.width -= sw;
35584             this.split.el.setLeft(box.x+box.width);
35585             this.split.el.setTop(box.y);
35586             this.split.el.setHeight(box.height);
35587         }
35588         if(this.collapsed){
35589             this.updateBody(null, box.height);
35590         }
35591         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35592     }
35593 });
35594 /*
35595  * Based on:
35596  * Ext JS Library 1.1.1
35597  * Copyright(c) 2006-2007, Ext JS, LLC.
35598  *
35599  * Originally Released Under LGPL - original licence link has changed is not relivant.
35600  *
35601  * Fork - LGPL
35602  * <script type="text/javascript">
35603  */
35604  
35605  
35606 /*
35607  * Private internal class for reading and applying state
35608  */
35609 Roo.LayoutStateManager = function(layout){
35610      // default empty state
35611      this.state = {
35612         north: {},
35613         south: {},
35614         east: {},
35615         west: {}       
35616     };
35617 };
35618
35619 Roo.LayoutStateManager.prototype = {
35620     init : function(layout, provider){
35621         this.provider = provider;
35622         var state = provider.get(layout.id+"-layout-state");
35623         if(state){
35624             var wasUpdating = layout.isUpdating();
35625             if(!wasUpdating){
35626                 layout.beginUpdate();
35627             }
35628             for(var key in state){
35629                 if(typeof state[key] != "function"){
35630                     var rstate = state[key];
35631                     var r = layout.getRegion(key);
35632                     if(r && rstate){
35633                         if(rstate.size){
35634                             r.resizeTo(rstate.size);
35635                         }
35636                         if(rstate.collapsed == true){
35637                             r.collapse(true);
35638                         }else{
35639                             r.expand(null, true);
35640                         }
35641                     }
35642                 }
35643             }
35644             if(!wasUpdating){
35645                 layout.endUpdate();
35646             }
35647             this.state = state; 
35648         }
35649         this.layout = layout;
35650         layout.on("regionresized", this.onRegionResized, this);
35651         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35652         layout.on("regionexpanded", this.onRegionExpanded, this);
35653     },
35654     
35655     storeState : function(){
35656         this.provider.set(this.layout.id+"-layout-state", this.state);
35657     },
35658     
35659     onRegionResized : function(region, newSize){
35660         this.state[region.getPosition()].size = newSize;
35661         this.storeState();
35662     },
35663     
35664     onRegionCollapsed : function(region){
35665         this.state[region.getPosition()].collapsed = true;
35666         this.storeState();
35667     },
35668     
35669     onRegionExpanded : function(region){
35670         this.state[region.getPosition()].collapsed = false;
35671         this.storeState();
35672     }
35673 };/*
35674  * Based on:
35675  * Ext JS Library 1.1.1
35676  * Copyright(c) 2006-2007, Ext JS, LLC.
35677  *
35678  * Originally Released Under LGPL - original licence link has changed is not relivant.
35679  *
35680  * Fork - LGPL
35681  * <script type="text/javascript">
35682  */
35683 /**
35684  * @class Roo.ContentPanel
35685  * @extends Roo.util.Observable
35686  * @children Roo.form.Form Roo.JsonView Roo.View
35687  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35688  * A basic ContentPanel element.
35689  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35690  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35691  * @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
35692  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35693  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35694  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35695  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35696  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35697  * @cfg {String} title          The title for this panel
35698  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35699  * @cfg {String} url            Calls {@link #setUrl} with this value
35700  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35701  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35702  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35703  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35704  * @cfg {String}    style  Extra style to add to the content panel
35705  * @cfg {Roo.menu.Menu} menu  popup menu
35706
35707  * @constructor
35708  * Create a new ContentPanel.
35709  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35710  * @param {String/Object} config A string to set only the title or a config object
35711  * @param {String} content (optional) Set the HTML content for this panel
35712  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35713  */
35714 Roo.ContentPanel = function(el, config, content){
35715     
35716      
35717     /*
35718     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35719         config = el;
35720         el = Roo.id();
35721     }
35722     if (config && config.parentLayout) { 
35723         el = config.parentLayout.el.createChild(); 
35724     }
35725     */
35726     if(el.autoCreate){ // xtype is available if this is called from factory
35727         config = el;
35728         el = Roo.id();
35729     }
35730     this.el = Roo.get(el);
35731     if(!this.el && config && config.autoCreate){
35732         if(typeof config.autoCreate == "object"){
35733             if(!config.autoCreate.id){
35734                 config.autoCreate.id = config.id||el;
35735             }
35736             this.el = Roo.DomHelper.append(document.body,
35737                         config.autoCreate, true);
35738         }else{
35739             this.el = Roo.DomHelper.append(document.body,
35740                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35741         }
35742     }
35743     
35744     
35745     this.closable = false;
35746     this.loaded = false;
35747     this.active = false;
35748     if(typeof config == "string"){
35749         this.title = config;
35750     }else{
35751         Roo.apply(this, config);
35752     }
35753     
35754     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35755         this.wrapEl = this.el.wrap();
35756         this.toolbar.container = this.el.insertSibling(false, 'before');
35757         this.toolbar = new Roo.Toolbar(this.toolbar);
35758     }
35759     
35760     // xtype created footer. - not sure if will work as we normally have to render first..
35761     if (this.footer && !this.footer.el && this.footer.xtype) {
35762         if (!this.wrapEl) {
35763             this.wrapEl = this.el.wrap();
35764         }
35765     
35766         this.footer.container = this.wrapEl.createChild();
35767          
35768         this.footer = Roo.factory(this.footer, Roo);
35769         
35770     }
35771     
35772     if(this.resizeEl){
35773         this.resizeEl = Roo.get(this.resizeEl, true);
35774     }else{
35775         this.resizeEl = this.el;
35776     }
35777     // handle view.xtype
35778     
35779  
35780     
35781     
35782     this.addEvents({
35783         /**
35784          * @event activate
35785          * Fires when this panel is activated. 
35786          * @param {Roo.ContentPanel} this
35787          */
35788         "activate" : true,
35789         /**
35790          * @event deactivate
35791          * Fires when this panel is activated. 
35792          * @param {Roo.ContentPanel} this
35793          */
35794         "deactivate" : true,
35795
35796         /**
35797          * @event resize
35798          * Fires when this panel is resized if fitToFrame is true.
35799          * @param {Roo.ContentPanel} this
35800          * @param {Number} width The width after any component adjustments
35801          * @param {Number} height The height after any component adjustments
35802          */
35803         "resize" : true,
35804         
35805          /**
35806          * @event render
35807          * Fires when this tab is created
35808          * @param {Roo.ContentPanel} this
35809          */
35810         "render" : true
35811          
35812         
35813     });
35814     
35815
35816     
35817     
35818     if(this.autoScroll){
35819         this.resizeEl.setStyle("overflow", "auto");
35820     } else {
35821         // fix randome scrolling
35822         this.el.on('scroll', function() {
35823             Roo.log('fix random scolling');
35824             this.scrollTo('top',0); 
35825         });
35826     }
35827     content = content || this.content;
35828     if(content){
35829         this.setContent(content);
35830     }
35831     if(config && config.url){
35832         this.setUrl(this.url, this.params, this.loadOnce);
35833     }
35834     
35835     
35836     
35837     Roo.ContentPanel.superclass.constructor.call(this);
35838     
35839     if (this.view && typeof(this.view.xtype) != 'undefined') {
35840         this.view.el = this.el.appendChild(document.createElement("div"));
35841         this.view = Roo.factory(this.view); 
35842         this.view.render  &&  this.view.render(false, '');  
35843     }
35844     
35845     
35846     this.fireEvent('render', this);
35847 };
35848
35849 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35850     tabTip:'',
35851     setRegion : function(region){
35852         this.region = region;
35853         if(region){
35854            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35855         }else{
35856            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35857         } 
35858     },
35859     
35860     /**
35861      * Returns the toolbar for this Panel if one was configured. 
35862      * @return {Roo.Toolbar} 
35863      */
35864     getToolbar : function(){
35865         return this.toolbar;
35866     },
35867     
35868     setActiveState : function(active){
35869         this.active = active;
35870         if(!active){
35871             this.fireEvent("deactivate", this);
35872         }else{
35873             this.fireEvent("activate", this);
35874         }
35875     },
35876     /**
35877      * Updates this panel's element
35878      * @param {String} content The new content
35879      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35880     */
35881     setContent : function(content, loadScripts){
35882         this.el.update(content, loadScripts);
35883     },
35884
35885     ignoreResize : function(w, h){
35886         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35887             return true;
35888         }else{
35889             this.lastSize = {width: w, height: h};
35890             return false;
35891         }
35892     },
35893     /**
35894      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35895      * @return {Roo.UpdateManager} The UpdateManager
35896      */
35897     getUpdateManager : function(){
35898         return this.el.getUpdateManager();
35899     },
35900      /**
35901      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35902      * @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:
35903 <pre><code>
35904 panel.load({
35905     url: "your-url.php",
35906     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35907     callback: yourFunction,
35908     scope: yourObject, //(optional scope)
35909     discardUrl: false,
35910     nocache: false,
35911     text: "Loading...",
35912     timeout: 30,
35913     scripts: false
35914 });
35915 </code></pre>
35916      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35917      * 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.
35918      * @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}
35919      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35920      * @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.
35921      * @return {Roo.ContentPanel} this
35922      */
35923     load : function(){
35924         var um = this.el.getUpdateManager();
35925         um.update.apply(um, arguments);
35926         return this;
35927     },
35928
35929
35930     /**
35931      * 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.
35932      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35933      * @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)
35934      * @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)
35935      * @return {Roo.UpdateManager} The UpdateManager
35936      */
35937     setUrl : function(url, params, loadOnce){
35938         if(this.refreshDelegate){
35939             this.removeListener("activate", this.refreshDelegate);
35940         }
35941         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35942         this.on("activate", this.refreshDelegate);
35943         return this.el.getUpdateManager();
35944     },
35945     
35946     _handleRefresh : function(url, params, loadOnce){
35947         if(!loadOnce || !this.loaded){
35948             var updater = this.el.getUpdateManager();
35949             updater.update(url, params, this._setLoaded.createDelegate(this));
35950         }
35951     },
35952     
35953     _setLoaded : function(){
35954         this.loaded = true;
35955     }, 
35956     
35957     /**
35958      * Returns this panel's id
35959      * @return {String} 
35960      */
35961     getId : function(){
35962         return this.el.id;
35963     },
35964     
35965     /** 
35966      * Returns this panel's element - used by regiosn to add.
35967      * @return {Roo.Element} 
35968      */
35969     getEl : function(){
35970         return this.wrapEl || this.el;
35971     },
35972     
35973     adjustForComponents : function(width, height)
35974     {
35975         //Roo.log('adjustForComponents ');
35976         if(this.resizeEl != this.el){
35977             width -= this.el.getFrameWidth('lr');
35978             height -= this.el.getFrameWidth('tb');
35979         }
35980         if(this.toolbar){
35981             var te = this.toolbar.getEl();
35982             height -= te.getHeight();
35983             te.setWidth(width);
35984         }
35985         if(this.footer){
35986             var te = this.footer.getEl();
35987             //Roo.log("footer:" + te.getHeight());
35988             
35989             height -= te.getHeight();
35990             te.setWidth(width);
35991         }
35992         
35993         
35994         if(this.adjustments){
35995             width += this.adjustments[0];
35996             height += this.adjustments[1];
35997         }
35998         return {"width": width, "height": height};
35999     },
36000     
36001     setSize : function(width, height){
36002         if(this.fitToFrame && !this.ignoreResize(width, height)){
36003             if(this.fitContainer && this.resizeEl != this.el){
36004                 this.el.setSize(width, height);
36005             }
36006             var size = this.adjustForComponents(width, height);
36007             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36008             this.fireEvent('resize', this, size.width, size.height);
36009         }
36010     },
36011     
36012     /**
36013      * Returns this panel's title
36014      * @return {String} 
36015      */
36016     getTitle : function(){
36017         return this.title;
36018     },
36019     
36020     /**
36021      * Set this panel's title
36022      * @param {String} title
36023      */
36024     setTitle : function(title){
36025         this.title = title;
36026         if(this.region){
36027             this.region.updatePanelTitle(this, title);
36028         }
36029     },
36030     
36031     /**
36032      * Returns true is this panel was configured to be closable
36033      * @return {Boolean} 
36034      */
36035     isClosable : function(){
36036         return this.closable;
36037     },
36038     
36039     beforeSlide : function(){
36040         this.el.clip();
36041         this.resizeEl.clip();
36042     },
36043     
36044     afterSlide : function(){
36045         this.el.unclip();
36046         this.resizeEl.unclip();
36047     },
36048     
36049     /**
36050      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36051      *   Will fail silently if the {@link #setUrl} method has not been called.
36052      *   This does not activate the panel, just updates its content.
36053      */
36054     refresh : function(){
36055         if(this.refreshDelegate){
36056            this.loaded = false;
36057            this.refreshDelegate();
36058         }
36059     },
36060     
36061     /**
36062      * Destroys this panel
36063      */
36064     destroy : function(){
36065         this.el.removeAllListeners();
36066         var tempEl = document.createElement("span");
36067         tempEl.appendChild(this.el.dom);
36068         tempEl.innerHTML = "";
36069         this.el.remove();
36070         this.el = null;
36071     },
36072     
36073     /**
36074      * form - if the content panel contains a form - this is a reference to it.
36075      * @type {Roo.form.Form}
36076      */
36077     form : false,
36078     /**
36079      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36080      *    This contains a reference to it.
36081      * @type {Roo.View}
36082      */
36083     view : false,
36084     
36085       /**
36086      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36087      * <pre><code>
36088
36089 layout.addxtype({
36090        xtype : 'Form',
36091        items: [ .... ]
36092    }
36093 );
36094
36095 </code></pre>
36096      * @param {Object} cfg Xtype definition of item to add.
36097      */
36098     
36099     addxtype : function(cfg) {
36100         // add form..
36101         if (cfg.xtype.match(/^Form$/)) {
36102             
36103             var el;
36104             //if (this.footer) {
36105             //    el = this.footer.container.insertSibling(false, 'before');
36106             //} else {
36107                 el = this.el.createChild();
36108             //}
36109
36110             this.form = new  Roo.form.Form(cfg);
36111             
36112             
36113             if ( this.form.allItems.length) {
36114                 this.form.render(el.dom);
36115             }
36116             return this.form;
36117         }
36118         // should only have one of theses..
36119         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36120             // views.. should not be just added - used named prop 'view''
36121             
36122             cfg.el = this.el.appendChild(document.createElement("div"));
36123             // factory?
36124             
36125             var ret = new Roo.factory(cfg);
36126              
36127              ret.render && ret.render(false, ''); // render blank..
36128             this.view = ret;
36129             return ret;
36130         }
36131         return false;
36132     }
36133 });
36134
36135 /**
36136  * @class Roo.GridPanel
36137  * @extends Roo.ContentPanel
36138  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36139  * @constructor
36140  * Create a new GridPanel.
36141  * @cfg {Roo.grid.Grid} grid The grid for this panel
36142  */
36143 Roo.GridPanel = function(grid, config){
36144     
36145     // universal ctor...
36146     if (typeof(grid.grid) != 'undefined') {
36147         config = grid;
36148         grid = config.grid;
36149     }
36150     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36151         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36152         
36153     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36154     
36155     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36156     
36157     if(this.toolbar){
36158         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36159     }
36160     // xtype created footer. - not sure if will work as we normally have to render first..
36161     if (this.footer && !this.footer.el && this.footer.xtype) {
36162         
36163         this.footer.container = this.grid.getView().getFooterPanel(true);
36164         this.footer.dataSource = this.grid.dataSource;
36165         this.footer = Roo.factory(this.footer, Roo);
36166         
36167     }
36168     
36169     grid.monitorWindowResize = false; // turn off autosizing
36170     grid.autoHeight = false;
36171     grid.autoWidth = false;
36172     this.grid = grid;
36173     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36174 };
36175
36176 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36177     getId : function(){
36178         return this.grid.id;
36179     },
36180     
36181     /**
36182      * Returns the grid for this panel
36183      * @return {Roo.grid.Grid} 
36184      */
36185     getGrid : function(){
36186         return this.grid;    
36187     },
36188     
36189     setSize : function(width, height){
36190         if(!this.ignoreResize(width, height)){
36191             var grid = this.grid;
36192             var size = this.adjustForComponents(width, height);
36193             grid.getGridEl().setSize(size.width, size.height);
36194             grid.autoSize();
36195         }
36196     },
36197     
36198     beforeSlide : function(){
36199         this.grid.getView().scroller.clip();
36200     },
36201     
36202     afterSlide : function(){
36203         this.grid.getView().scroller.unclip();
36204     },
36205     
36206     destroy : function(){
36207         this.grid.destroy();
36208         delete this.grid;
36209         Roo.GridPanel.superclass.destroy.call(this); 
36210     }
36211 });
36212
36213
36214 /**
36215  * @class Roo.NestedLayoutPanel
36216  * @extends Roo.ContentPanel
36217  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36218  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
36219  *
36220  * 
36221  * @constructor
36222  * Create a new NestedLayoutPanel.
36223  * 
36224  * 
36225  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36226  * @param {String/Object} config A string to set only the title or a config object
36227  */
36228 Roo.NestedLayoutPanel = function(layout, config)
36229 {
36230     // construct with only one argument..
36231     /* FIXME - implement nicer consturctors
36232     if (layout.layout) {
36233         config = layout;
36234         layout = config.layout;
36235         delete config.layout;
36236     }
36237     if (layout.xtype && !layout.getEl) {
36238         // then layout needs constructing..
36239         layout = Roo.factory(layout, Roo);
36240     }
36241     */
36242     
36243     
36244     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36245     
36246     layout.monitorWindowResize = false; // turn off autosizing
36247     this.layout = layout;
36248     this.layout.getEl().addClass("x-layout-nested-layout");
36249     
36250     
36251     
36252     
36253 };
36254
36255 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36256
36257     setSize : function(width, height){
36258         if(!this.ignoreResize(width, height)){
36259             var size = this.adjustForComponents(width, height);
36260             var el = this.layout.getEl();
36261             el.setSize(size.width, size.height);
36262             var touch = el.dom.offsetWidth;
36263             this.layout.layout();
36264             // ie requires a double layout on the first pass
36265             if(Roo.isIE && !this.initialized){
36266                 this.initialized = true;
36267                 this.layout.layout();
36268             }
36269         }
36270     },
36271     
36272     // activate all subpanels if not currently active..
36273     
36274     setActiveState : function(active){
36275         this.active = active;
36276         if(!active){
36277             this.fireEvent("deactivate", this);
36278             return;
36279         }
36280         
36281         this.fireEvent("activate", this);
36282         // not sure if this should happen before or after..
36283         if (!this.layout) {
36284             return; // should not happen..
36285         }
36286         var reg = false;
36287         for (var r in this.layout.regions) {
36288             reg = this.layout.getRegion(r);
36289             if (reg.getActivePanel()) {
36290                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36291                 reg.setActivePanel(reg.getActivePanel());
36292                 continue;
36293             }
36294             if (!reg.panels.length) {
36295                 continue;
36296             }
36297             reg.showPanel(reg.getPanel(0));
36298         }
36299         
36300         
36301         
36302         
36303     },
36304     
36305     /**
36306      * Returns the nested BorderLayout for this panel
36307      * @return {Roo.BorderLayout} 
36308      */
36309     getLayout : function(){
36310         return this.layout;
36311     },
36312     
36313      /**
36314      * Adds a xtype elements to the layout of the nested panel
36315      * <pre><code>
36316
36317 panel.addxtype({
36318        xtype : 'ContentPanel',
36319        region: 'west',
36320        items: [ .... ]
36321    }
36322 );
36323
36324 panel.addxtype({
36325         xtype : 'NestedLayoutPanel',
36326         region: 'west',
36327         layout: {
36328            center: { },
36329            west: { }   
36330         },
36331         items : [ ... list of content panels or nested layout panels.. ]
36332    }
36333 );
36334 </code></pre>
36335      * @param {Object} cfg Xtype definition of item to add.
36336      */
36337     addxtype : function(cfg) {
36338         return this.layout.addxtype(cfg);
36339     
36340     }
36341 });
36342
36343 Roo.ScrollPanel = function(el, config, content){
36344     config = config || {};
36345     config.fitToFrame = true;
36346     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36347     
36348     this.el.dom.style.overflow = "hidden";
36349     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36350     this.el.removeClass("x-layout-inactive-content");
36351     this.el.on("mousewheel", this.onWheel, this);
36352
36353     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36354     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36355     up.unselectable(); down.unselectable();
36356     up.on("click", this.scrollUp, this);
36357     down.on("click", this.scrollDown, this);
36358     up.addClassOnOver("x-scroller-btn-over");
36359     down.addClassOnOver("x-scroller-btn-over");
36360     up.addClassOnClick("x-scroller-btn-click");
36361     down.addClassOnClick("x-scroller-btn-click");
36362     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36363
36364     this.resizeEl = this.el;
36365     this.el = wrap; this.up = up; this.down = down;
36366 };
36367
36368 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36369     increment : 100,
36370     wheelIncrement : 5,
36371     scrollUp : function(){
36372         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36373     },
36374
36375     scrollDown : function(){
36376         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36377     },
36378
36379     afterScroll : function(){
36380         var el = this.resizeEl;
36381         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36382         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36383         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36384     },
36385
36386     setSize : function(){
36387         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36388         this.afterScroll();
36389     },
36390
36391     onWheel : function(e){
36392         var d = e.getWheelDelta();
36393         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36394         this.afterScroll();
36395         e.stopEvent();
36396     },
36397
36398     setContent : function(content, loadScripts){
36399         this.resizeEl.update(content, loadScripts);
36400     }
36401
36402 });
36403
36404
36405
36406 /**
36407  * @class Roo.TreePanel
36408  * @extends Roo.ContentPanel
36409  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36410  * Treepanel component
36411  * 
36412  * @constructor
36413  * Create a new TreePanel. - defaults to fit/scoll contents.
36414  * @param {String/Object} config A string to set only the panel's title, or a config object
36415  */
36416 Roo.TreePanel = function(config){
36417     var el = config.el;
36418     var tree = config.tree;
36419     delete config.tree; 
36420     delete config.el; // hopefull!
36421     
36422     // wrapper for IE7 strict & safari scroll issue
36423     
36424     var treeEl = el.createChild();
36425     config.resizeEl = treeEl;
36426     
36427     
36428     
36429     Roo.TreePanel.superclass.constructor.call(this, el, config);
36430  
36431  
36432     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36433     //console.log(tree);
36434     this.on('activate', function()
36435     {
36436         if (this.tree.rendered) {
36437             return;
36438         }
36439         //console.log('render tree');
36440         this.tree.render();
36441     });
36442     // this should not be needed.. - it's actually the 'el' that resizes?
36443     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36444     
36445     //this.on('resize',  function (cp, w, h) {
36446     //        this.tree.innerCt.setWidth(w);
36447     //        this.tree.innerCt.setHeight(h);
36448     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36449     //});
36450
36451         
36452     
36453 };
36454
36455 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36456     fitToFrame : true,
36457     autoScroll : true,
36458     /*
36459      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36460      */
36461     tree : false
36462
36463 });
36464
36465
36466
36467
36468
36469
36470
36471
36472
36473
36474
36475 /*
36476  * Based on:
36477  * Ext JS Library 1.1.1
36478  * Copyright(c) 2006-2007, Ext JS, LLC.
36479  *
36480  * Originally Released Under LGPL - original licence link has changed is not relivant.
36481  *
36482  * Fork - LGPL
36483  * <script type="text/javascript">
36484  */
36485  
36486
36487 /**
36488  * @class Roo.ReaderLayout
36489  * @extends Roo.BorderLayout
36490  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36491  * center region containing two nested regions (a top one for a list view and one for item preview below),
36492  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36493  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36494  * expedites the setup of the overall layout and regions for this common application style.
36495  * Example:
36496  <pre><code>
36497 var reader = new Roo.ReaderLayout();
36498 var CP = Roo.ContentPanel;  // shortcut for adding
36499
36500 reader.beginUpdate();
36501 reader.add("north", new CP("north", "North"));
36502 reader.add("west", new CP("west", {title: "West"}));
36503 reader.add("east", new CP("east", {title: "East"}));
36504
36505 reader.regions.listView.add(new CP("listView", "List"));
36506 reader.regions.preview.add(new CP("preview", "Preview"));
36507 reader.endUpdate();
36508 </code></pre>
36509 * @constructor
36510 * Create a new ReaderLayout
36511 * @param {Object} config Configuration options
36512 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36513 * document.body if omitted)
36514 */
36515 Roo.ReaderLayout = function(config, renderTo){
36516     var c = config || {size:{}};
36517     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36518         north: c.north !== false ? Roo.apply({
36519             split:false,
36520             initialSize: 32,
36521             titlebar: false
36522         }, c.north) : false,
36523         west: c.west !== false ? Roo.apply({
36524             split:true,
36525             initialSize: 200,
36526             minSize: 175,
36527             maxSize: 400,
36528             titlebar: true,
36529             collapsible: true,
36530             animate: true,
36531             margins:{left:5,right:0,bottom:5,top:5},
36532             cmargins:{left:5,right:5,bottom:5,top:5}
36533         }, c.west) : false,
36534         east: c.east !== false ? Roo.apply({
36535             split:true,
36536             initialSize: 200,
36537             minSize: 175,
36538             maxSize: 400,
36539             titlebar: true,
36540             collapsible: true,
36541             animate: true,
36542             margins:{left:0,right:5,bottom:5,top:5},
36543             cmargins:{left:5,right:5,bottom:5,top:5}
36544         }, c.east) : false,
36545         center: Roo.apply({
36546             tabPosition: 'top',
36547             autoScroll:false,
36548             closeOnTab: true,
36549             titlebar:false,
36550             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36551         }, c.center)
36552     });
36553
36554     this.el.addClass('x-reader');
36555
36556     this.beginUpdate();
36557
36558     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36559         south: c.preview !== false ? Roo.apply({
36560             split:true,
36561             initialSize: 200,
36562             minSize: 100,
36563             autoScroll:true,
36564             collapsible:true,
36565             titlebar: true,
36566             cmargins:{top:5,left:0, right:0, bottom:0}
36567         }, c.preview) : false,
36568         center: Roo.apply({
36569             autoScroll:false,
36570             titlebar:false,
36571             minHeight:200
36572         }, c.listView)
36573     });
36574     this.add('center', new Roo.NestedLayoutPanel(inner,
36575             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36576
36577     this.endUpdate();
36578
36579     this.regions.preview = inner.getRegion('south');
36580     this.regions.listView = inner.getRegion('center');
36581 };
36582
36583 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36584  * Based on:
36585  * Ext JS Library 1.1.1
36586  * Copyright(c) 2006-2007, Ext JS, LLC.
36587  *
36588  * Originally Released Under LGPL - original licence link has changed is not relivant.
36589  *
36590  * Fork - LGPL
36591  * <script type="text/javascript">
36592  */
36593  
36594 /**
36595  * @class Roo.grid.Grid
36596  * @extends Roo.util.Observable
36597  * This class represents the primary interface of a component based grid control.
36598  * <br><br>Usage:<pre><code>
36599  var grid = new Roo.grid.Grid("my-container-id", {
36600      ds: myDataStore,
36601      cm: myColModel,
36602      selModel: mySelectionModel,
36603      autoSizeColumns: true,
36604      monitorWindowResize: false,
36605      trackMouseOver: true
36606  });
36607  // set any options
36608  grid.render();
36609  * </code></pre>
36610  * <b>Common Problems:</b><br/>
36611  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36612  * element will correct this<br/>
36613  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36614  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36615  * are unpredictable.<br/>
36616  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36617  * grid to calculate dimensions/offsets.<br/>
36618   * @constructor
36619  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36620  * The container MUST have some type of size defined for the grid to fill. The container will be
36621  * automatically set to position relative if it isn't already.
36622  * @param {Object} config A config object that sets properties on this grid.
36623  */
36624 Roo.grid.Grid = function(container, config){
36625         // initialize the container
36626         this.container = Roo.get(container);
36627         this.container.update("");
36628         this.container.setStyle("overflow", "hidden");
36629     this.container.addClass('x-grid-container');
36630
36631     this.id = this.container.id;
36632
36633     Roo.apply(this, config);
36634     // check and correct shorthanded configs
36635     if(this.ds){
36636         this.dataSource = this.ds;
36637         delete this.ds;
36638     }
36639     if(this.cm){
36640         this.colModel = this.cm;
36641         delete this.cm;
36642     }
36643     if(this.sm){
36644         this.selModel = this.sm;
36645         delete this.sm;
36646     }
36647
36648     if (this.selModel) {
36649         this.selModel = Roo.factory(this.selModel, Roo.grid);
36650         this.sm = this.selModel;
36651         this.sm.xmodule = this.xmodule || false;
36652     }
36653     if (typeof(this.colModel.config) == 'undefined') {
36654         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36655         this.cm = this.colModel;
36656         this.cm.xmodule = this.xmodule || false;
36657     }
36658     if (this.dataSource) {
36659         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36660         this.ds = this.dataSource;
36661         this.ds.xmodule = this.xmodule || false;
36662          
36663     }
36664     
36665     
36666     
36667     if(this.width){
36668         this.container.setWidth(this.width);
36669     }
36670
36671     if(this.height){
36672         this.container.setHeight(this.height);
36673     }
36674     /** @private */
36675         this.addEvents({
36676         // raw events
36677         /**
36678          * @event click
36679          * The raw click event for the entire grid.
36680          * @param {Roo.EventObject} e
36681          */
36682         "click" : true,
36683         /**
36684          * @event dblclick
36685          * The raw dblclick event for the entire grid.
36686          * @param {Roo.EventObject} e
36687          */
36688         "dblclick" : true,
36689         /**
36690          * @event contextmenu
36691          * The raw contextmenu event for the entire grid.
36692          * @param {Roo.EventObject} e
36693          */
36694         "contextmenu" : true,
36695         /**
36696          * @event mousedown
36697          * The raw mousedown event for the entire grid.
36698          * @param {Roo.EventObject} e
36699          */
36700         "mousedown" : true,
36701         /**
36702          * @event mouseup
36703          * The raw mouseup event for the entire grid.
36704          * @param {Roo.EventObject} e
36705          */
36706         "mouseup" : true,
36707         /**
36708          * @event mouseover
36709          * The raw mouseover event for the entire grid.
36710          * @param {Roo.EventObject} e
36711          */
36712         "mouseover" : true,
36713         /**
36714          * @event mouseout
36715          * The raw mouseout event for the entire grid.
36716          * @param {Roo.EventObject} e
36717          */
36718         "mouseout" : true,
36719         /**
36720          * @event keypress
36721          * The raw keypress event for the entire grid.
36722          * @param {Roo.EventObject} e
36723          */
36724         "keypress" : true,
36725         /**
36726          * @event keydown
36727          * The raw keydown event for the entire grid.
36728          * @param {Roo.EventObject} e
36729          */
36730         "keydown" : true,
36731
36732         // custom events
36733
36734         /**
36735          * @event cellclick
36736          * Fires when a cell is clicked
36737          * @param {Grid} this
36738          * @param {Number} rowIndex
36739          * @param {Number} columnIndex
36740          * @param {Roo.EventObject} e
36741          */
36742         "cellclick" : true,
36743         /**
36744          * @event celldblclick
36745          * Fires when a cell is double clicked
36746          * @param {Grid} this
36747          * @param {Number} rowIndex
36748          * @param {Number} columnIndex
36749          * @param {Roo.EventObject} e
36750          */
36751         "celldblclick" : true,
36752         /**
36753          * @event rowclick
36754          * Fires when a row is clicked
36755          * @param {Grid} this
36756          * @param {Number} rowIndex
36757          * @param {Roo.EventObject} e
36758          */
36759         "rowclick" : true,
36760         /**
36761          * @event rowdblclick
36762          * Fires when a row is double clicked
36763          * @param {Grid} this
36764          * @param {Number} rowIndex
36765          * @param {Roo.EventObject} e
36766          */
36767         "rowdblclick" : true,
36768         /**
36769          * @event headerclick
36770          * Fires when a header is clicked
36771          * @param {Grid} this
36772          * @param {Number} columnIndex
36773          * @param {Roo.EventObject} e
36774          */
36775         "headerclick" : true,
36776         /**
36777          * @event headerdblclick
36778          * Fires when a header cell is double clicked
36779          * @param {Grid} this
36780          * @param {Number} columnIndex
36781          * @param {Roo.EventObject} e
36782          */
36783         "headerdblclick" : true,
36784         /**
36785          * @event rowcontextmenu
36786          * Fires when a row is right clicked
36787          * @param {Grid} this
36788          * @param {Number} rowIndex
36789          * @param {Roo.EventObject} e
36790          */
36791         "rowcontextmenu" : true,
36792         /**
36793          * @event cellcontextmenu
36794          * Fires when a cell is right clicked
36795          * @param {Grid} this
36796          * @param {Number} rowIndex
36797          * @param {Number} cellIndex
36798          * @param {Roo.EventObject} e
36799          */
36800          "cellcontextmenu" : true,
36801         /**
36802          * @event headercontextmenu
36803          * Fires when a header is right clicked
36804          * @param {Grid} this
36805          * @param {Number} columnIndex
36806          * @param {Roo.EventObject} e
36807          */
36808         "headercontextmenu" : true,
36809         /**
36810          * @event bodyscroll
36811          * Fires when the body element is scrolled
36812          * @param {Number} scrollLeft
36813          * @param {Number} scrollTop
36814          */
36815         "bodyscroll" : true,
36816         /**
36817          * @event columnresize
36818          * Fires when the user resizes a column
36819          * @param {Number} columnIndex
36820          * @param {Number} newSize
36821          */
36822         "columnresize" : true,
36823         /**
36824          * @event columnmove
36825          * Fires when the user moves a column
36826          * @param {Number} oldIndex
36827          * @param {Number} newIndex
36828          */
36829         "columnmove" : true,
36830         /**
36831          * @event startdrag
36832          * Fires when row(s) start being dragged
36833          * @param {Grid} this
36834          * @param {Roo.GridDD} dd The drag drop object
36835          * @param {event} e The raw browser event
36836          */
36837         "startdrag" : true,
36838         /**
36839          * @event enddrag
36840          * Fires when a drag operation is complete
36841          * @param {Grid} this
36842          * @param {Roo.GridDD} dd The drag drop object
36843          * @param {event} e The raw browser event
36844          */
36845         "enddrag" : true,
36846         /**
36847          * @event dragdrop
36848          * Fires when dragged row(s) are dropped on a valid DD target
36849          * @param {Grid} this
36850          * @param {Roo.GridDD} dd The drag drop object
36851          * @param {String} targetId The target drag drop object
36852          * @param {event} e The raw browser event
36853          */
36854         "dragdrop" : true,
36855         /**
36856          * @event dragover
36857          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36858          * @param {Grid} this
36859          * @param {Roo.GridDD} dd The drag drop object
36860          * @param {String} targetId The target drag drop object
36861          * @param {event} e The raw browser event
36862          */
36863         "dragover" : true,
36864         /**
36865          * @event dragenter
36866          *  Fires when the dragged row(s) first cross another DD target while being dragged
36867          * @param {Grid} this
36868          * @param {Roo.GridDD} dd The drag drop object
36869          * @param {String} targetId The target drag drop object
36870          * @param {event} e The raw browser event
36871          */
36872         "dragenter" : true,
36873         /**
36874          * @event dragout
36875          * Fires when the dragged row(s) leave another DD target while being dragged
36876          * @param {Grid} this
36877          * @param {Roo.GridDD} dd The drag drop object
36878          * @param {String} targetId The target drag drop object
36879          * @param {event} e The raw browser event
36880          */
36881         "dragout" : true,
36882         /**
36883          * @event rowclass
36884          * Fires when a row is rendered, so you can change add a style to it.
36885          * @param {GridView} gridview   The grid view
36886          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36887          */
36888         'rowclass' : true,
36889
36890         /**
36891          * @event render
36892          * Fires when the grid is rendered
36893          * @param {Grid} grid
36894          */
36895         'render' : true
36896     });
36897
36898     Roo.grid.Grid.superclass.constructor.call(this);
36899 };
36900 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36901     
36902     /**
36903          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36904          */
36905         /**
36906          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36907          */
36908         /**
36909          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36910          */
36911         /**
36912          * @cfg {Roo.grid.Store} ds The data store for the grid
36913          */
36914         /**
36915          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36916          */
36917         /**
36918      * @cfg {String} ddGroup - drag drop group.
36919      */
36920       /**
36921      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36922      */
36923
36924     /**
36925      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36926      */
36927     minColumnWidth : 25,
36928
36929     /**
36930      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36931      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36932      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36933      */
36934     autoSizeColumns : false,
36935
36936     /**
36937      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36938      */
36939     autoSizeHeaders : true,
36940
36941     /**
36942      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36943      */
36944     monitorWindowResize : true,
36945
36946     /**
36947      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36948      * rows measured to get a columns size. Default is 0 (all rows).
36949      */
36950     maxRowsToMeasure : 0,
36951
36952     /**
36953      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36954      */
36955     trackMouseOver : true,
36956
36957     /**
36958     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36959     */
36960       /**
36961     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
36962     */
36963     
36964     /**
36965     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36966     */
36967     enableDragDrop : false,
36968     
36969     /**
36970     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36971     */
36972     enableColumnMove : true,
36973     
36974     /**
36975     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36976     */
36977     enableColumnHide : true,
36978     
36979     /**
36980     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36981     */
36982     enableRowHeightSync : false,
36983     
36984     /**
36985     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36986     */
36987     stripeRows : true,
36988     
36989     /**
36990     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36991     */
36992     autoHeight : false,
36993
36994     /**
36995      * @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.
36996      */
36997     autoExpandColumn : false,
36998
36999     /**
37000     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37001     * Default is 50.
37002     */
37003     autoExpandMin : 50,
37004
37005     /**
37006     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37007     */
37008     autoExpandMax : 1000,
37009
37010     /**
37011     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37012     */
37013     view : null,
37014
37015     /**
37016     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37017     */
37018     loadMask : false,
37019     /**
37020     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37021     */
37022     dropTarget: false,
37023      /**
37024     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37025     */ 
37026     sortColMenu : false,
37027     
37028     // private
37029     rendered : false,
37030
37031     /**
37032     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37033     * of a fixed width. Default is false.
37034     */
37035     /**
37036     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37037     */
37038     
37039     
37040     /**
37041     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37042     * %0 is replaced with the number of selected rows.
37043     */
37044     ddText : "{0} selected row{1}",
37045     
37046     
37047     /**
37048      * Called once after all setup has been completed and the grid is ready to be rendered.
37049      * @return {Roo.grid.Grid} this
37050      */
37051     render : function()
37052     {
37053         var c = this.container;
37054         // try to detect autoHeight/width mode
37055         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37056             this.autoHeight = true;
37057         }
37058         var view = this.getView();
37059         view.init(this);
37060
37061         c.on("click", this.onClick, this);
37062         c.on("dblclick", this.onDblClick, this);
37063         c.on("contextmenu", this.onContextMenu, this);
37064         c.on("keydown", this.onKeyDown, this);
37065         if (Roo.isTouch) {
37066             c.on("touchstart", this.onTouchStart, this);
37067         }
37068
37069         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37070
37071         this.getSelectionModel().init(this);
37072
37073         view.render();
37074
37075         if(this.loadMask){
37076             this.loadMask = new Roo.LoadMask(this.container,
37077                     Roo.apply({store:this.dataSource}, this.loadMask));
37078         }
37079         
37080         
37081         if (this.toolbar && this.toolbar.xtype) {
37082             this.toolbar.container = this.getView().getHeaderPanel(true);
37083             this.toolbar = new Roo.Toolbar(this.toolbar);
37084         }
37085         if (this.footer && this.footer.xtype) {
37086             this.footer.dataSource = this.getDataSource();
37087             this.footer.container = this.getView().getFooterPanel(true);
37088             this.footer = Roo.factory(this.footer, Roo);
37089         }
37090         if (this.dropTarget && this.dropTarget.xtype) {
37091             delete this.dropTarget.xtype;
37092             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37093         }
37094         
37095         
37096         this.rendered = true;
37097         this.fireEvent('render', this);
37098         return this;
37099     },
37100
37101     /**
37102      * Reconfigures the grid to use a different Store and Column Model.
37103      * The View will be bound to the new objects and refreshed.
37104      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37105      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37106      */
37107     reconfigure : function(dataSource, colModel){
37108         if(this.loadMask){
37109             this.loadMask.destroy();
37110             this.loadMask = new Roo.LoadMask(this.container,
37111                     Roo.apply({store:dataSource}, this.loadMask));
37112         }
37113         this.view.bind(dataSource, colModel);
37114         this.dataSource = dataSource;
37115         this.colModel = colModel;
37116         this.view.refresh(true);
37117     },
37118     /**
37119      * addColumns
37120      * Add's a column, default at the end..
37121      
37122      * @param {int} position to add (default end)
37123      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37124      */
37125     addColumns : function(pos, ar)
37126     {
37127         
37128         for (var i =0;i< ar.length;i++) {
37129             var cfg = ar[i];
37130             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37131             this.cm.lookup[cfg.id] = cfg;
37132         }
37133         
37134         
37135         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37136             pos = this.cm.config.length; //this.cm.config.push(cfg);
37137         } 
37138         pos = Math.max(0,pos);
37139         ar.unshift(0);
37140         ar.unshift(pos);
37141         this.cm.config.splice.apply(this.cm.config, ar);
37142         
37143         
37144         
37145         this.view.generateRules(this.cm);
37146         this.view.refresh(true);
37147         
37148     },
37149     
37150     
37151     
37152     
37153     // private
37154     onKeyDown : function(e){
37155         this.fireEvent("keydown", e);
37156     },
37157
37158     /**
37159      * Destroy this grid.
37160      * @param {Boolean} removeEl True to remove the element
37161      */
37162     destroy : function(removeEl, keepListeners){
37163         if(this.loadMask){
37164             this.loadMask.destroy();
37165         }
37166         var c = this.container;
37167         c.removeAllListeners();
37168         this.view.destroy();
37169         this.colModel.purgeListeners();
37170         if(!keepListeners){
37171             this.purgeListeners();
37172         }
37173         c.update("");
37174         if(removeEl === true){
37175             c.remove();
37176         }
37177     },
37178
37179     // private
37180     processEvent : function(name, e){
37181         // does this fire select???
37182         //Roo.log('grid:processEvent '  + name);
37183         
37184         if (name != 'touchstart' ) {
37185             this.fireEvent(name, e);    
37186         }
37187         
37188         var t = e.getTarget();
37189         var v = this.view;
37190         var header = v.findHeaderIndex(t);
37191         if(header !== false){
37192             var ename = name == 'touchstart' ? 'click' : name;
37193              
37194             this.fireEvent("header" + ename, this, header, e);
37195         }else{
37196             var row = v.findRowIndex(t);
37197             var cell = v.findCellIndex(t);
37198             if (name == 'touchstart') {
37199                 // first touch is always a click.
37200                 // hopefull this happens after selection is updated.?
37201                 name = false;
37202                 
37203                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37204                     var cs = this.selModel.getSelectedCell();
37205                     if (row == cs[0] && cell == cs[1]){
37206                         name = 'dblclick';
37207                     }
37208                 }
37209                 if (typeof(this.selModel.getSelections) != 'undefined') {
37210                     var cs = this.selModel.getSelections();
37211                     var ds = this.dataSource;
37212                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37213                         name = 'dblclick';
37214                     }
37215                 }
37216                 if (!name) {
37217                     return;
37218                 }
37219             }
37220             
37221             
37222             if(row !== false){
37223                 this.fireEvent("row" + name, this, row, e);
37224                 if(cell !== false){
37225                     this.fireEvent("cell" + name, this, row, cell, e);
37226                 }
37227             }
37228         }
37229     },
37230
37231     // private
37232     onClick : function(e){
37233         this.processEvent("click", e);
37234     },
37235    // private
37236     onTouchStart : function(e){
37237         this.processEvent("touchstart", e);
37238     },
37239
37240     // private
37241     onContextMenu : function(e, t){
37242         this.processEvent("contextmenu", e);
37243     },
37244
37245     // private
37246     onDblClick : function(e){
37247         this.processEvent("dblclick", e);
37248     },
37249
37250     // private
37251     walkCells : function(row, col, step, fn, scope){
37252         var cm = this.colModel, clen = cm.getColumnCount();
37253         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37254         if(step < 0){
37255             if(col < 0){
37256                 row--;
37257                 first = false;
37258             }
37259             while(row >= 0){
37260                 if(!first){
37261                     col = clen-1;
37262                 }
37263                 first = false;
37264                 while(col >= 0){
37265                     if(fn.call(scope || this, row, col, cm) === true){
37266                         return [row, col];
37267                     }
37268                     col--;
37269                 }
37270                 row--;
37271             }
37272         } else {
37273             if(col >= clen){
37274                 row++;
37275                 first = false;
37276             }
37277             while(row < rlen){
37278                 if(!first){
37279                     col = 0;
37280                 }
37281                 first = false;
37282                 while(col < clen){
37283                     if(fn.call(scope || this, row, col, cm) === true){
37284                         return [row, col];
37285                     }
37286                     col++;
37287                 }
37288                 row++;
37289             }
37290         }
37291         return null;
37292     },
37293
37294     // private
37295     getSelections : function(){
37296         return this.selModel.getSelections();
37297     },
37298
37299     /**
37300      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37301      * but if manual update is required this method will initiate it.
37302      */
37303     autoSize : function(){
37304         if(this.rendered){
37305             this.view.layout();
37306             if(this.view.adjustForScroll){
37307                 this.view.adjustForScroll();
37308             }
37309         }
37310     },
37311
37312     /**
37313      * Returns the grid's underlying element.
37314      * @return {Element} The element
37315      */
37316     getGridEl : function(){
37317         return this.container;
37318     },
37319
37320     // private for compatibility, overridden by editor grid
37321     stopEditing : function(){},
37322
37323     /**
37324      * Returns the grid's SelectionModel.
37325      * @return {SelectionModel}
37326      */
37327     getSelectionModel : function(){
37328         if(!this.selModel){
37329             this.selModel = new Roo.grid.RowSelectionModel();
37330         }
37331         return this.selModel;
37332     },
37333
37334     /**
37335      * Returns the grid's DataSource.
37336      * @return {DataSource}
37337      */
37338     getDataSource : function(){
37339         return this.dataSource;
37340     },
37341
37342     /**
37343      * Returns the grid's ColumnModel.
37344      * @return {ColumnModel}
37345      */
37346     getColumnModel : function(){
37347         return this.colModel;
37348     },
37349
37350     /**
37351      * Returns the grid's GridView object.
37352      * @return {GridView}
37353      */
37354     getView : function(){
37355         if(!this.view){
37356             this.view = new Roo.grid.GridView(this.viewConfig);
37357             this.relayEvents(this.view, [
37358                 "beforerowremoved", "beforerowsinserted",
37359                 "beforerefresh", "rowremoved",
37360                 "rowsinserted", "rowupdated" ,"refresh"
37361             ]);
37362         }
37363         return this.view;
37364     },
37365     /**
37366      * Called to get grid's drag proxy text, by default returns this.ddText.
37367      * Override this to put something different in the dragged text.
37368      * @return {String}
37369      */
37370     getDragDropText : function(){
37371         var count = this.selModel.getCount();
37372         return String.format(this.ddText, count, count == 1 ? '' : 's');
37373     }
37374 });
37375 /*
37376  * Based on:
37377  * Ext JS Library 1.1.1
37378  * Copyright(c) 2006-2007, Ext JS, LLC.
37379  *
37380  * Originally Released Under LGPL - original licence link has changed is not relivant.
37381  *
37382  * Fork - LGPL
37383  * <script type="text/javascript">
37384  */
37385  /**
37386  * @class Roo.grid.AbstractGridView
37387  * @extends Roo.util.Observable
37388  * @abstract
37389  * Abstract base class for grid Views
37390  * @constructor
37391  */
37392 Roo.grid.AbstractGridView = function(){
37393         this.grid = null;
37394         
37395         this.events = {
37396             "beforerowremoved" : true,
37397             "beforerowsinserted" : true,
37398             "beforerefresh" : true,
37399             "rowremoved" : true,
37400             "rowsinserted" : true,
37401             "rowupdated" : true,
37402             "refresh" : true
37403         };
37404     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37405 };
37406
37407 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37408     rowClass : "x-grid-row",
37409     cellClass : "x-grid-cell",
37410     tdClass : "x-grid-td",
37411     hdClass : "x-grid-hd",
37412     splitClass : "x-grid-hd-split",
37413     
37414     init: function(grid){
37415         this.grid = grid;
37416                 var cid = this.grid.getGridEl().id;
37417         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37418         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37419         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37420         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37421         },
37422         
37423     getColumnRenderers : function(){
37424         var renderers = [];
37425         var cm = this.grid.colModel;
37426         var colCount = cm.getColumnCount();
37427         for(var i = 0; i < colCount; i++){
37428             renderers[i] = cm.getRenderer(i);
37429         }
37430         return renderers;
37431     },
37432     
37433     getColumnIds : function(){
37434         var ids = [];
37435         var cm = this.grid.colModel;
37436         var colCount = cm.getColumnCount();
37437         for(var i = 0; i < colCount; i++){
37438             ids[i] = cm.getColumnId(i);
37439         }
37440         return ids;
37441     },
37442     
37443     getDataIndexes : function(){
37444         if(!this.indexMap){
37445             this.indexMap = this.buildIndexMap();
37446         }
37447         return this.indexMap.colToData;
37448     },
37449     
37450     getColumnIndexByDataIndex : function(dataIndex){
37451         if(!this.indexMap){
37452             this.indexMap = this.buildIndexMap();
37453         }
37454         return this.indexMap.dataToCol[dataIndex];
37455     },
37456     
37457     /**
37458      * Set a css style for a column dynamically. 
37459      * @param {Number} colIndex The index of the column
37460      * @param {String} name The css property name
37461      * @param {String} value The css value
37462      */
37463     setCSSStyle : function(colIndex, name, value){
37464         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37465         Roo.util.CSS.updateRule(selector, name, value);
37466     },
37467     
37468     generateRules : function(cm){
37469         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37470         Roo.util.CSS.removeStyleSheet(rulesId);
37471         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37472             var cid = cm.getColumnId(i);
37473             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37474                          this.tdSelector, cid, " {\n}\n",
37475                          this.hdSelector, cid, " {\n}\n",
37476                          this.splitSelector, cid, " {\n}\n");
37477         }
37478         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37479     }
37480 });/*
37481  * Based on:
37482  * Ext JS Library 1.1.1
37483  * Copyright(c) 2006-2007, Ext JS, LLC.
37484  *
37485  * Originally Released Under LGPL - original licence link has changed is not relivant.
37486  *
37487  * Fork - LGPL
37488  * <script type="text/javascript">
37489  */
37490
37491 // private
37492 // This is a support class used internally by the Grid components
37493 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37494     this.grid = grid;
37495     this.view = grid.getView();
37496     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37497     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37498     if(hd2){
37499         this.setHandleElId(Roo.id(hd));
37500         this.setOuterHandleElId(Roo.id(hd2));
37501     }
37502     this.scroll = false;
37503 };
37504 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37505     maxDragWidth: 120,
37506     getDragData : function(e){
37507         var t = Roo.lib.Event.getTarget(e);
37508         var h = this.view.findHeaderCell(t);
37509         if(h){
37510             return {ddel: h.firstChild, header:h};
37511         }
37512         return false;
37513     },
37514
37515     onInitDrag : function(e){
37516         this.view.headersDisabled = true;
37517         var clone = this.dragData.ddel.cloneNode(true);
37518         clone.id = Roo.id();
37519         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37520         this.proxy.update(clone);
37521         return true;
37522     },
37523
37524     afterValidDrop : function(){
37525         var v = this.view;
37526         setTimeout(function(){
37527             v.headersDisabled = false;
37528         }, 50);
37529     },
37530
37531     afterInvalidDrop : function(){
37532         var v = this.view;
37533         setTimeout(function(){
37534             v.headersDisabled = false;
37535         }, 50);
37536     }
37537 });
37538 /*
37539  * Based on:
37540  * Ext JS Library 1.1.1
37541  * Copyright(c) 2006-2007, Ext JS, LLC.
37542  *
37543  * Originally Released Under LGPL - original licence link has changed is not relivant.
37544  *
37545  * Fork - LGPL
37546  * <script type="text/javascript">
37547  */
37548 // private
37549 // This is a support class used internally by the Grid components
37550 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37551     this.grid = grid;
37552     this.view = grid.getView();
37553     // split the proxies so they don't interfere with mouse events
37554     this.proxyTop = Roo.DomHelper.append(document.body, {
37555         cls:"col-move-top", html:"&#160;"
37556     }, true);
37557     this.proxyBottom = Roo.DomHelper.append(document.body, {
37558         cls:"col-move-bottom", html:"&#160;"
37559     }, true);
37560     this.proxyTop.hide = this.proxyBottom.hide = function(){
37561         this.setLeftTop(-100,-100);
37562         this.setStyle("visibility", "hidden");
37563     };
37564     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37565     // temporarily disabled
37566     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37567     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37568 };
37569 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37570     proxyOffsets : [-4, -9],
37571     fly: Roo.Element.fly,
37572
37573     getTargetFromEvent : function(e){
37574         var t = Roo.lib.Event.getTarget(e);
37575         var cindex = this.view.findCellIndex(t);
37576         if(cindex !== false){
37577             return this.view.getHeaderCell(cindex);
37578         }
37579         return null;
37580     },
37581
37582     nextVisible : function(h){
37583         var v = this.view, cm = this.grid.colModel;
37584         h = h.nextSibling;
37585         while(h){
37586             if(!cm.isHidden(v.getCellIndex(h))){
37587                 return h;
37588             }
37589             h = h.nextSibling;
37590         }
37591         return null;
37592     },
37593
37594     prevVisible : function(h){
37595         var v = this.view, cm = this.grid.colModel;
37596         h = h.prevSibling;
37597         while(h){
37598             if(!cm.isHidden(v.getCellIndex(h))){
37599                 return h;
37600             }
37601             h = h.prevSibling;
37602         }
37603         return null;
37604     },
37605
37606     positionIndicator : function(h, n, e){
37607         var x = Roo.lib.Event.getPageX(e);
37608         var r = Roo.lib.Dom.getRegion(n.firstChild);
37609         var px, pt, py = r.top + this.proxyOffsets[1];
37610         if((r.right - x) <= (r.right-r.left)/2){
37611             px = r.right+this.view.borderWidth;
37612             pt = "after";
37613         }else{
37614             px = r.left;
37615             pt = "before";
37616         }
37617         var oldIndex = this.view.getCellIndex(h);
37618         var newIndex = this.view.getCellIndex(n);
37619
37620         if(this.grid.colModel.isFixed(newIndex)){
37621             return false;
37622         }
37623
37624         var locked = this.grid.colModel.isLocked(newIndex);
37625
37626         if(pt == "after"){
37627             newIndex++;
37628         }
37629         if(oldIndex < newIndex){
37630             newIndex--;
37631         }
37632         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37633             return false;
37634         }
37635         px +=  this.proxyOffsets[0];
37636         this.proxyTop.setLeftTop(px, py);
37637         this.proxyTop.show();
37638         if(!this.bottomOffset){
37639             this.bottomOffset = this.view.mainHd.getHeight();
37640         }
37641         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37642         this.proxyBottom.show();
37643         return pt;
37644     },
37645
37646     onNodeEnter : function(n, dd, e, data){
37647         if(data.header != n){
37648             this.positionIndicator(data.header, n, e);
37649         }
37650     },
37651
37652     onNodeOver : function(n, dd, e, data){
37653         var result = false;
37654         if(data.header != n){
37655             result = this.positionIndicator(data.header, n, e);
37656         }
37657         if(!result){
37658             this.proxyTop.hide();
37659             this.proxyBottom.hide();
37660         }
37661         return result ? this.dropAllowed : this.dropNotAllowed;
37662     },
37663
37664     onNodeOut : function(n, dd, e, data){
37665         this.proxyTop.hide();
37666         this.proxyBottom.hide();
37667     },
37668
37669     onNodeDrop : function(n, dd, e, data){
37670         var h = data.header;
37671         if(h != n){
37672             var cm = this.grid.colModel;
37673             var x = Roo.lib.Event.getPageX(e);
37674             var r = Roo.lib.Dom.getRegion(n.firstChild);
37675             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37676             var oldIndex = this.view.getCellIndex(h);
37677             var newIndex = this.view.getCellIndex(n);
37678             var locked = cm.isLocked(newIndex);
37679             if(pt == "after"){
37680                 newIndex++;
37681             }
37682             if(oldIndex < newIndex){
37683                 newIndex--;
37684             }
37685             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37686                 return false;
37687             }
37688             cm.setLocked(oldIndex, locked, true);
37689             cm.moveColumn(oldIndex, newIndex);
37690             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37691             return true;
37692         }
37693         return false;
37694     }
37695 });
37696 /*
37697  * Based on:
37698  * Ext JS Library 1.1.1
37699  * Copyright(c) 2006-2007, Ext JS, LLC.
37700  *
37701  * Originally Released Under LGPL - original licence link has changed is not relivant.
37702  *
37703  * Fork - LGPL
37704  * <script type="text/javascript">
37705  */
37706   
37707 /**
37708  * @class Roo.grid.GridView
37709  * @extends Roo.util.Observable
37710  *
37711  * @constructor
37712  * @param {Object} config
37713  */
37714 Roo.grid.GridView = function(config){
37715     Roo.grid.GridView.superclass.constructor.call(this);
37716     this.el = null;
37717
37718     Roo.apply(this, config);
37719 };
37720
37721 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37722
37723     unselectable :  'unselectable="on"',
37724     unselectableCls :  'x-unselectable',
37725     
37726     
37727     rowClass : "x-grid-row",
37728
37729     cellClass : "x-grid-col",
37730
37731     tdClass : "x-grid-td",
37732
37733     hdClass : "x-grid-hd",
37734
37735     splitClass : "x-grid-split",
37736
37737     sortClasses : ["sort-asc", "sort-desc"],
37738
37739     enableMoveAnim : false,
37740
37741     hlColor: "C3DAF9",
37742
37743     dh : Roo.DomHelper,
37744
37745     fly : Roo.Element.fly,
37746
37747     css : Roo.util.CSS,
37748
37749     borderWidth: 1,
37750
37751     splitOffset: 3,
37752
37753     scrollIncrement : 22,
37754
37755     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37756
37757     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37758
37759     bind : function(ds, cm){
37760         if(this.ds){
37761             this.ds.un("load", this.onLoad, this);
37762             this.ds.un("datachanged", this.onDataChange, this);
37763             this.ds.un("add", this.onAdd, this);
37764             this.ds.un("remove", this.onRemove, this);
37765             this.ds.un("update", this.onUpdate, this);
37766             this.ds.un("clear", this.onClear, this);
37767         }
37768         if(ds){
37769             ds.on("load", this.onLoad, this);
37770             ds.on("datachanged", this.onDataChange, this);
37771             ds.on("add", this.onAdd, this);
37772             ds.on("remove", this.onRemove, this);
37773             ds.on("update", this.onUpdate, this);
37774             ds.on("clear", this.onClear, this);
37775         }
37776         this.ds = ds;
37777
37778         if(this.cm){
37779             this.cm.un("widthchange", this.onColWidthChange, this);
37780             this.cm.un("headerchange", this.onHeaderChange, this);
37781             this.cm.un("hiddenchange", this.onHiddenChange, this);
37782             this.cm.un("columnmoved", this.onColumnMove, this);
37783             this.cm.un("columnlockchange", this.onColumnLock, this);
37784         }
37785         if(cm){
37786             this.generateRules(cm);
37787             cm.on("widthchange", this.onColWidthChange, this);
37788             cm.on("headerchange", this.onHeaderChange, this);
37789             cm.on("hiddenchange", this.onHiddenChange, this);
37790             cm.on("columnmoved", this.onColumnMove, this);
37791             cm.on("columnlockchange", this.onColumnLock, this);
37792         }
37793         this.cm = cm;
37794     },
37795
37796     init: function(grid){
37797         Roo.grid.GridView.superclass.init.call(this, grid);
37798
37799         this.bind(grid.dataSource, grid.colModel);
37800
37801         grid.on("headerclick", this.handleHeaderClick, this);
37802
37803         if(grid.trackMouseOver){
37804             grid.on("mouseover", this.onRowOver, this);
37805             grid.on("mouseout", this.onRowOut, this);
37806         }
37807         grid.cancelTextSelection = function(){};
37808         this.gridId = grid.id;
37809
37810         var tpls = this.templates || {};
37811
37812         if(!tpls.master){
37813             tpls.master = new Roo.Template(
37814                '<div class="x-grid" hidefocus="true">',
37815                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37816                   '<div class="x-grid-topbar"></div>',
37817                   '<div class="x-grid-scroller"><div></div></div>',
37818                   '<div class="x-grid-locked">',
37819                       '<div class="x-grid-header">{lockedHeader}</div>',
37820                       '<div class="x-grid-body">{lockedBody}</div>',
37821                   "</div>",
37822                   '<div class="x-grid-viewport">',
37823                       '<div class="x-grid-header">{header}</div>',
37824                       '<div class="x-grid-body">{body}</div>',
37825                   "</div>",
37826                   '<div class="x-grid-bottombar"></div>',
37827                  
37828                   '<div class="x-grid-resize-proxy">&#160;</div>',
37829                "</div>"
37830             );
37831             tpls.master.disableformats = true;
37832         }
37833
37834         if(!tpls.header){
37835             tpls.header = new Roo.Template(
37836                '<table border="0" cellspacing="0" cellpadding="0">',
37837                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37838                "</table>{splits}"
37839             );
37840             tpls.header.disableformats = true;
37841         }
37842         tpls.header.compile();
37843
37844         if(!tpls.hcell){
37845             tpls.hcell = new Roo.Template(
37846                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37847                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37848                 "</div></td>"
37849              );
37850              tpls.hcell.disableFormats = true;
37851         }
37852         tpls.hcell.compile();
37853
37854         if(!tpls.hsplit){
37855             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37856                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37857             tpls.hsplit.disableFormats = true;
37858         }
37859         tpls.hsplit.compile();
37860
37861         if(!tpls.body){
37862             tpls.body = new Roo.Template(
37863                '<table border="0" cellspacing="0" cellpadding="0">',
37864                "<tbody>{rows}</tbody>",
37865                "</table>"
37866             );
37867             tpls.body.disableFormats = true;
37868         }
37869         tpls.body.compile();
37870
37871         if(!tpls.row){
37872             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37873             tpls.row.disableFormats = true;
37874         }
37875         tpls.row.compile();
37876
37877         if(!tpls.cell){
37878             tpls.cell = new Roo.Template(
37879                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37880                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37881                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37882                 "</td>"
37883             );
37884             tpls.cell.disableFormats = true;
37885         }
37886         tpls.cell.compile();
37887
37888         this.templates = tpls;
37889     },
37890
37891     // remap these for backwards compat
37892     onColWidthChange : function(){
37893         this.updateColumns.apply(this, arguments);
37894     },
37895     onHeaderChange : function(){
37896         this.updateHeaders.apply(this, arguments);
37897     }, 
37898     onHiddenChange : function(){
37899         this.handleHiddenChange.apply(this, arguments);
37900     },
37901     onColumnMove : function(){
37902         this.handleColumnMove.apply(this, arguments);
37903     },
37904     onColumnLock : function(){
37905         this.handleLockChange.apply(this, arguments);
37906     },
37907
37908     onDataChange : function(){
37909         this.refresh();
37910         this.updateHeaderSortState();
37911     },
37912
37913     onClear : function(){
37914         this.refresh();
37915     },
37916
37917     onUpdate : function(ds, record){
37918         this.refreshRow(record);
37919     },
37920
37921     refreshRow : function(record){
37922         var ds = this.ds, index;
37923         if(typeof record == 'number'){
37924             index = record;
37925             record = ds.getAt(index);
37926         }else{
37927             index = ds.indexOf(record);
37928         }
37929         this.insertRows(ds, index, index, true);
37930         this.onRemove(ds, record, index+1, true);
37931         this.syncRowHeights(index, index);
37932         this.layout();
37933         this.fireEvent("rowupdated", this, index, record);
37934     },
37935
37936     onAdd : function(ds, records, index){
37937         this.insertRows(ds, index, index + (records.length-1));
37938     },
37939
37940     onRemove : function(ds, record, index, isUpdate){
37941         if(isUpdate !== true){
37942             this.fireEvent("beforerowremoved", this, index, record);
37943         }
37944         var bt = this.getBodyTable(), lt = this.getLockedTable();
37945         if(bt.rows[index]){
37946             bt.firstChild.removeChild(bt.rows[index]);
37947         }
37948         if(lt.rows[index]){
37949             lt.firstChild.removeChild(lt.rows[index]);
37950         }
37951         if(isUpdate !== true){
37952             this.stripeRows(index);
37953             this.syncRowHeights(index, index);
37954             this.layout();
37955             this.fireEvent("rowremoved", this, index, record);
37956         }
37957     },
37958
37959     onLoad : function(){
37960         this.scrollToTop();
37961     },
37962
37963     /**
37964      * Scrolls the grid to the top
37965      */
37966     scrollToTop : function(){
37967         if(this.scroller){
37968             this.scroller.dom.scrollTop = 0;
37969             this.syncScroll();
37970         }
37971     },
37972
37973     /**
37974      * Gets a panel in the header of the grid that can be used for toolbars etc.
37975      * After modifying the contents of this panel a call to grid.autoSize() may be
37976      * required to register any changes in size.
37977      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37978      * @return Roo.Element
37979      */
37980     getHeaderPanel : function(doShow){
37981         if(doShow){
37982             this.headerPanel.show();
37983         }
37984         return this.headerPanel;
37985     },
37986
37987     /**
37988      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37989      * After modifying the contents of this panel a call to grid.autoSize() may be
37990      * required to register any changes in size.
37991      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37992      * @return Roo.Element
37993      */
37994     getFooterPanel : function(doShow){
37995         if(doShow){
37996             this.footerPanel.show();
37997         }
37998         return this.footerPanel;
37999     },
38000
38001     initElements : function(){
38002         var E = Roo.Element;
38003         var el = this.grid.getGridEl().dom.firstChild;
38004         var cs = el.childNodes;
38005
38006         this.el = new E(el);
38007         
38008          this.focusEl = new E(el.firstChild);
38009         this.focusEl.swallowEvent("click", true);
38010         
38011         this.headerPanel = new E(cs[1]);
38012         this.headerPanel.enableDisplayMode("block");
38013
38014         this.scroller = new E(cs[2]);
38015         this.scrollSizer = new E(this.scroller.dom.firstChild);
38016
38017         this.lockedWrap = new E(cs[3]);
38018         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38019         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38020
38021         this.mainWrap = new E(cs[4]);
38022         this.mainHd = new E(this.mainWrap.dom.firstChild);
38023         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38024
38025         this.footerPanel = new E(cs[5]);
38026         this.footerPanel.enableDisplayMode("block");
38027
38028         this.resizeProxy = new E(cs[6]);
38029
38030         this.headerSelector = String.format(
38031            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38032            this.lockedHd.id, this.mainHd.id
38033         );
38034
38035         this.splitterSelector = String.format(
38036            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38037            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38038         );
38039     },
38040     idToCssName : function(s)
38041     {
38042         return s.replace(/[^a-z0-9]+/ig, '-');
38043     },
38044
38045     getHeaderCell : function(index){
38046         return Roo.DomQuery.select(this.headerSelector)[index];
38047     },
38048
38049     getHeaderCellMeasure : function(index){
38050         return this.getHeaderCell(index).firstChild;
38051     },
38052
38053     getHeaderCellText : function(index){
38054         return this.getHeaderCell(index).firstChild.firstChild;
38055     },
38056
38057     getLockedTable : function(){
38058         return this.lockedBody.dom.firstChild;
38059     },
38060
38061     getBodyTable : function(){
38062         return this.mainBody.dom.firstChild;
38063     },
38064
38065     getLockedRow : function(index){
38066         return this.getLockedTable().rows[index];
38067     },
38068
38069     getRow : function(index){
38070         return this.getBodyTable().rows[index];
38071     },
38072
38073     getRowComposite : function(index){
38074         if(!this.rowEl){
38075             this.rowEl = new Roo.CompositeElementLite();
38076         }
38077         var els = [], lrow, mrow;
38078         if(lrow = this.getLockedRow(index)){
38079             els.push(lrow);
38080         }
38081         if(mrow = this.getRow(index)){
38082             els.push(mrow);
38083         }
38084         this.rowEl.elements = els;
38085         return this.rowEl;
38086     },
38087     /**
38088      * Gets the 'td' of the cell
38089      * 
38090      * @param {Integer} rowIndex row to select
38091      * @param {Integer} colIndex column to select
38092      * 
38093      * @return {Object} 
38094      */
38095     getCell : function(rowIndex, colIndex){
38096         var locked = this.cm.getLockedCount();
38097         var source;
38098         if(colIndex < locked){
38099             source = this.lockedBody.dom.firstChild;
38100         }else{
38101             source = this.mainBody.dom.firstChild;
38102             colIndex -= locked;
38103         }
38104         return source.rows[rowIndex].childNodes[colIndex];
38105     },
38106
38107     getCellText : function(rowIndex, colIndex){
38108         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38109     },
38110
38111     getCellBox : function(cell){
38112         var b = this.fly(cell).getBox();
38113         if(Roo.isOpera){ // opera fails to report the Y
38114             b.y = cell.offsetTop + this.mainBody.getY();
38115         }
38116         return b;
38117     },
38118
38119     getCellIndex : function(cell){
38120         var id = String(cell.className).match(this.cellRE);
38121         if(id){
38122             return parseInt(id[1], 10);
38123         }
38124         return 0;
38125     },
38126
38127     findHeaderIndex : function(n){
38128         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38129         return r ? this.getCellIndex(r) : false;
38130     },
38131
38132     findHeaderCell : function(n){
38133         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38134         return r ? r : false;
38135     },
38136
38137     findRowIndex : function(n){
38138         if(!n){
38139             return false;
38140         }
38141         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38142         return r ? r.rowIndex : false;
38143     },
38144
38145     findCellIndex : function(node){
38146         var stop = this.el.dom;
38147         while(node && node != stop){
38148             if(this.findRE.test(node.className)){
38149                 return this.getCellIndex(node);
38150             }
38151             node = node.parentNode;
38152         }
38153         return false;
38154     },
38155
38156     getColumnId : function(index){
38157         return this.cm.getColumnId(index);
38158     },
38159
38160     getSplitters : function()
38161     {
38162         if(this.splitterSelector){
38163            return Roo.DomQuery.select(this.splitterSelector);
38164         }else{
38165             return null;
38166       }
38167     },
38168
38169     getSplitter : function(index){
38170         return this.getSplitters()[index];
38171     },
38172
38173     onRowOver : function(e, t){
38174         var row;
38175         if((row = this.findRowIndex(t)) !== false){
38176             this.getRowComposite(row).addClass("x-grid-row-over");
38177         }
38178     },
38179
38180     onRowOut : function(e, t){
38181         var row;
38182         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38183             this.getRowComposite(row).removeClass("x-grid-row-over");
38184         }
38185     },
38186
38187     renderHeaders : function(){
38188         var cm = this.cm;
38189         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38190         var cb = [], lb = [], sb = [], lsb = [], p = {};
38191         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38192             p.cellId = "x-grid-hd-0-" + i;
38193             p.splitId = "x-grid-csplit-0-" + i;
38194             p.id = cm.getColumnId(i);
38195             p.value = cm.getColumnHeader(i) || "";
38196             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38197             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38198             if(!cm.isLocked(i)){
38199                 cb[cb.length] = ct.apply(p);
38200                 sb[sb.length] = st.apply(p);
38201             }else{
38202                 lb[lb.length] = ct.apply(p);
38203                 lsb[lsb.length] = st.apply(p);
38204             }
38205         }
38206         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38207                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38208     },
38209
38210     updateHeaders : function(){
38211         var html = this.renderHeaders();
38212         this.lockedHd.update(html[0]);
38213         this.mainHd.update(html[1]);
38214     },
38215
38216     /**
38217      * Focuses the specified row.
38218      * @param {Number} row The row index
38219      */
38220     focusRow : function(row)
38221     {
38222         //Roo.log('GridView.focusRow');
38223         var x = this.scroller.dom.scrollLeft;
38224         this.focusCell(row, 0, false);
38225         this.scroller.dom.scrollLeft = x;
38226     },
38227
38228     /**
38229      * Focuses the specified cell.
38230      * @param {Number} row The row index
38231      * @param {Number} col The column index
38232      * @param {Boolean} hscroll false to disable horizontal scrolling
38233      */
38234     focusCell : function(row, col, hscroll)
38235     {
38236         //Roo.log('GridView.focusCell');
38237         var el = this.ensureVisible(row, col, hscroll);
38238         this.focusEl.alignTo(el, "tl-tl");
38239         if(Roo.isGecko){
38240             this.focusEl.focus();
38241         }else{
38242             this.focusEl.focus.defer(1, this.focusEl);
38243         }
38244     },
38245
38246     /**
38247      * Scrolls the specified cell into view
38248      * @param {Number} row The row index
38249      * @param {Number} col The column index
38250      * @param {Boolean} hscroll false to disable horizontal scrolling
38251      */
38252     ensureVisible : function(row, col, hscroll)
38253     {
38254         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38255         //return null; //disable for testing.
38256         if(typeof row != "number"){
38257             row = row.rowIndex;
38258         }
38259         if(row < 0 && row >= this.ds.getCount()){
38260             return  null;
38261         }
38262         col = (col !== undefined ? col : 0);
38263         var cm = this.grid.colModel;
38264         while(cm.isHidden(col)){
38265             col++;
38266         }
38267
38268         var el = this.getCell(row, col);
38269         if(!el){
38270             return null;
38271         }
38272         var c = this.scroller.dom;
38273
38274         var ctop = parseInt(el.offsetTop, 10);
38275         var cleft = parseInt(el.offsetLeft, 10);
38276         var cbot = ctop + el.offsetHeight;
38277         var cright = cleft + el.offsetWidth;
38278         
38279         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38280         var stop = parseInt(c.scrollTop, 10);
38281         var sleft = parseInt(c.scrollLeft, 10);
38282         var sbot = stop + ch;
38283         var sright = sleft + c.clientWidth;
38284         /*
38285         Roo.log('GridView.ensureVisible:' +
38286                 ' ctop:' + ctop +
38287                 ' c.clientHeight:' + c.clientHeight +
38288                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38289                 ' stop:' + stop +
38290                 ' cbot:' + cbot +
38291                 ' sbot:' + sbot +
38292                 ' ch:' + ch  
38293                 );
38294         */
38295         if(ctop < stop){
38296             c.scrollTop = ctop;
38297             //Roo.log("set scrolltop to ctop DISABLE?");
38298         }else if(cbot > sbot){
38299             //Roo.log("set scrolltop to cbot-ch");
38300             c.scrollTop = cbot-ch;
38301         }
38302         
38303         if(hscroll !== false){
38304             if(cleft < sleft){
38305                 c.scrollLeft = cleft;
38306             }else if(cright > sright){
38307                 c.scrollLeft = cright-c.clientWidth;
38308             }
38309         }
38310          
38311         return el;
38312     },
38313
38314     updateColumns : function(){
38315         this.grid.stopEditing();
38316         var cm = this.grid.colModel, colIds = this.getColumnIds();
38317         //var totalWidth = cm.getTotalWidth();
38318         var pos = 0;
38319         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38320             //if(cm.isHidden(i)) continue;
38321             var w = cm.getColumnWidth(i);
38322             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38323             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38324         }
38325         this.updateSplitters();
38326     },
38327
38328     generateRules : function(cm){
38329         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38330         Roo.util.CSS.removeStyleSheet(rulesId);
38331         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38332             var cid = cm.getColumnId(i);
38333             var align = '';
38334             if(cm.config[i].align){
38335                 align = 'text-align:'+cm.config[i].align+';';
38336             }
38337             var hidden = '';
38338             if(cm.isHidden(i)){
38339                 hidden = 'display:none;';
38340             }
38341             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38342             ruleBuf.push(
38343                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38344                     this.hdSelector, cid, " {\n", align, width, "}\n",
38345                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38346                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38347         }
38348         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38349     },
38350
38351     updateSplitters : function(){
38352         var cm = this.cm, s = this.getSplitters();
38353         if(s){ // splitters not created yet
38354             var pos = 0, locked = true;
38355             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38356                 if(cm.isHidden(i)) {
38357                     continue;
38358                 }
38359                 var w = cm.getColumnWidth(i); // make sure it's a number
38360                 if(!cm.isLocked(i) && locked){
38361                     pos = 0;
38362                     locked = false;
38363                 }
38364                 pos += w;
38365                 s[i].style.left = (pos-this.splitOffset) + "px";
38366             }
38367         }
38368     },
38369
38370     handleHiddenChange : function(colModel, colIndex, hidden){
38371         if(hidden){
38372             this.hideColumn(colIndex);
38373         }else{
38374             this.unhideColumn(colIndex);
38375         }
38376     },
38377
38378     hideColumn : function(colIndex){
38379         var cid = this.getColumnId(colIndex);
38380         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38381         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38382         if(Roo.isSafari){
38383             this.updateHeaders();
38384         }
38385         this.updateSplitters();
38386         this.layout();
38387     },
38388
38389     unhideColumn : function(colIndex){
38390         var cid = this.getColumnId(colIndex);
38391         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38392         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38393
38394         if(Roo.isSafari){
38395             this.updateHeaders();
38396         }
38397         this.updateSplitters();
38398         this.layout();
38399     },
38400
38401     insertRows : function(dm, firstRow, lastRow, isUpdate){
38402         if(firstRow == 0 && lastRow == dm.getCount()-1){
38403             this.refresh();
38404         }else{
38405             if(!isUpdate){
38406                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38407             }
38408             var s = this.getScrollState();
38409             var markup = this.renderRows(firstRow, lastRow);
38410             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38411             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38412             this.restoreScroll(s);
38413             if(!isUpdate){
38414                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38415                 this.syncRowHeights(firstRow, lastRow);
38416                 this.stripeRows(firstRow);
38417                 this.layout();
38418             }
38419         }
38420     },
38421
38422     bufferRows : function(markup, target, index){
38423         var before = null, trows = target.rows, tbody = target.tBodies[0];
38424         if(index < trows.length){
38425             before = trows[index];
38426         }
38427         var b = document.createElement("div");
38428         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38429         var rows = b.firstChild.rows;
38430         for(var i = 0, len = rows.length; i < len; i++){
38431             if(before){
38432                 tbody.insertBefore(rows[0], before);
38433             }else{
38434                 tbody.appendChild(rows[0]);
38435             }
38436         }
38437         b.innerHTML = "";
38438         b = null;
38439     },
38440
38441     deleteRows : function(dm, firstRow, lastRow){
38442         if(dm.getRowCount()<1){
38443             this.fireEvent("beforerefresh", this);
38444             this.mainBody.update("");
38445             this.lockedBody.update("");
38446             this.fireEvent("refresh", this);
38447         }else{
38448             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38449             var bt = this.getBodyTable();
38450             var tbody = bt.firstChild;
38451             var rows = bt.rows;
38452             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38453                 tbody.removeChild(rows[firstRow]);
38454             }
38455             this.stripeRows(firstRow);
38456             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38457         }
38458     },
38459
38460     updateRows : function(dataSource, firstRow, lastRow){
38461         var s = this.getScrollState();
38462         this.refresh();
38463         this.restoreScroll(s);
38464     },
38465
38466     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38467         if(!noRefresh){
38468            this.refresh();
38469         }
38470         this.updateHeaderSortState();
38471     },
38472
38473     getScrollState : function(){
38474         
38475         var sb = this.scroller.dom;
38476         return {left: sb.scrollLeft, top: sb.scrollTop};
38477     },
38478
38479     stripeRows : function(startRow){
38480         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38481             return;
38482         }
38483         startRow = startRow || 0;
38484         var rows = this.getBodyTable().rows;
38485         var lrows = this.getLockedTable().rows;
38486         var cls = ' x-grid-row-alt ';
38487         for(var i = startRow, len = rows.length; i < len; i++){
38488             var row = rows[i], lrow = lrows[i];
38489             var isAlt = ((i+1) % 2 == 0);
38490             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38491             if(isAlt == hasAlt){
38492                 continue;
38493             }
38494             if(isAlt){
38495                 row.className += " x-grid-row-alt";
38496             }else{
38497                 row.className = row.className.replace("x-grid-row-alt", "");
38498             }
38499             if(lrow){
38500                 lrow.className = row.className;
38501             }
38502         }
38503     },
38504
38505     restoreScroll : function(state){
38506         //Roo.log('GridView.restoreScroll');
38507         var sb = this.scroller.dom;
38508         sb.scrollLeft = state.left;
38509         sb.scrollTop = state.top;
38510         this.syncScroll();
38511     },
38512
38513     syncScroll : function(){
38514         //Roo.log('GridView.syncScroll');
38515         var sb = this.scroller.dom;
38516         var sh = this.mainHd.dom;
38517         var bs = this.mainBody.dom;
38518         var lv = this.lockedBody.dom;
38519         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38520         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38521     },
38522
38523     handleScroll : function(e){
38524         this.syncScroll();
38525         var sb = this.scroller.dom;
38526         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38527         e.stopEvent();
38528     },
38529
38530     handleWheel : function(e){
38531         var d = e.getWheelDelta();
38532         this.scroller.dom.scrollTop -= d*22;
38533         // set this here to prevent jumpy scrolling on large tables
38534         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38535         e.stopEvent();
38536     },
38537
38538     renderRows : function(startRow, endRow){
38539         // pull in all the crap needed to render rows
38540         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38541         var colCount = cm.getColumnCount();
38542
38543         if(ds.getCount() < 1){
38544             return ["", ""];
38545         }
38546
38547         // build a map for all the columns
38548         var cs = [];
38549         for(var i = 0; i < colCount; i++){
38550             var name = cm.getDataIndex(i);
38551             cs[i] = {
38552                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38553                 renderer : cm.getRenderer(i),
38554                 id : cm.getColumnId(i),
38555                 locked : cm.isLocked(i),
38556                 has_editor : cm.isCellEditable(i)
38557             };
38558         }
38559
38560         startRow = startRow || 0;
38561         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38562
38563         // records to render
38564         var rs = ds.getRange(startRow, endRow);
38565
38566         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38567     },
38568
38569     // As much as I hate to duplicate code, this was branched because FireFox really hates
38570     // [].join("") on strings. The performance difference was substantial enough to
38571     // branch this function
38572     doRender : Roo.isGecko ?
38573             function(cs, rs, ds, startRow, colCount, stripe){
38574                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38575                 // buffers
38576                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38577                 
38578                 var hasListener = this.grid.hasListener('rowclass');
38579                 var rowcfg = {};
38580                 for(var j = 0, len = rs.length; j < len; j++){
38581                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38582                     for(var i = 0; i < colCount; i++){
38583                         c = cs[i];
38584                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38585                         p.id = c.id;
38586                         p.css = p.attr = "";
38587                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38588                         if(p.value == undefined || p.value === "") {
38589                             p.value = "&#160;";
38590                         }
38591                         if(c.has_editor){
38592                             p.css += ' x-grid-editable-cell';
38593                         }
38594                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38595                             p.css +=  ' x-grid-dirty-cell';
38596                         }
38597                         var markup = ct.apply(p);
38598                         if(!c.locked){
38599                             cb+= markup;
38600                         }else{
38601                             lcb+= markup;
38602                         }
38603                     }
38604                     var alt = [];
38605                     if(stripe && ((rowIndex+1) % 2 == 0)){
38606                         alt.push("x-grid-row-alt")
38607                     }
38608                     if(r.dirty){
38609                         alt.push(  " x-grid-dirty-row");
38610                     }
38611                     rp.cells = lcb;
38612                     if(this.getRowClass){
38613                         alt.push(this.getRowClass(r, rowIndex));
38614                     }
38615                     if (hasListener) {
38616                         rowcfg = {
38617                              
38618                             record: r,
38619                             rowIndex : rowIndex,
38620                             rowClass : ''
38621                         };
38622                         this.grid.fireEvent('rowclass', this, rowcfg);
38623                         alt.push(rowcfg.rowClass);
38624                     }
38625                     rp.alt = alt.join(" ");
38626                     lbuf+= rt.apply(rp);
38627                     rp.cells = cb;
38628                     buf+=  rt.apply(rp);
38629                 }
38630                 return [lbuf, buf];
38631             } :
38632             function(cs, rs, ds, startRow, colCount, stripe){
38633                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38634                 // buffers
38635                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38636                 var hasListener = this.grid.hasListener('rowclass');
38637  
38638                 var rowcfg = {};
38639                 for(var j = 0, len = rs.length; j < len; j++){
38640                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38641                     for(var i = 0; i < colCount; i++){
38642                         c = cs[i];
38643                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38644                         p.id = c.id;
38645                         p.css = p.attr = "";
38646                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38647                         if(p.value == undefined || p.value === "") {
38648                             p.value = "&#160;";
38649                         }
38650                         //Roo.log(c);
38651                          if(c.has_editor){
38652                             p.css += ' x-grid-editable-cell';
38653                         }
38654                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38655                             p.css += ' x-grid-dirty-cell' 
38656                         }
38657                         
38658                         var markup = ct.apply(p);
38659                         if(!c.locked){
38660                             cb[cb.length] = markup;
38661                         }else{
38662                             lcb[lcb.length] = markup;
38663                         }
38664                     }
38665                     var alt = [];
38666                     if(stripe && ((rowIndex+1) % 2 == 0)){
38667                         alt.push( "x-grid-row-alt");
38668                     }
38669                     if(r.dirty){
38670                         alt.push(" x-grid-dirty-row");
38671                     }
38672                     rp.cells = lcb;
38673                     if(this.getRowClass){
38674                         alt.push( this.getRowClass(r, rowIndex));
38675                     }
38676                     if (hasListener) {
38677                         rowcfg = {
38678                              
38679                             record: r,
38680                             rowIndex : rowIndex,
38681                             rowClass : ''
38682                         };
38683                         this.grid.fireEvent('rowclass', this, rowcfg);
38684                         alt.push(rowcfg.rowClass);
38685                     }
38686                     
38687                     rp.alt = alt.join(" ");
38688                     rp.cells = lcb.join("");
38689                     lbuf[lbuf.length] = rt.apply(rp);
38690                     rp.cells = cb.join("");
38691                     buf[buf.length] =  rt.apply(rp);
38692                 }
38693                 return [lbuf.join(""), buf.join("")];
38694             },
38695
38696     renderBody : function(){
38697         var markup = this.renderRows();
38698         var bt = this.templates.body;
38699         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38700     },
38701
38702     /**
38703      * Refreshes the grid
38704      * @param {Boolean} headersToo
38705      */
38706     refresh : function(headersToo){
38707         this.fireEvent("beforerefresh", this);
38708         this.grid.stopEditing();
38709         var result = this.renderBody();
38710         this.lockedBody.update(result[0]);
38711         this.mainBody.update(result[1]);
38712         if(headersToo === true){
38713             this.updateHeaders();
38714             this.updateColumns();
38715             this.updateSplitters();
38716             this.updateHeaderSortState();
38717         }
38718         this.syncRowHeights();
38719         this.layout();
38720         this.fireEvent("refresh", this);
38721     },
38722
38723     handleColumnMove : function(cm, oldIndex, newIndex){
38724         this.indexMap = null;
38725         var s = this.getScrollState();
38726         this.refresh(true);
38727         this.restoreScroll(s);
38728         this.afterMove(newIndex);
38729     },
38730
38731     afterMove : function(colIndex){
38732         if(this.enableMoveAnim && Roo.enableFx){
38733             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38734         }
38735         // if multisort - fix sortOrder, and reload..
38736         if (this.grid.dataSource.multiSort) {
38737             // the we can call sort again..
38738             var dm = this.grid.dataSource;
38739             var cm = this.grid.colModel;
38740             var so = [];
38741             for(var i = 0; i < cm.config.length; i++ ) {
38742                 
38743                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38744                     continue; // dont' bother, it's not in sort list or being set.
38745                 }
38746                 
38747                 so.push(cm.config[i].dataIndex);
38748             };
38749             dm.sortOrder = so;
38750             dm.load(dm.lastOptions);
38751             
38752             
38753         }
38754         
38755     },
38756
38757     updateCell : function(dm, rowIndex, dataIndex){
38758         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38759         if(typeof colIndex == "undefined"){ // not present in grid
38760             return;
38761         }
38762         var cm = this.grid.colModel;
38763         var cell = this.getCell(rowIndex, colIndex);
38764         var cellText = this.getCellText(rowIndex, colIndex);
38765
38766         var p = {
38767             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38768             id : cm.getColumnId(colIndex),
38769             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38770         };
38771         var renderer = cm.getRenderer(colIndex);
38772         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38773         if(typeof val == "undefined" || val === "") {
38774             val = "&#160;";
38775         }
38776         cellText.innerHTML = val;
38777         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38778         this.syncRowHeights(rowIndex, rowIndex);
38779     },
38780
38781     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38782         var maxWidth = 0;
38783         if(this.grid.autoSizeHeaders){
38784             var h = this.getHeaderCellMeasure(colIndex);
38785             maxWidth = Math.max(maxWidth, h.scrollWidth);
38786         }
38787         var tb, index;
38788         if(this.cm.isLocked(colIndex)){
38789             tb = this.getLockedTable();
38790             index = colIndex;
38791         }else{
38792             tb = this.getBodyTable();
38793             index = colIndex - this.cm.getLockedCount();
38794         }
38795         if(tb && tb.rows){
38796             var rows = tb.rows;
38797             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38798             for(var i = 0; i < stopIndex; i++){
38799                 var cell = rows[i].childNodes[index].firstChild;
38800                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38801             }
38802         }
38803         return maxWidth + /*margin for error in IE*/ 5;
38804     },
38805     /**
38806      * Autofit a column to its content.
38807      * @param {Number} colIndex
38808      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38809      */
38810      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38811          if(this.cm.isHidden(colIndex)){
38812              return; // can't calc a hidden column
38813          }
38814         if(forceMinSize){
38815             var cid = this.cm.getColumnId(colIndex);
38816             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38817            if(this.grid.autoSizeHeaders){
38818                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38819            }
38820         }
38821         var newWidth = this.calcColumnWidth(colIndex);
38822         this.cm.setColumnWidth(colIndex,
38823             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38824         if(!suppressEvent){
38825             this.grid.fireEvent("columnresize", colIndex, newWidth);
38826         }
38827     },
38828
38829     /**
38830      * Autofits all columns to their content and then expands to fit any extra space in the grid
38831      */
38832      autoSizeColumns : function(){
38833         var cm = this.grid.colModel;
38834         var colCount = cm.getColumnCount();
38835         for(var i = 0; i < colCount; i++){
38836             this.autoSizeColumn(i, true, true);
38837         }
38838         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38839             this.fitColumns();
38840         }else{
38841             this.updateColumns();
38842             this.layout();
38843         }
38844     },
38845
38846     /**
38847      * Autofits all columns to the grid's width proportionate with their current size
38848      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38849      */
38850     fitColumns : function(reserveScrollSpace){
38851         var cm = this.grid.colModel;
38852         var colCount = cm.getColumnCount();
38853         var cols = [];
38854         var width = 0;
38855         var i, w;
38856         for (i = 0; i < colCount; i++){
38857             if(!cm.isHidden(i) && !cm.isFixed(i)){
38858                 w = cm.getColumnWidth(i);
38859                 cols.push(i);
38860                 cols.push(w);
38861                 width += w;
38862             }
38863         }
38864         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38865         if(reserveScrollSpace){
38866             avail -= 17;
38867         }
38868         var frac = (avail - cm.getTotalWidth())/width;
38869         while (cols.length){
38870             w = cols.pop();
38871             i = cols.pop();
38872             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38873         }
38874         this.updateColumns();
38875         this.layout();
38876     },
38877
38878     onRowSelect : function(rowIndex){
38879         var row = this.getRowComposite(rowIndex);
38880         row.addClass("x-grid-row-selected");
38881     },
38882
38883     onRowDeselect : function(rowIndex){
38884         var row = this.getRowComposite(rowIndex);
38885         row.removeClass("x-grid-row-selected");
38886     },
38887
38888     onCellSelect : function(row, col){
38889         var cell = this.getCell(row, col);
38890         if(cell){
38891             Roo.fly(cell).addClass("x-grid-cell-selected");
38892         }
38893     },
38894
38895     onCellDeselect : function(row, col){
38896         var cell = this.getCell(row, col);
38897         if(cell){
38898             Roo.fly(cell).removeClass("x-grid-cell-selected");
38899         }
38900     },
38901
38902     updateHeaderSortState : function(){
38903         
38904         // sort state can be single { field: xxx, direction : yyy}
38905         // or   { xxx=>ASC , yyy : DESC ..... }
38906         
38907         var mstate = {};
38908         if (!this.ds.multiSort) { 
38909             var state = this.ds.getSortState();
38910             if(!state){
38911                 return;
38912             }
38913             mstate[state.field] = state.direction;
38914             // FIXME... - this is not used here.. but might be elsewhere..
38915             this.sortState = state;
38916             
38917         } else {
38918             mstate = this.ds.sortToggle;
38919         }
38920         //remove existing sort classes..
38921         
38922         var sc = this.sortClasses;
38923         var hds = this.el.select(this.headerSelector).removeClass(sc);
38924         
38925         for(var f in mstate) {
38926         
38927             var sortColumn = this.cm.findColumnIndex(f);
38928             
38929             if(sortColumn != -1){
38930                 var sortDir = mstate[f];        
38931                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38932             }
38933         }
38934         
38935          
38936         
38937     },
38938
38939
38940     handleHeaderClick : function(g, index,e){
38941         
38942         Roo.log("header click");
38943         
38944         if (Roo.isTouch) {
38945             // touch events on header are handled by context
38946             this.handleHdCtx(g,index,e);
38947             return;
38948         }
38949         
38950         
38951         if(this.headersDisabled){
38952             return;
38953         }
38954         var dm = g.dataSource, cm = g.colModel;
38955         if(!cm.isSortable(index)){
38956             return;
38957         }
38958         g.stopEditing();
38959         
38960         if (dm.multiSort) {
38961             // update the sortOrder
38962             var so = [];
38963             for(var i = 0; i < cm.config.length; i++ ) {
38964                 
38965                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38966                     continue; // dont' bother, it's not in sort list or being set.
38967                 }
38968                 
38969                 so.push(cm.config[i].dataIndex);
38970             };
38971             dm.sortOrder = so;
38972         }
38973         
38974         
38975         dm.sort(cm.getDataIndex(index));
38976     },
38977
38978
38979     destroy : function(){
38980         if(this.colMenu){
38981             this.colMenu.removeAll();
38982             Roo.menu.MenuMgr.unregister(this.colMenu);
38983             this.colMenu.getEl().remove();
38984             delete this.colMenu;
38985         }
38986         if(this.hmenu){
38987             this.hmenu.removeAll();
38988             Roo.menu.MenuMgr.unregister(this.hmenu);
38989             this.hmenu.getEl().remove();
38990             delete this.hmenu;
38991         }
38992         if(this.grid.enableColumnMove){
38993             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38994             if(dds){
38995                 for(var dd in dds){
38996                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38997                         var elid = dds[dd].dragElId;
38998                         dds[dd].unreg();
38999                         Roo.get(elid).remove();
39000                     } else if(dds[dd].config.isTarget){
39001                         dds[dd].proxyTop.remove();
39002                         dds[dd].proxyBottom.remove();
39003                         dds[dd].unreg();
39004                     }
39005                     if(Roo.dd.DDM.locationCache[dd]){
39006                         delete Roo.dd.DDM.locationCache[dd];
39007                     }
39008                 }
39009                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39010             }
39011         }
39012         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39013         this.bind(null, null);
39014         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39015     },
39016
39017     handleLockChange : function(){
39018         this.refresh(true);
39019     },
39020
39021     onDenyColumnLock : function(){
39022
39023     },
39024
39025     onDenyColumnHide : function(){
39026
39027     },
39028
39029     handleHdMenuClick : function(item){
39030         var index = this.hdCtxIndex;
39031         var cm = this.cm, ds = this.ds;
39032         switch(item.id){
39033             case "asc":
39034                 ds.sort(cm.getDataIndex(index), "ASC");
39035                 break;
39036             case "desc":
39037                 ds.sort(cm.getDataIndex(index), "DESC");
39038                 break;
39039             case "lock":
39040                 var lc = cm.getLockedCount();
39041                 if(cm.getColumnCount(true) <= lc+1){
39042                     this.onDenyColumnLock();
39043                     return;
39044                 }
39045                 if(lc != index){
39046                     cm.setLocked(index, true, true);
39047                     cm.moveColumn(index, lc);
39048                     this.grid.fireEvent("columnmove", index, lc);
39049                 }else{
39050                     cm.setLocked(index, true);
39051                 }
39052             break;
39053             case "unlock":
39054                 var lc = cm.getLockedCount();
39055                 if((lc-1) != index){
39056                     cm.setLocked(index, false, true);
39057                     cm.moveColumn(index, lc-1);
39058                     this.grid.fireEvent("columnmove", index, lc-1);
39059                 }else{
39060                     cm.setLocked(index, false);
39061                 }
39062             break;
39063             case 'wider': // used to expand cols on touch..
39064             case 'narrow':
39065                 var cw = cm.getColumnWidth(index);
39066                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39067                 cw = Math.max(0, cw);
39068                 cw = Math.min(cw,4000);
39069                 cm.setColumnWidth(index, cw);
39070                 break;
39071                 
39072             default:
39073                 index = cm.getIndexById(item.id.substr(4));
39074                 if(index != -1){
39075                     if(item.checked && cm.getColumnCount(true) <= 1){
39076                         this.onDenyColumnHide();
39077                         return false;
39078                     }
39079                     cm.setHidden(index, item.checked);
39080                 }
39081         }
39082         return true;
39083     },
39084
39085     beforeColMenuShow : function(){
39086         var cm = this.cm,  colCount = cm.getColumnCount();
39087         this.colMenu.removeAll();
39088         
39089         var items = [];
39090         for(var i = 0; i < colCount; i++){
39091             items.push({
39092                 id: "col-"+cm.getColumnId(i),
39093                 text: cm.getColumnHeader(i),
39094                 checked: !cm.isHidden(i),
39095                 hideOnClick:false
39096             });
39097         }
39098         
39099         if (this.grid.sortColMenu) {
39100             items.sort(function(a,b) {
39101                 if (a.text == b.text) {
39102                     return 0;
39103                 }
39104                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39105             });
39106         }
39107         
39108         for(var i = 0; i < colCount; i++){
39109             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39110         }
39111     },
39112
39113     handleHdCtx : function(g, index, e){
39114         e.stopEvent();
39115         var hd = this.getHeaderCell(index);
39116         this.hdCtxIndex = index;
39117         var ms = this.hmenu.items, cm = this.cm;
39118         ms.get("asc").setDisabled(!cm.isSortable(index));
39119         ms.get("desc").setDisabled(!cm.isSortable(index));
39120         if(this.grid.enableColLock !== false){
39121             ms.get("lock").setDisabled(cm.isLocked(index));
39122             ms.get("unlock").setDisabled(!cm.isLocked(index));
39123         }
39124         this.hmenu.show(hd, "tl-bl");
39125     },
39126
39127     handleHdOver : function(e){
39128         var hd = this.findHeaderCell(e.getTarget());
39129         if(hd && !this.headersDisabled){
39130             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39131                this.fly(hd).addClass("x-grid-hd-over");
39132             }
39133         }
39134     },
39135
39136     handleHdOut : function(e){
39137         var hd = this.findHeaderCell(e.getTarget());
39138         if(hd){
39139             this.fly(hd).removeClass("x-grid-hd-over");
39140         }
39141     },
39142
39143     handleSplitDblClick : function(e, t){
39144         var i = this.getCellIndex(t);
39145         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39146             this.autoSizeColumn(i, true);
39147             this.layout();
39148         }
39149     },
39150
39151     render : function(){
39152
39153         var cm = this.cm;
39154         var colCount = cm.getColumnCount();
39155
39156         if(this.grid.monitorWindowResize === true){
39157             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39158         }
39159         var header = this.renderHeaders();
39160         var body = this.templates.body.apply({rows:""});
39161         var html = this.templates.master.apply({
39162             lockedBody: body,
39163             body: body,
39164             lockedHeader: header[0],
39165             header: header[1]
39166         });
39167
39168         //this.updateColumns();
39169
39170         this.grid.getGridEl().dom.innerHTML = html;
39171
39172         this.initElements();
39173         
39174         // a kludge to fix the random scolling effect in webkit
39175         this.el.on("scroll", function() {
39176             this.el.dom.scrollTop=0; // hopefully not recursive..
39177         },this);
39178
39179         this.scroller.on("scroll", this.handleScroll, this);
39180         this.lockedBody.on("mousewheel", this.handleWheel, this);
39181         this.mainBody.on("mousewheel", this.handleWheel, this);
39182
39183         this.mainHd.on("mouseover", this.handleHdOver, this);
39184         this.mainHd.on("mouseout", this.handleHdOut, this);
39185         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39186                 {delegate: "."+this.splitClass});
39187
39188         this.lockedHd.on("mouseover", this.handleHdOver, this);
39189         this.lockedHd.on("mouseout", this.handleHdOut, this);
39190         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39191                 {delegate: "."+this.splitClass});
39192
39193         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39194             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39195         }
39196
39197         this.updateSplitters();
39198
39199         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39200             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39201             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39202         }
39203
39204         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39205             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39206             this.hmenu.add(
39207                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39208                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39209             );
39210             if(this.grid.enableColLock !== false){
39211                 this.hmenu.add('-',
39212                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39213                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39214                 );
39215             }
39216             if (Roo.isTouch) {
39217                  this.hmenu.add('-',
39218                     {id:"wider", text: this.columnsWiderText},
39219                     {id:"narrow", text: this.columnsNarrowText }
39220                 );
39221                 
39222                  
39223             }
39224             
39225             if(this.grid.enableColumnHide !== false){
39226
39227                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39228                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39229                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39230
39231                 this.hmenu.add('-',
39232                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39233                 );
39234             }
39235             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39236
39237             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39238         }
39239
39240         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39241             this.dd = new Roo.grid.GridDragZone(this.grid, {
39242                 ddGroup : this.grid.ddGroup || 'GridDD'
39243             });
39244             
39245         }
39246
39247         /*
39248         for(var i = 0; i < colCount; i++){
39249             if(cm.isHidden(i)){
39250                 this.hideColumn(i);
39251             }
39252             if(cm.config[i].align){
39253                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39254                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39255             }
39256         }*/
39257         
39258         this.updateHeaderSortState();
39259
39260         this.beforeInitialResize();
39261         this.layout(true);
39262
39263         // two part rendering gives faster view to the user
39264         this.renderPhase2.defer(1, this);
39265     },
39266
39267     renderPhase2 : function(){
39268         // render the rows now
39269         this.refresh();
39270         if(this.grid.autoSizeColumns){
39271             this.autoSizeColumns();
39272         }
39273     },
39274
39275     beforeInitialResize : function(){
39276
39277     },
39278
39279     onColumnSplitterMoved : function(i, w){
39280         this.userResized = true;
39281         var cm = this.grid.colModel;
39282         cm.setColumnWidth(i, w, true);
39283         var cid = cm.getColumnId(i);
39284         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39285         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39286         this.updateSplitters();
39287         this.layout();
39288         this.grid.fireEvent("columnresize", i, w);
39289     },
39290
39291     syncRowHeights : function(startIndex, endIndex){
39292         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39293             startIndex = startIndex || 0;
39294             var mrows = this.getBodyTable().rows;
39295             var lrows = this.getLockedTable().rows;
39296             var len = mrows.length-1;
39297             endIndex = Math.min(endIndex || len, len);
39298             for(var i = startIndex; i <= endIndex; i++){
39299                 var m = mrows[i], l = lrows[i];
39300                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39301                 m.style.height = l.style.height = h + "px";
39302             }
39303         }
39304     },
39305
39306     layout : function(initialRender, is2ndPass)
39307     {
39308         var g = this.grid;
39309         var auto = g.autoHeight;
39310         var scrollOffset = 16;
39311         var c = g.getGridEl(), cm = this.cm,
39312                 expandCol = g.autoExpandColumn,
39313                 gv = this;
39314         //c.beginMeasure();
39315
39316         if(!c.dom.offsetWidth){ // display:none?
39317             if(initialRender){
39318                 this.lockedWrap.show();
39319                 this.mainWrap.show();
39320             }
39321             return;
39322         }
39323
39324         var hasLock = this.cm.isLocked(0);
39325
39326         var tbh = this.headerPanel.getHeight();
39327         var bbh = this.footerPanel.getHeight();
39328
39329         if(auto){
39330             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39331             var newHeight = ch + c.getBorderWidth("tb");
39332             if(g.maxHeight){
39333                 newHeight = Math.min(g.maxHeight, newHeight);
39334             }
39335             c.setHeight(newHeight);
39336         }
39337
39338         if(g.autoWidth){
39339             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39340         }
39341
39342         var s = this.scroller;
39343
39344         var csize = c.getSize(true);
39345
39346         this.el.setSize(csize.width, csize.height);
39347
39348         this.headerPanel.setWidth(csize.width);
39349         this.footerPanel.setWidth(csize.width);
39350
39351         var hdHeight = this.mainHd.getHeight();
39352         var vw = csize.width;
39353         var vh = csize.height - (tbh + bbh);
39354
39355         s.setSize(vw, vh);
39356
39357         var bt = this.getBodyTable();
39358         
39359         if(cm.getLockedCount() == cm.config.length){
39360             bt = this.getLockedTable();
39361         }
39362         
39363         var ltWidth = hasLock ?
39364                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39365
39366         var scrollHeight = bt.offsetHeight;
39367         var scrollWidth = ltWidth + bt.offsetWidth;
39368         var vscroll = false, hscroll = false;
39369
39370         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39371
39372         var lw = this.lockedWrap, mw = this.mainWrap;
39373         var lb = this.lockedBody, mb = this.mainBody;
39374
39375         setTimeout(function(){
39376             var t = s.dom.offsetTop;
39377             var w = s.dom.clientWidth,
39378                 h = s.dom.clientHeight;
39379
39380             lw.setTop(t);
39381             lw.setSize(ltWidth, h);
39382
39383             mw.setLeftTop(ltWidth, t);
39384             mw.setSize(w-ltWidth, h);
39385
39386             lb.setHeight(h-hdHeight);
39387             mb.setHeight(h-hdHeight);
39388
39389             if(is2ndPass !== true && !gv.userResized && expandCol){
39390                 // high speed resize without full column calculation
39391                 
39392                 var ci = cm.getIndexById(expandCol);
39393                 if (ci < 0) {
39394                     ci = cm.findColumnIndex(expandCol);
39395                 }
39396                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39397                 var expandId = cm.getColumnId(ci);
39398                 var  tw = cm.getTotalWidth(false);
39399                 var currentWidth = cm.getColumnWidth(ci);
39400                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39401                 if(currentWidth != cw){
39402                     cm.setColumnWidth(ci, cw, true);
39403                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39404                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39405                     gv.updateSplitters();
39406                     gv.layout(false, true);
39407                 }
39408             }
39409
39410             if(initialRender){
39411                 lw.show();
39412                 mw.show();
39413             }
39414             //c.endMeasure();
39415         }, 10);
39416     },
39417
39418     onWindowResize : function(){
39419         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39420             return;
39421         }
39422         this.layout();
39423     },
39424
39425     appendFooter : function(parentEl){
39426         return null;
39427     },
39428
39429     sortAscText : "Sort Ascending",
39430     sortDescText : "Sort Descending",
39431     lockText : "Lock Column",
39432     unlockText : "Unlock Column",
39433     columnsText : "Columns",
39434  
39435     columnsWiderText : "Wider",
39436     columnsNarrowText : "Thinner"
39437 });
39438
39439
39440 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39441     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39442     this.proxy.el.addClass('x-grid3-col-dd');
39443 };
39444
39445 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39446     handleMouseDown : function(e){
39447
39448     },
39449
39450     callHandleMouseDown : function(e){
39451         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39452     }
39453 });
39454 /*
39455  * Based on:
39456  * Ext JS Library 1.1.1
39457  * Copyright(c) 2006-2007, Ext JS, LLC.
39458  *
39459  * Originally Released Under LGPL - original licence link has changed is not relivant.
39460  *
39461  * Fork - LGPL
39462  * <script type="text/javascript">
39463  */
39464  /**
39465  * @extends Roo.dd.DDProxy
39466  * @class Roo.grid.SplitDragZone
39467  * Support for Column Header resizing
39468  * @constructor
39469  * @param {Object} config
39470  */
39471 // private
39472 // This is a support class used internally by the Grid components
39473 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39474     this.grid = grid;
39475     this.view = grid.getView();
39476     this.proxy = this.view.resizeProxy;
39477     Roo.grid.SplitDragZone.superclass.constructor.call(
39478         this,
39479         hd, // ID
39480         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39481         {  // CONFIG
39482             dragElId : Roo.id(this.proxy.dom),
39483             resizeFrame:false
39484         }
39485     );
39486     
39487     this.setHandleElId(Roo.id(hd));
39488     if (hd2 !== false) {
39489         this.setOuterHandleElId(Roo.id(hd2));
39490     }
39491     
39492     this.scroll = false;
39493 };
39494 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39495     fly: Roo.Element.fly,
39496
39497     b4StartDrag : function(x, y){
39498         this.view.headersDisabled = true;
39499         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39500                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39501         );
39502         this.proxy.setHeight(h);
39503         
39504         // for old system colWidth really stored the actual width?
39505         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39506         // which in reality did not work.. - it worked only for fixed sizes
39507         // for resizable we need to use actual sizes.
39508         var w = this.cm.getColumnWidth(this.cellIndex);
39509         if (!this.view.mainWrap) {
39510             // bootstrap.
39511             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39512         }
39513         
39514         
39515         
39516         // this was w-this.grid.minColumnWidth;
39517         // doesnt really make sense? - w = thie curren width or the rendered one?
39518         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39519         this.resetConstraints();
39520         this.setXConstraint(minw, 1000);
39521         this.setYConstraint(0, 0);
39522         this.minX = x - minw;
39523         this.maxX = x + 1000;
39524         this.startPos = x;
39525         if (!this.view.mainWrap) { // this is Bootstrap code..
39526             this.getDragEl().style.display='block';
39527         }
39528         
39529         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39530     },
39531
39532
39533     handleMouseDown : function(e){
39534         ev = Roo.EventObject.setEvent(e);
39535         var t = this.fly(ev.getTarget());
39536         if(t.hasClass("x-grid-split")){
39537             this.cellIndex = this.view.getCellIndex(t.dom);
39538             this.split = t.dom;
39539             this.cm = this.grid.colModel;
39540             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39541                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39542             }
39543         }
39544     },
39545
39546     endDrag : function(e){
39547         this.view.headersDisabled = false;
39548         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39549         var diff = endX - this.startPos;
39550         // 
39551         var w = this.cm.getColumnWidth(this.cellIndex);
39552         if (!this.view.mainWrap) {
39553             w = 0;
39554         }
39555         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39556     },
39557
39558     autoOffset : function(){
39559         this.setDelta(0,0);
39560     }
39561 });/*
39562  * Based on:
39563  * Ext JS Library 1.1.1
39564  * Copyright(c) 2006-2007, Ext JS, LLC.
39565  *
39566  * Originally Released Under LGPL - original licence link has changed is not relivant.
39567  *
39568  * Fork - LGPL
39569  * <script type="text/javascript">
39570  */
39571  
39572 // private
39573 // This is a support class used internally by the Grid components
39574 Roo.grid.GridDragZone = function(grid, config){
39575     this.view = grid.getView();
39576     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39577     if(this.view.lockedBody){
39578         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39579         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39580     }
39581     this.scroll = false;
39582     this.grid = grid;
39583     this.ddel = document.createElement('div');
39584     this.ddel.className = 'x-grid-dd-wrap';
39585 };
39586
39587 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39588     ddGroup : "GridDD",
39589
39590     getDragData : function(e){
39591         var t = Roo.lib.Event.getTarget(e);
39592         var rowIndex = this.view.findRowIndex(t);
39593         var sm = this.grid.selModel;
39594             
39595         //Roo.log(rowIndex);
39596         
39597         if (sm.getSelectedCell) {
39598             // cell selection..
39599             if (!sm.getSelectedCell()) {
39600                 return false;
39601             }
39602             if (rowIndex != sm.getSelectedCell()[0]) {
39603                 return false;
39604             }
39605         
39606         }
39607         if (sm.getSelections && sm.getSelections().length < 1) {
39608             return false;
39609         }
39610         
39611         
39612         // before it used to all dragging of unseleted... - now we dont do that.
39613         if(rowIndex !== false){
39614             
39615             // if editorgrid.. 
39616             
39617             
39618             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39619                
39620             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39621               //  
39622             //}
39623             if (e.hasModifier()){
39624                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39625             }
39626             
39627             Roo.log("getDragData");
39628             
39629             return {
39630                 grid: this.grid,
39631                 ddel: this.ddel,
39632                 rowIndex: rowIndex,
39633                 selections: sm.getSelections ? sm.getSelections() : (
39634                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39635             };
39636         }
39637         return false;
39638     },
39639     
39640     
39641     onInitDrag : function(e){
39642         var data = this.dragData;
39643         this.ddel.innerHTML = this.grid.getDragDropText();
39644         this.proxy.update(this.ddel);
39645         // fire start drag?
39646     },
39647
39648     afterRepair : function(){
39649         this.dragging = false;
39650     },
39651
39652     getRepairXY : function(e, data){
39653         return false;
39654     },
39655
39656     onEndDrag : function(data, e){
39657         // fire end drag?
39658     },
39659
39660     onValidDrop : function(dd, e, id){
39661         // fire drag drop?
39662         this.hideProxy();
39663     },
39664
39665     beforeInvalidDrop : function(e, id){
39666
39667     }
39668 });/*
39669  * Based on:
39670  * Ext JS Library 1.1.1
39671  * Copyright(c) 2006-2007, Ext JS, LLC.
39672  *
39673  * Originally Released Under LGPL - original licence link has changed is not relivant.
39674  *
39675  * Fork - LGPL
39676  * <script type="text/javascript">
39677  */
39678  
39679
39680 /**
39681  * @class Roo.grid.ColumnModel
39682  * @extends Roo.util.Observable
39683  * This is the default implementation of a ColumnModel used by the Grid. It defines
39684  * the columns in the grid.
39685  * <br>Usage:<br>
39686  <pre><code>
39687  var colModel = new Roo.grid.ColumnModel([
39688         {header: "Ticker", width: 60, sortable: true, locked: true},
39689         {header: "Company Name", width: 150, sortable: true},
39690         {header: "Market Cap.", width: 100, sortable: true},
39691         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39692         {header: "Employees", width: 100, sortable: true, resizable: false}
39693  ]);
39694  </code></pre>
39695  * <p>
39696  
39697  * The config options listed for this class are options which may appear in each
39698  * individual column definition.
39699  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39700  * @constructor
39701  * @param {Object} config An Array of column config objects. See this class's
39702  * config objects for details.
39703 */
39704 Roo.grid.ColumnModel = function(config){
39705         /**
39706      * The config passed into the constructor
39707      */
39708     this.config = []; //config;
39709     this.lookup = {};
39710
39711     // if no id, create one
39712     // if the column does not have a dataIndex mapping,
39713     // map it to the order it is in the config
39714     for(var i = 0, len = config.length; i < len; i++){
39715         this.addColumn(config[i]);
39716         
39717     }
39718
39719     /**
39720      * The width of columns which have no width specified (defaults to 100)
39721      * @type Number
39722      */
39723     this.defaultWidth = 100;
39724
39725     /**
39726      * Default sortable of columns which have no sortable specified (defaults to false)
39727      * @type Boolean
39728      */
39729     this.defaultSortable = false;
39730
39731     this.addEvents({
39732         /**
39733              * @event widthchange
39734              * Fires when the width of a column changes.
39735              * @param {ColumnModel} this
39736              * @param {Number} columnIndex The column index
39737              * @param {Number} newWidth The new width
39738              */
39739             "widthchange": true,
39740         /**
39741              * @event headerchange
39742              * Fires when the text of a header changes.
39743              * @param {ColumnModel} this
39744              * @param {Number} columnIndex The column index
39745              * @param {Number} newText The new header text
39746              */
39747             "headerchange": true,
39748         /**
39749              * @event hiddenchange
39750              * Fires when a column is hidden or "unhidden".
39751              * @param {ColumnModel} this
39752              * @param {Number} columnIndex The column index
39753              * @param {Boolean} hidden true if hidden, false otherwise
39754              */
39755             "hiddenchange": true,
39756             /**
39757          * @event columnmoved
39758          * Fires when a column is moved.
39759          * @param {ColumnModel} this
39760          * @param {Number} oldIndex
39761          * @param {Number} newIndex
39762          */
39763         "columnmoved" : true,
39764         /**
39765          * @event columlockchange
39766          * Fires when a column's locked state is changed
39767          * @param {ColumnModel} this
39768          * @param {Number} colIndex
39769          * @param {Boolean} locked true if locked
39770          */
39771         "columnlockchange" : true
39772     });
39773     Roo.grid.ColumnModel.superclass.constructor.call(this);
39774 };
39775 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39776     /**
39777      * @cfg {String} header The header text to display in the Grid view.
39778      */
39779         /**
39780      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39781      */
39782         /**
39783      * @cfg {String} smHeader Header at Bootsrap Small width
39784      */
39785         /**
39786      * @cfg {String} mdHeader Header at Bootsrap Medium width
39787      */
39788         /**
39789      * @cfg {String} lgHeader Header at Bootsrap Large width
39790      */
39791         /**
39792      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39793      */
39794     /**
39795      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39796      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39797      * specified, the column's index is used as an index into the Record's data Array.
39798      */
39799     /**
39800      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39801      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39802      */
39803     /**
39804      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39805      * Defaults to the value of the {@link #defaultSortable} property.
39806      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39807      */
39808     /**
39809      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39810      */
39811     /**
39812      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39813      */
39814     /**
39815      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39816      */
39817     /**
39818      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39819      */
39820     /**
39821      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39822      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39823      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39824      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39825      */
39826        /**
39827      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39828      */
39829     /**
39830      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39831      */
39832     /**
39833      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
39834      */
39835     /**
39836      * @cfg {String} cursor (Optional)
39837      */
39838     /**
39839      * @cfg {String} tooltip (Optional)
39840      */
39841     /**
39842      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
39843      */
39844     /**
39845      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
39846      */
39847     /**
39848      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
39849      */
39850     /**
39851      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
39852      */
39853         /**
39854      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
39855      */
39856     /**
39857      * Returns the id of the column at the specified index.
39858      * @param {Number} index The column index
39859      * @return {String} the id
39860      */
39861     getColumnId : function(index){
39862         return this.config[index].id;
39863     },
39864
39865     /**
39866      * Returns the column for a specified id.
39867      * @param {String} id The column id
39868      * @return {Object} the column
39869      */
39870     getColumnById : function(id){
39871         return this.lookup[id];
39872     },
39873
39874     
39875     /**
39876      * Returns the column Object for a specified dataIndex.
39877      * @param {String} dataIndex The column dataIndex
39878      * @return {Object|Boolean} the column or false if not found
39879      */
39880     getColumnByDataIndex: function(dataIndex){
39881         var index = this.findColumnIndex(dataIndex);
39882         return index > -1 ? this.config[index] : false;
39883     },
39884     
39885     /**
39886      * Returns the index for a specified column id.
39887      * @param {String} id The column id
39888      * @return {Number} the index, or -1 if not found
39889      */
39890     getIndexById : function(id){
39891         for(var i = 0, len = this.config.length; i < len; i++){
39892             if(this.config[i].id == id){
39893                 return i;
39894             }
39895         }
39896         return -1;
39897     },
39898     
39899     /**
39900      * Returns the index for a specified column dataIndex.
39901      * @param {String} dataIndex The column dataIndex
39902      * @return {Number} the index, or -1 if not found
39903      */
39904     
39905     findColumnIndex : function(dataIndex){
39906         for(var i = 0, len = this.config.length; i < len; i++){
39907             if(this.config[i].dataIndex == dataIndex){
39908                 return i;
39909             }
39910         }
39911         return -1;
39912     },
39913     
39914     
39915     moveColumn : function(oldIndex, newIndex){
39916         var c = this.config[oldIndex];
39917         this.config.splice(oldIndex, 1);
39918         this.config.splice(newIndex, 0, c);
39919         this.dataMap = null;
39920         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39921     },
39922
39923     isLocked : function(colIndex){
39924         return this.config[colIndex].locked === true;
39925     },
39926
39927     setLocked : function(colIndex, value, suppressEvent){
39928         if(this.isLocked(colIndex) == value){
39929             return;
39930         }
39931         this.config[colIndex].locked = value;
39932         if(!suppressEvent){
39933             this.fireEvent("columnlockchange", this, colIndex, value);
39934         }
39935     },
39936
39937     getTotalLockedWidth : function(){
39938         var totalWidth = 0;
39939         for(var i = 0; i < this.config.length; i++){
39940             if(this.isLocked(i) && !this.isHidden(i)){
39941                 this.totalWidth += this.getColumnWidth(i);
39942             }
39943         }
39944         return totalWidth;
39945     },
39946
39947     getLockedCount : function(){
39948         for(var i = 0, len = this.config.length; i < len; i++){
39949             if(!this.isLocked(i)){
39950                 return i;
39951             }
39952         }
39953         
39954         return this.config.length;
39955     },
39956
39957     /**
39958      * Returns the number of columns.
39959      * @return {Number}
39960      */
39961     getColumnCount : function(visibleOnly){
39962         if(visibleOnly === true){
39963             var c = 0;
39964             for(var i = 0, len = this.config.length; i < len; i++){
39965                 if(!this.isHidden(i)){
39966                     c++;
39967                 }
39968             }
39969             return c;
39970         }
39971         return this.config.length;
39972     },
39973
39974     /**
39975      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39976      * @param {Function} fn
39977      * @param {Object} scope (optional)
39978      * @return {Array} result
39979      */
39980     getColumnsBy : function(fn, scope){
39981         var r = [];
39982         for(var i = 0, len = this.config.length; i < len; i++){
39983             var c = this.config[i];
39984             if(fn.call(scope||this, c, i) === true){
39985                 r[r.length] = c;
39986             }
39987         }
39988         return r;
39989     },
39990
39991     /**
39992      * Returns true if the specified column is sortable.
39993      * @param {Number} col The column index
39994      * @return {Boolean}
39995      */
39996     isSortable : function(col){
39997         if(typeof this.config[col].sortable == "undefined"){
39998             return this.defaultSortable;
39999         }
40000         return this.config[col].sortable;
40001     },
40002
40003     /**
40004      * Returns the rendering (formatting) function defined for the column.
40005      * @param {Number} col The column index.
40006      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40007      */
40008     getRenderer : function(col){
40009         if(!this.config[col].renderer){
40010             return Roo.grid.ColumnModel.defaultRenderer;
40011         }
40012         return this.config[col].renderer;
40013     },
40014
40015     /**
40016      * Sets the rendering (formatting) function for a column.
40017      * @param {Number} col The column index
40018      * @param {Function} fn The function to use to process the cell's raw data
40019      * to return HTML markup for the grid view. The render function is called with
40020      * the following parameters:<ul>
40021      * <li>Data value.</li>
40022      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40023      * <li>css A CSS style string to apply to the table cell.</li>
40024      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40025      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40026      * <li>Row index</li>
40027      * <li>Column index</li>
40028      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40029      */
40030     setRenderer : function(col, fn){
40031         this.config[col].renderer = fn;
40032     },
40033
40034     /**
40035      * Returns the width for the specified column.
40036      * @param {Number} col The column index
40037      * @param (optional) {String} gridSize bootstrap width size.
40038      * @return {Number}
40039      */
40040     getColumnWidth : function(col, gridSize)
40041         {
40042                 var cfg = this.config[col];
40043                 
40044                 if (typeof(gridSize) == 'undefined') {
40045                         return cfg.width * 1 || this.defaultWidth;
40046                 }
40047                 if (gridSize === false) { // if we set it..
40048                         return cfg.width || false;
40049                 }
40050                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40051                 
40052                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40053                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40054                                 continue;
40055                         }
40056                         return cfg[ sizes[i] ];
40057                 }
40058                 return 1;
40059                 
40060     },
40061
40062     /**
40063      * Sets the width for a column.
40064      * @param {Number} col The column index
40065      * @param {Number} width The new width
40066      */
40067     setColumnWidth : function(col, width, suppressEvent){
40068         this.config[col].width = width;
40069         this.totalWidth = null;
40070         if(!suppressEvent){
40071              this.fireEvent("widthchange", this, col, width);
40072         }
40073     },
40074
40075     /**
40076      * Returns the total width of all columns.
40077      * @param {Boolean} includeHidden True to include hidden column widths
40078      * @return {Number}
40079      */
40080     getTotalWidth : function(includeHidden){
40081         if(!this.totalWidth){
40082             this.totalWidth = 0;
40083             for(var i = 0, len = this.config.length; i < len; i++){
40084                 if(includeHidden || !this.isHidden(i)){
40085                     this.totalWidth += this.getColumnWidth(i);
40086                 }
40087             }
40088         }
40089         return this.totalWidth;
40090     },
40091
40092     /**
40093      * Returns the header for the specified column.
40094      * @param {Number} col The column index
40095      * @return {String}
40096      */
40097     getColumnHeader : function(col){
40098         return this.config[col].header;
40099     },
40100
40101     /**
40102      * Sets the header for a column.
40103      * @param {Number} col The column index
40104      * @param {String} header The new header
40105      */
40106     setColumnHeader : function(col, header){
40107         this.config[col].header = header;
40108         this.fireEvent("headerchange", this, col, header);
40109     },
40110
40111     /**
40112      * Returns the tooltip for the specified column.
40113      * @param {Number} col The column index
40114      * @return {String}
40115      */
40116     getColumnTooltip : function(col){
40117             return this.config[col].tooltip;
40118     },
40119     /**
40120      * Sets the tooltip for a column.
40121      * @param {Number} col The column index
40122      * @param {String} tooltip The new tooltip
40123      */
40124     setColumnTooltip : function(col, tooltip){
40125             this.config[col].tooltip = tooltip;
40126     },
40127
40128     /**
40129      * Returns the dataIndex for the specified column.
40130      * @param {Number} col The column index
40131      * @return {Number}
40132      */
40133     getDataIndex : function(col){
40134         return this.config[col].dataIndex;
40135     },
40136
40137     /**
40138      * Sets the dataIndex for a column.
40139      * @param {Number} col The column index
40140      * @param {Number} dataIndex The new dataIndex
40141      */
40142     setDataIndex : function(col, dataIndex){
40143         this.config[col].dataIndex = dataIndex;
40144     },
40145
40146     
40147     
40148     /**
40149      * Returns true if the cell is editable.
40150      * @param {Number} colIndex The column index
40151      * @param {Number} rowIndex The row index - this is nto actually used..?
40152      * @return {Boolean}
40153      */
40154     isCellEditable : function(colIndex, rowIndex){
40155         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40156     },
40157
40158     /**
40159      * Returns the editor defined for the cell/column.
40160      * return false or null to disable editing.
40161      * @param {Number} colIndex The column index
40162      * @param {Number} rowIndex The row index
40163      * @return {Object}
40164      */
40165     getCellEditor : function(colIndex, rowIndex){
40166         return this.config[colIndex].editor;
40167     },
40168
40169     /**
40170      * Sets if a column is editable.
40171      * @param {Number} col The column index
40172      * @param {Boolean} editable True if the column is editable
40173      */
40174     setEditable : function(col, editable){
40175         this.config[col].editable = editable;
40176     },
40177
40178
40179     /**
40180      * Returns true if the column is hidden.
40181      * @param {Number} colIndex The column index
40182      * @return {Boolean}
40183      */
40184     isHidden : function(colIndex){
40185         return this.config[colIndex].hidden;
40186     },
40187
40188
40189     /**
40190      * Returns true if the column width cannot be changed
40191      */
40192     isFixed : function(colIndex){
40193         return this.config[colIndex].fixed;
40194     },
40195
40196     /**
40197      * Returns true if the column can be resized
40198      * @return {Boolean}
40199      */
40200     isResizable : function(colIndex){
40201         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40202     },
40203     /**
40204      * Sets if a column is hidden.
40205      * @param {Number} colIndex The column index
40206      * @param {Boolean} hidden True if the column is hidden
40207      */
40208     setHidden : function(colIndex, hidden){
40209         this.config[colIndex].hidden = hidden;
40210         this.totalWidth = null;
40211         this.fireEvent("hiddenchange", this, colIndex, hidden);
40212     },
40213
40214     /**
40215      * Sets the editor for a column.
40216      * @param {Number} col The column index
40217      * @param {Object} editor The editor object
40218      */
40219     setEditor : function(col, editor){
40220         this.config[col].editor = editor;
40221     },
40222     /**
40223      * Add a column (experimental...) - defaults to adding to the end..
40224      * @param {Object} config 
40225     */
40226     addColumn : function(c)
40227     {
40228     
40229         var i = this.config.length;
40230         this.config[i] = c;
40231         
40232         if(typeof c.dataIndex == "undefined"){
40233             c.dataIndex = i;
40234         }
40235         if(typeof c.renderer == "string"){
40236             c.renderer = Roo.util.Format[c.renderer];
40237         }
40238         if(typeof c.id == "undefined"){
40239             c.id = Roo.id();
40240         }
40241         if(c.editor && c.editor.xtype){
40242             c.editor  = Roo.factory(c.editor, Roo.grid);
40243         }
40244         if(c.editor && c.editor.isFormField){
40245             c.editor = new Roo.grid.GridEditor(c.editor);
40246         }
40247         this.lookup[c.id] = c;
40248     }
40249     
40250 });
40251
40252 Roo.grid.ColumnModel.defaultRenderer = function(value)
40253 {
40254     if(typeof value == "object") {
40255         return value;
40256     }
40257         if(typeof value == "string" && value.length < 1){
40258             return "&#160;";
40259         }
40260     
40261         return String.format("{0}", value);
40262 };
40263
40264 // Alias for backwards compatibility
40265 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40266 /*
40267  * Based on:
40268  * Ext JS Library 1.1.1
40269  * Copyright(c) 2006-2007, Ext JS, LLC.
40270  *
40271  * Originally Released Under LGPL - original licence link has changed is not relivant.
40272  *
40273  * Fork - LGPL
40274  * <script type="text/javascript">
40275  */
40276
40277 /**
40278  * @class Roo.grid.AbstractSelectionModel
40279  * @extends Roo.util.Observable
40280  * @abstract
40281  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40282  * implemented by descendant classes.  This class should not be directly instantiated.
40283  * @constructor
40284  */
40285 Roo.grid.AbstractSelectionModel = function(){
40286     this.locked = false;
40287     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40288 };
40289
40290 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40291     /** @ignore Called by the grid automatically. Do not call directly. */
40292     init : function(grid){
40293         this.grid = grid;
40294         this.initEvents();
40295     },
40296
40297     /**
40298      * Locks the selections.
40299      */
40300     lock : function(){
40301         this.locked = true;
40302     },
40303
40304     /**
40305      * Unlocks the selections.
40306      */
40307     unlock : function(){
40308         this.locked = false;
40309     },
40310
40311     /**
40312      * Returns true if the selections are locked.
40313      * @return {Boolean}
40314      */
40315     isLocked : function(){
40316         return this.locked;
40317     }
40318 });/*
40319  * Based on:
40320  * Ext JS Library 1.1.1
40321  * Copyright(c) 2006-2007, Ext JS, LLC.
40322  *
40323  * Originally Released Under LGPL - original licence link has changed is not relivant.
40324  *
40325  * Fork - LGPL
40326  * <script type="text/javascript">
40327  */
40328 /**
40329  * @extends Roo.grid.AbstractSelectionModel
40330  * @class Roo.grid.RowSelectionModel
40331  * The default SelectionModel used by {@link Roo.grid.Grid}.
40332  * It supports multiple selections and keyboard selection/navigation. 
40333  * @constructor
40334  * @param {Object} config
40335  */
40336 Roo.grid.RowSelectionModel = function(config){
40337     Roo.apply(this, config);
40338     this.selections = new Roo.util.MixedCollection(false, function(o){
40339         return o.id;
40340     });
40341
40342     this.last = false;
40343     this.lastActive = false;
40344
40345     this.addEvents({
40346         /**
40347         * @event selectionchange
40348         * Fires when the selection changes
40349         * @param {SelectionModel} this
40350         */
40351        "selectionchange" : true,
40352        /**
40353         * @event afterselectionchange
40354         * Fires after the selection changes (eg. by key press or clicking)
40355         * @param {SelectionModel} this
40356         */
40357        "afterselectionchange" : true,
40358        /**
40359         * @event beforerowselect
40360         * Fires when a row is selected being selected, return false to cancel.
40361         * @param {SelectionModel} this
40362         * @param {Number} rowIndex The selected index
40363         * @param {Boolean} keepExisting False if other selections will be cleared
40364         */
40365        "beforerowselect" : true,
40366        /**
40367         * @event rowselect
40368         * Fires when a row is selected.
40369         * @param {SelectionModel} this
40370         * @param {Number} rowIndex The selected index
40371         * @param {Roo.data.Record} r The record
40372         */
40373        "rowselect" : true,
40374        /**
40375         * @event rowdeselect
40376         * Fires when a row is deselected.
40377         * @param {SelectionModel} this
40378         * @param {Number} rowIndex The selected index
40379         */
40380         "rowdeselect" : true
40381     });
40382     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40383     this.locked = false;
40384 };
40385
40386 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40387     /**
40388      * @cfg {Boolean} singleSelect
40389      * True to allow selection of only one row at a time (defaults to false)
40390      */
40391     singleSelect : false,
40392
40393     // private
40394     initEvents : function(){
40395
40396         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40397             this.grid.on("mousedown", this.handleMouseDown, this);
40398         }else{ // allow click to work like normal
40399             this.grid.on("rowclick", this.handleDragableRowClick, this);
40400         }
40401         // bootstrap does not have a view..
40402         var view = this.grid.view ? this.grid.view : this.grid;
40403         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40404             "up" : function(e){
40405                 if(!e.shiftKey){
40406                     this.selectPrevious(e.shiftKey);
40407                 }else if(this.last !== false && this.lastActive !== false){
40408                     var last = this.last;
40409                     this.selectRange(this.last,  this.lastActive-1);
40410                     view.focusRow(this.lastActive);
40411                     if(last !== false){
40412                         this.last = last;
40413                     }
40414                 }else{
40415                     this.selectFirstRow();
40416                 }
40417                 this.fireEvent("afterselectionchange", this);
40418             },
40419             "down" : function(e){
40420                 if(!e.shiftKey){
40421                     this.selectNext(e.shiftKey);
40422                 }else if(this.last !== false && this.lastActive !== false){
40423                     var last = this.last;
40424                     this.selectRange(this.last,  this.lastActive+1);
40425                     view.focusRow(this.lastActive);
40426                     if(last !== false){
40427                         this.last = last;
40428                     }
40429                 }else{
40430                     this.selectFirstRow();
40431                 }
40432                 this.fireEvent("afterselectionchange", this);
40433             },
40434             scope: this
40435         });
40436
40437          
40438         view.on("refresh", this.onRefresh, this);
40439         view.on("rowupdated", this.onRowUpdated, this);
40440         view.on("rowremoved", this.onRemove, this);
40441     },
40442
40443     // private
40444     onRefresh : function(){
40445         var ds = this.grid.ds, i, v = this.grid.view;
40446         var s = this.selections;
40447         s.each(function(r){
40448             if((i = ds.indexOfId(r.id)) != -1){
40449                 v.onRowSelect(i);
40450                 s.add(ds.getAt(i)); // updating the selection relate data
40451             }else{
40452                 s.remove(r);
40453             }
40454         });
40455     },
40456
40457     // private
40458     onRemove : function(v, index, r){
40459         this.selections.remove(r);
40460     },
40461
40462     // private
40463     onRowUpdated : function(v, index, r){
40464         if(this.isSelected(r)){
40465             v.onRowSelect(index);
40466         }
40467     },
40468
40469     /**
40470      * Select records.
40471      * @param {Array} records The records to select
40472      * @param {Boolean} keepExisting (optional) True to keep existing selections
40473      */
40474     selectRecords : function(records, keepExisting){
40475         if(!keepExisting){
40476             this.clearSelections();
40477         }
40478         var ds = this.grid.ds;
40479         for(var i = 0, len = records.length; i < len; i++){
40480             this.selectRow(ds.indexOf(records[i]), true);
40481         }
40482     },
40483
40484     /**
40485      * Gets the number of selected rows.
40486      * @return {Number}
40487      */
40488     getCount : function(){
40489         return this.selections.length;
40490     },
40491
40492     /**
40493      * Selects the first row in the grid.
40494      */
40495     selectFirstRow : function(){
40496         this.selectRow(0);
40497     },
40498
40499     /**
40500      * Select the last row.
40501      * @param {Boolean} keepExisting (optional) True to keep existing selections
40502      */
40503     selectLastRow : function(keepExisting){
40504         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40505     },
40506
40507     /**
40508      * Selects the row immediately following the last selected row.
40509      * @param {Boolean} keepExisting (optional) True to keep existing selections
40510      */
40511     selectNext : function(keepExisting){
40512         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40513             this.selectRow(this.last+1, keepExisting);
40514             var view = this.grid.view ? this.grid.view : this.grid;
40515             view.focusRow(this.last);
40516         }
40517     },
40518
40519     /**
40520      * Selects the row that precedes the last selected row.
40521      * @param {Boolean} keepExisting (optional) True to keep existing selections
40522      */
40523     selectPrevious : function(keepExisting){
40524         if(this.last){
40525             this.selectRow(this.last-1, keepExisting);
40526             var view = this.grid.view ? this.grid.view : this.grid;
40527             view.focusRow(this.last);
40528         }
40529     },
40530
40531     /**
40532      * Returns the selected records
40533      * @return {Array} Array of selected records
40534      */
40535     getSelections : function(){
40536         return [].concat(this.selections.items);
40537     },
40538
40539     /**
40540      * Returns the first selected record.
40541      * @return {Record}
40542      */
40543     getSelected : function(){
40544         return this.selections.itemAt(0);
40545     },
40546
40547
40548     /**
40549      * Clears all selections.
40550      */
40551     clearSelections : function(fast){
40552         if(this.locked) {
40553             return;
40554         }
40555         if(fast !== true){
40556             var ds = this.grid.ds;
40557             var s = this.selections;
40558             s.each(function(r){
40559                 this.deselectRow(ds.indexOfId(r.id));
40560             }, this);
40561             s.clear();
40562         }else{
40563             this.selections.clear();
40564         }
40565         this.last = false;
40566     },
40567
40568
40569     /**
40570      * Selects all rows.
40571      */
40572     selectAll : function(){
40573         if(this.locked) {
40574             return;
40575         }
40576         this.selections.clear();
40577         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40578             this.selectRow(i, true);
40579         }
40580     },
40581
40582     /**
40583      * Returns True if there is a selection.
40584      * @return {Boolean}
40585      */
40586     hasSelection : function(){
40587         return this.selections.length > 0;
40588     },
40589
40590     /**
40591      * Returns True if the specified row is selected.
40592      * @param {Number/Record} record The record or index of the record to check
40593      * @return {Boolean}
40594      */
40595     isSelected : function(index){
40596         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40597         return (r && this.selections.key(r.id) ? true : false);
40598     },
40599
40600     /**
40601      * Returns True if the specified record id is selected.
40602      * @param {String} id The id of record to check
40603      * @return {Boolean}
40604      */
40605     isIdSelected : function(id){
40606         return (this.selections.key(id) ? true : false);
40607     },
40608
40609     // private
40610     handleMouseDown : function(e, t)
40611     {
40612         var view = this.grid.view ? this.grid.view : this.grid;
40613         var rowIndex;
40614         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40615             return;
40616         };
40617         if(e.shiftKey && this.last !== false){
40618             var last = this.last;
40619             this.selectRange(last, rowIndex, e.ctrlKey);
40620             this.last = last; // reset the last
40621             view.focusRow(rowIndex);
40622         }else{
40623             var isSelected = this.isSelected(rowIndex);
40624             if(e.button !== 0 && isSelected){
40625                 view.focusRow(rowIndex);
40626             }else if(e.ctrlKey && isSelected){
40627                 this.deselectRow(rowIndex);
40628             }else if(!isSelected){
40629                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40630                 view.focusRow(rowIndex);
40631             }
40632         }
40633         this.fireEvent("afterselectionchange", this);
40634     },
40635     // private
40636     handleDragableRowClick :  function(grid, rowIndex, e) 
40637     {
40638         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40639             this.selectRow(rowIndex, false);
40640             var view = this.grid.view ? this.grid.view : this.grid;
40641             view.focusRow(rowIndex);
40642              this.fireEvent("afterselectionchange", this);
40643         }
40644     },
40645     
40646     /**
40647      * Selects multiple rows.
40648      * @param {Array} rows Array of the indexes of the row to select
40649      * @param {Boolean} keepExisting (optional) True to keep existing selections
40650      */
40651     selectRows : function(rows, keepExisting){
40652         if(!keepExisting){
40653             this.clearSelections();
40654         }
40655         for(var i = 0, len = rows.length; i < len; i++){
40656             this.selectRow(rows[i], true);
40657         }
40658     },
40659
40660     /**
40661      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40662      * @param {Number} startRow The index of the first row in the range
40663      * @param {Number} endRow The index of the last row in the range
40664      * @param {Boolean} keepExisting (optional) True to retain existing selections
40665      */
40666     selectRange : function(startRow, endRow, keepExisting){
40667         if(this.locked) {
40668             return;
40669         }
40670         if(!keepExisting){
40671             this.clearSelections();
40672         }
40673         if(startRow <= endRow){
40674             for(var i = startRow; i <= endRow; i++){
40675                 this.selectRow(i, true);
40676             }
40677         }else{
40678             for(var i = startRow; i >= endRow; i--){
40679                 this.selectRow(i, true);
40680             }
40681         }
40682     },
40683
40684     /**
40685      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40686      * @param {Number} startRow The index of the first row in the range
40687      * @param {Number} endRow The index of the last row in the range
40688      */
40689     deselectRange : function(startRow, endRow, preventViewNotify){
40690         if(this.locked) {
40691             return;
40692         }
40693         for(var i = startRow; i <= endRow; i++){
40694             this.deselectRow(i, preventViewNotify);
40695         }
40696     },
40697
40698     /**
40699      * Selects a row.
40700      * @param {Number} row The index of the row to select
40701      * @param {Boolean} keepExisting (optional) True to keep existing selections
40702      */
40703     selectRow : function(index, keepExisting, preventViewNotify){
40704         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40705             return;
40706         }
40707         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40708             if(!keepExisting || this.singleSelect){
40709                 this.clearSelections();
40710             }
40711             var r = this.grid.ds.getAt(index);
40712             this.selections.add(r);
40713             this.last = this.lastActive = index;
40714             if(!preventViewNotify){
40715                 var view = this.grid.view ? this.grid.view : this.grid;
40716                 view.onRowSelect(index);
40717             }
40718             this.fireEvent("rowselect", this, index, r);
40719             this.fireEvent("selectionchange", this);
40720         }
40721     },
40722
40723     /**
40724      * Deselects a row.
40725      * @param {Number} row The index of the row to deselect
40726      */
40727     deselectRow : function(index, preventViewNotify){
40728         if(this.locked) {
40729             return;
40730         }
40731         if(this.last == index){
40732             this.last = false;
40733         }
40734         if(this.lastActive == index){
40735             this.lastActive = false;
40736         }
40737         var r = this.grid.ds.getAt(index);
40738         this.selections.remove(r);
40739         if(!preventViewNotify){
40740             var view = this.grid.view ? this.grid.view : this.grid;
40741             view.onRowDeselect(index);
40742         }
40743         this.fireEvent("rowdeselect", this, index);
40744         this.fireEvent("selectionchange", this);
40745     },
40746
40747     // private
40748     restoreLast : function(){
40749         if(this._last){
40750             this.last = this._last;
40751         }
40752     },
40753
40754     // private
40755     acceptsNav : function(row, col, cm){
40756         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40757     },
40758
40759     // private
40760     onEditorKey : function(field, e){
40761         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40762         if(k == e.TAB){
40763             e.stopEvent();
40764             ed.completeEdit();
40765             if(e.shiftKey){
40766                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40767             }else{
40768                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40769             }
40770         }else if(k == e.ENTER && !e.ctrlKey){
40771             e.stopEvent();
40772             ed.completeEdit();
40773             if(e.shiftKey){
40774                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40775             }else{
40776                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40777             }
40778         }else if(k == e.ESC){
40779             ed.cancelEdit();
40780         }
40781         if(newCell){
40782             g.startEditing(newCell[0], newCell[1]);
40783         }
40784     }
40785 });/*
40786  * Based on:
40787  * Ext JS Library 1.1.1
40788  * Copyright(c) 2006-2007, Ext JS, LLC.
40789  *
40790  * Originally Released Under LGPL - original licence link has changed is not relivant.
40791  *
40792  * Fork - LGPL
40793  * <script type="text/javascript">
40794  */
40795 /**
40796  * @class Roo.grid.CellSelectionModel
40797  * @extends Roo.grid.AbstractSelectionModel
40798  * This class provides the basic implementation for cell selection in a grid.
40799  * @constructor
40800  * @param {Object} config The object containing the configuration of this model.
40801  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40802  */
40803 Roo.grid.CellSelectionModel = function(config){
40804     Roo.apply(this, config);
40805
40806     this.selection = null;
40807
40808     this.addEvents({
40809         /**
40810              * @event beforerowselect
40811              * Fires before a cell is selected.
40812              * @param {SelectionModel} this
40813              * @param {Number} rowIndex The selected row index
40814              * @param {Number} colIndex The selected cell index
40815              */
40816             "beforecellselect" : true,
40817         /**
40818              * @event cellselect
40819              * Fires when a cell is selected.
40820              * @param {SelectionModel} this
40821              * @param {Number} rowIndex The selected row index
40822              * @param {Number} colIndex The selected cell index
40823              */
40824             "cellselect" : true,
40825         /**
40826              * @event selectionchange
40827              * Fires when the active selection changes.
40828              * @param {SelectionModel} this
40829              * @param {Object} selection null for no selection or an object (o) with two properties
40830                 <ul>
40831                 <li>o.record: the record object for the row the selection is in</li>
40832                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40833                 </ul>
40834              */
40835             "selectionchange" : true,
40836         /**
40837              * @event tabend
40838              * Fires when the tab (or enter) was pressed on the last editable cell
40839              * You can use this to trigger add new row.
40840              * @param {SelectionModel} this
40841              */
40842             "tabend" : true,
40843          /**
40844              * @event beforeeditnext
40845              * Fires before the next editable sell is made active
40846              * You can use this to skip to another cell or fire the tabend
40847              *    if you set cell to false
40848              * @param {Object} eventdata object : { cell : [ row, col ] } 
40849              */
40850             "beforeeditnext" : true
40851     });
40852     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40853 };
40854
40855 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40856     
40857     enter_is_tab: false,
40858
40859     /** @ignore */
40860     initEvents : function(){
40861         this.grid.on("mousedown", this.handleMouseDown, this);
40862         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40863         var view = this.grid.view;
40864         view.on("refresh", this.onViewChange, this);
40865         view.on("rowupdated", this.onRowUpdated, this);
40866         view.on("beforerowremoved", this.clearSelections, this);
40867         view.on("beforerowsinserted", this.clearSelections, this);
40868         if(this.grid.isEditor){
40869             this.grid.on("beforeedit", this.beforeEdit,  this);
40870         }
40871     },
40872
40873         //private
40874     beforeEdit : function(e){
40875         this.select(e.row, e.column, false, true, e.record);
40876     },
40877
40878         //private
40879     onRowUpdated : function(v, index, r){
40880         if(this.selection && this.selection.record == r){
40881             v.onCellSelect(index, this.selection.cell[1]);
40882         }
40883     },
40884
40885         //private
40886     onViewChange : function(){
40887         this.clearSelections(true);
40888     },
40889
40890         /**
40891          * Returns the currently selected cell,.
40892          * @return {Array} The selected cell (row, column) or null if none selected.
40893          */
40894     getSelectedCell : function(){
40895         return this.selection ? this.selection.cell : null;
40896     },
40897
40898     /**
40899      * Clears all selections.
40900      * @param {Boolean} true to prevent the gridview from being notified about the change.
40901      */
40902     clearSelections : function(preventNotify){
40903         var s = this.selection;
40904         if(s){
40905             if(preventNotify !== true){
40906                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40907             }
40908             this.selection = null;
40909             this.fireEvent("selectionchange", this, null);
40910         }
40911     },
40912
40913     /**
40914      * Returns true if there is a selection.
40915      * @return {Boolean}
40916      */
40917     hasSelection : function(){
40918         return this.selection ? true : false;
40919     },
40920
40921     /** @ignore */
40922     handleMouseDown : function(e, t){
40923         var v = this.grid.getView();
40924         if(this.isLocked()){
40925             return;
40926         };
40927         var row = v.findRowIndex(t);
40928         var cell = v.findCellIndex(t);
40929         if(row !== false && cell !== false){
40930             this.select(row, cell);
40931         }
40932     },
40933
40934     /**
40935      * Selects a cell.
40936      * @param {Number} rowIndex
40937      * @param {Number} collIndex
40938      */
40939     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40940         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40941             this.clearSelections();
40942             r = r || this.grid.dataSource.getAt(rowIndex);
40943             this.selection = {
40944                 record : r,
40945                 cell : [rowIndex, colIndex]
40946             };
40947             if(!preventViewNotify){
40948                 var v = this.grid.getView();
40949                 v.onCellSelect(rowIndex, colIndex);
40950                 if(preventFocus !== true){
40951                     v.focusCell(rowIndex, colIndex);
40952                 }
40953             }
40954             this.fireEvent("cellselect", this, rowIndex, colIndex);
40955             this.fireEvent("selectionchange", this, this.selection);
40956         }
40957     },
40958
40959         //private
40960     isSelectable : function(rowIndex, colIndex, cm){
40961         return !cm.isHidden(colIndex);
40962     },
40963
40964     /** @ignore */
40965     handleKeyDown : function(e){
40966         //Roo.log('Cell Sel Model handleKeyDown');
40967         if(!e.isNavKeyPress()){
40968             return;
40969         }
40970         var g = this.grid, s = this.selection;
40971         if(!s){
40972             e.stopEvent();
40973             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40974             if(cell){
40975                 this.select(cell[0], cell[1]);
40976             }
40977             return;
40978         }
40979         var sm = this;
40980         var walk = function(row, col, step){
40981             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40982         };
40983         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40984         var newCell;
40985
40986       
40987
40988         switch(k){
40989             case e.TAB:
40990                 // handled by onEditorKey
40991                 if (g.isEditor && g.editing) {
40992                     return;
40993                 }
40994                 if(e.shiftKey) {
40995                     newCell = walk(r, c-1, -1);
40996                 } else {
40997                     newCell = walk(r, c+1, 1);
40998                 }
40999                 break;
41000             
41001             case e.DOWN:
41002                newCell = walk(r+1, c, 1);
41003                 break;
41004             
41005             case e.UP:
41006                 newCell = walk(r-1, c, -1);
41007                 break;
41008             
41009             case e.RIGHT:
41010                 newCell = walk(r, c+1, 1);
41011                 break;
41012             
41013             case e.LEFT:
41014                 newCell = walk(r, c-1, -1);
41015                 break;
41016             
41017             case e.ENTER:
41018                 
41019                 if(g.isEditor && !g.editing){
41020                    g.startEditing(r, c);
41021                    e.stopEvent();
41022                    return;
41023                 }
41024                 
41025                 
41026              break;
41027         };
41028         if(newCell){
41029             this.select(newCell[0], newCell[1]);
41030             e.stopEvent();
41031             
41032         }
41033     },
41034
41035     acceptsNav : function(row, col, cm){
41036         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41037     },
41038     /**
41039      * Selects a cell.
41040      * @param {Number} field (not used) - as it's normally used as a listener
41041      * @param {Number} e - event - fake it by using
41042      *
41043      * var e = Roo.EventObjectImpl.prototype;
41044      * e.keyCode = e.TAB
41045      *
41046      * 
41047      */
41048     onEditorKey : function(field, e){
41049         
41050         var k = e.getKey(),
41051             newCell,
41052             g = this.grid,
41053             ed = g.activeEditor,
41054             forward = false;
41055         ///Roo.log('onEditorKey' + k);
41056         
41057         
41058         if (this.enter_is_tab && k == e.ENTER) {
41059             k = e.TAB;
41060         }
41061         
41062         if(k == e.TAB){
41063             if(e.shiftKey){
41064                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41065             }else{
41066                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41067                 forward = true;
41068             }
41069             
41070             e.stopEvent();
41071             
41072         } else if(k == e.ENTER &&  !e.ctrlKey){
41073             ed.completeEdit();
41074             e.stopEvent();
41075             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41076         
41077                 } else if(k == e.ESC){
41078             ed.cancelEdit();
41079         }
41080                 
41081         if (newCell) {
41082             var ecall = { cell : newCell, forward : forward };
41083             this.fireEvent('beforeeditnext', ecall );
41084             newCell = ecall.cell;
41085                         forward = ecall.forward;
41086         }
41087                 
41088         if(newCell){
41089             //Roo.log('next cell after edit');
41090             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41091         } else if (forward) {
41092             // tabbed past last
41093             this.fireEvent.defer(100, this, ['tabend',this]);
41094         }
41095     }
41096 });/*
41097  * Based on:
41098  * Ext JS Library 1.1.1
41099  * Copyright(c) 2006-2007, Ext JS, LLC.
41100  *
41101  * Originally Released Under LGPL - original licence link has changed is not relivant.
41102  *
41103  * Fork - LGPL
41104  * <script type="text/javascript">
41105  */
41106  
41107 /**
41108  * @class Roo.grid.EditorGrid
41109  * @extends Roo.grid.Grid
41110  * Class for creating and editable grid.
41111  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41112  * The container MUST have some type of size defined for the grid to fill. The container will be 
41113  * automatically set to position relative if it isn't already.
41114  * @param {Object} dataSource The data model to bind to
41115  * @param {Object} colModel The column model with info about this grid's columns
41116  */
41117 Roo.grid.EditorGrid = function(container, config){
41118     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41119     this.getGridEl().addClass("xedit-grid");
41120
41121     if(!this.selModel){
41122         this.selModel = new Roo.grid.CellSelectionModel();
41123     }
41124
41125     this.activeEditor = null;
41126
41127         this.addEvents({
41128             /**
41129              * @event beforeedit
41130              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41131              * <ul style="padding:5px;padding-left:16px;">
41132              * <li>grid - This grid</li>
41133              * <li>record - The record being edited</li>
41134              * <li>field - The field name being edited</li>
41135              * <li>value - The value for the field being edited.</li>
41136              * <li>row - The grid row index</li>
41137              * <li>column - The grid column index</li>
41138              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41139              * </ul>
41140              * @param {Object} e An edit event (see above for description)
41141              */
41142             "beforeedit" : true,
41143             /**
41144              * @event afteredit
41145              * Fires after a cell is edited. <br />
41146              * <ul style="padding:5px;padding-left:16px;">
41147              * <li>grid - This grid</li>
41148              * <li>record - The record being edited</li>
41149              * <li>field - The field name being edited</li>
41150              * <li>value - The value being set</li>
41151              * <li>originalValue - The original value for the field, before the edit.</li>
41152              * <li>row - The grid row index</li>
41153              * <li>column - The grid column index</li>
41154              * </ul>
41155              * @param {Object} e An edit event (see above for description)
41156              */
41157             "afteredit" : true,
41158             /**
41159              * @event validateedit
41160              * Fires after a cell is edited, but before the value is set in the record. 
41161          * You can use this to modify the value being set in the field, Return false
41162              * to cancel the change. The edit event object has the following properties <br />
41163              * <ul style="padding:5px;padding-left:16px;">
41164          * <li>editor - This editor</li>
41165              * <li>grid - This grid</li>
41166              * <li>record - The record being edited</li>
41167              * <li>field - The field name being edited</li>
41168              * <li>value - The value being set</li>
41169              * <li>originalValue - The original value for the field, before the edit.</li>
41170              * <li>row - The grid row index</li>
41171              * <li>column - The grid column index</li>
41172              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41173              * </ul>
41174              * @param {Object} e An edit event (see above for description)
41175              */
41176             "validateedit" : true
41177         });
41178     this.on("bodyscroll", this.stopEditing,  this);
41179     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41180 };
41181
41182 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41183     /**
41184      * @cfg {Number} clicksToEdit
41185      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41186      */
41187     clicksToEdit: 2,
41188
41189     // private
41190     isEditor : true,
41191     // private
41192     trackMouseOver: false, // causes very odd FF errors
41193
41194     onCellDblClick : function(g, row, col){
41195         this.startEditing(row, col);
41196     },
41197
41198     onEditComplete : function(ed, value, startValue){
41199         this.editing = false;
41200         this.activeEditor = null;
41201         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41202         var r = ed.record;
41203         var field = this.colModel.getDataIndex(ed.col);
41204         var e = {
41205             grid: this,
41206             record: r,
41207             field: field,
41208             originalValue: startValue,
41209             value: value,
41210             row: ed.row,
41211             column: ed.col,
41212             cancel:false,
41213             editor: ed
41214         };
41215         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41216         cell.show();
41217           
41218         if(String(value) !== String(startValue)){
41219             
41220             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41221                 r.set(field, e.value);
41222                 // if we are dealing with a combo box..
41223                 // then we also set the 'name' colum to be the displayField
41224                 if (ed.field.displayField && ed.field.name) {
41225                     r.set(ed.field.name, ed.field.el.dom.value);
41226                 }
41227                 
41228                 delete e.cancel; //?? why!!!
41229                 this.fireEvent("afteredit", e);
41230             }
41231         } else {
41232             this.fireEvent("afteredit", e); // always fire it!
41233         }
41234         this.view.focusCell(ed.row, ed.col);
41235     },
41236
41237     /**
41238      * Starts editing the specified for the specified row/column
41239      * @param {Number} rowIndex
41240      * @param {Number} colIndex
41241      */
41242     startEditing : function(row, col){
41243         this.stopEditing();
41244         if(this.colModel.isCellEditable(col, row)){
41245             this.view.ensureVisible(row, col, true);
41246           
41247             var r = this.dataSource.getAt(row);
41248             var field = this.colModel.getDataIndex(col);
41249             var cell = Roo.get(this.view.getCell(row,col));
41250             var e = {
41251                 grid: this,
41252                 record: r,
41253                 field: field,
41254                 value: r.data[field],
41255                 row: row,
41256                 column: col,
41257                 cancel:false 
41258             };
41259             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41260                 this.editing = true;
41261                 var ed = this.colModel.getCellEditor(col, row);
41262                 
41263                 if (!ed) {
41264                     return;
41265                 }
41266                 if(!ed.rendered){
41267                     ed.render(ed.parentEl || document.body);
41268                 }
41269                 ed.field.reset();
41270                
41271                 cell.hide();
41272                 
41273                 (function(){ // complex but required for focus issues in safari, ie and opera
41274                     ed.row = row;
41275                     ed.col = col;
41276                     ed.record = r;
41277                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41278                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41279                     this.activeEditor = ed;
41280                     var v = r.data[field];
41281                     ed.startEdit(this.view.getCell(row, col), v);
41282                     // combo's with 'displayField and name set
41283                     if (ed.field.displayField && ed.field.name) {
41284                         ed.field.el.dom.value = r.data[ed.field.name];
41285                     }
41286                     
41287                     
41288                 }).defer(50, this);
41289             }
41290         }
41291     },
41292         
41293     /**
41294      * Stops any active editing
41295      */
41296     stopEditing : function(){
41297         if(this.activeEditor){
41298             this.activeEditor.completeEdit();
41299         }
41300         this.activeEditor = null;
41301     },
41302         
41303          /**
41304      * Called to get grid's drag proxy text, by default returns this.ddText.
41305      * @return {String}
41306      */
41307     getDragDropText : function(){
41308         var count = this.selModel.getSelectedCell() ? 1 : 0;
41309         return String.format(this.ddText, count, count == 1 ? '' : 's');
41310     }
41311         
41312 });/*
41313  * Based on:
41314  * Ext JS Library 1.1.1
41315  * Copyright(c) 2006-2007, Ext JS, LLC.
41316  *
41317  * Originally Released Under LGPL - original licence link has changed is not relivant.
41318  *
41319  * Fork - LGPL
41320  * <script type="text/javascript">
41321  */
41322
41323 // private - not really -- you end up using it !
41324 // This is a support class used internally by the Grid components
41325
41326 /**
41327  * @class Roo.grid.GridEditor
41328  * @extends Roo.Editor
41329  * Class for creating and editable grid elements.
41330  * @param {Object} config any settings (must include field)
41331  */
41332 Roo.grid.GridEditor = function(field, config){
41333     if (!config && field.field) {
41334         config = field;
41335         field = Roo.factory(config.field, Roo.form);
41336     }
41337     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41338     field.monitorTab = false;
41339 };
41340
41341 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41342     
41343     /**
41344      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41345      */
41346     
41347     alignment: "tl-tl",
41348     autoSize: "width",
41349     hideEl : false,
41350     cls: "x-small-editor x-grid-editor",
41351     shim:false,
41352     shadow:"frame"
41353 });/*
41354  * Based on:
41355  * Ext JS Library 1.1.1
41356  * Copyright(c) 2006-2007, Ext JS, LLC.
41357  *
41358  * Originally Released Under LGPL - original licence link has changed is not relivant.
41359  *
41360  * Fork - LGPL
41361  * <script type="text/javascript">
41362  */
41363   
41364
41365   
41366 Roo.grid.PropertyRecord = Roo.data.Record.create([
41367     {name:'name',type:'string'},  'value'
41368 ]);
41369
41370
41371 Roo.grid.PropertyStore = function(grid, source){
41372     this.grid = grid;
41373     this.store = new Roo.data.Store({
41374         recordType : Roo.grid.PropertyRecord
41375     });
41376     this.store.on('update', this.onUpdate,  this);
41377     if(source){
41378         this.setSource(source);
41379     }
41380     Roo.grid.PropertyStore.superclass.constructor.call(this);
41381 };
41382
41383
41384
41385 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41386     setSource : function(o){
41387         this.source = o;
41388         this.store.removeAll();
41389         var data = [];
41390         for(var k in o){
41391             if(this.isEditableValue(o[k])){
41392                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41393             }
41394         }
41395         this.store.loadRecords({records: data}, {}, true);
41396     },
41397
41398     onUpdate : function(ds, record, type){
41399         if(type == Roo.data.Record.EDIT){
41400             var v = record.data['value'];
41401             var oldValue = record.modified['value'];
41402             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41403                 this.source[record.id] = v;
41404                 record.commit();
41405                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41406             }else{
41407                 record.reject();
41408             }
41409         }
41410     },
41411
41412     getProperty : function(row){
41413        return this.store.getAt(row);
41414     },
41415
41416     isEditableValue: function(val){
41417         if(val && val instanceof Date){
41418             return true;
41419         }else if(typeof val == 'object' || typeof val == 'function'){
41420             return false;
41421         }
41422         return true;
41423     },
41424
41425     setValue : function(prop, value){
41426         this.source[prop] = value;
41427         this.store.getById(prop).set('value', value);
41428     },
41429
41430     getSource : function(){
41431         return this.source;
41432     }
41433 });
41434
41435 Roo.grid.PropertyColumnModel = function(grid, store){
41436     this.grid = grid;
41437     var g = Roo.grid;
41438     g.PropertyColumnModel.superclass.constructor.call(this, [
41439         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41440         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41441     ]);
41442     this.store = store;
41443     this.bselect = Roo.DomHelper.append(document.body, {
41444         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41445             {tag: 'option', value: 'true', html: 'true'},
41446             {tag: 'option', value: 'false', html: 'false'}
41447         ]
41448     });
41449     Roo.id(this.bselect);
41450     var f = Roo.form;
41451     this.editors = {
41452         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41453         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41454         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41455         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41456         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41457     };
41458     this.renderCellDelegate = this.renderCell.createDelegate(this);
41459     this.renderPropDelegate = this.renderProp.createDelegate(this);
41460 };
41461
41462 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41463     
41464     
41465     nameText : 'Name',
41466     valueText : 'Value',
41467     
41468     dateFormat : 'm/j/Y',
41469     
41470     
41471     renderDate : function(dateVal){
41472         return dateVal.dateFormat(this.dateFormat);
41473     },
41474
41475     renderBool : function(bVal){
41476         return bVal ? 'true' : 'false';
41477     },
41478
41479     isCellEditable : function(colIndex, rowIndex){
41480         return colIndex == 1;
41481     },
41482
41483     getRenderer : function(col){
41484         return col == 1 ?
41485             this.renderCellDelegate : this.renderPropDelegate;
41486     },
41487
41488     renderProp : function(v){
41489         return this.getPropertyName(v);
41490     },
41491
41492     renderCell : function(val){
41493         var rv = val;
41494         if(val instanceof Date){
41495             rv = this.renderDate(val);
41496         }else if(typeof val == 'boolean'){
41497             rv = this.renderBool(val);
41498         }
41499         return Roo.util.Format.htmlEncode(rv);
41500     },
41501
41502     getPropertyName : function(name){
41503         var pn = this.grid.propertyNames;
41504         return pn && pn[name] ? pn[name] : name;
41505     },
41506
41507     getCellEditor : function(colIndex, rowIndex){
41508         var p = this.store.getProperty(rowIndex);
41509         var n = p.data['name'], val = p.data['value'];
41510         
41511         if(typeof(this.grid.customEditors[n]) == 'string'){
41512             return this.editors[this.grid.customEditors[n]];
41513         }
41514         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41515             return this.grid.customEditors[n];
41516         }
41517         if(val instanceof Date){
41518             return this.editors['date'];
41519         }else if(typeof val == 'number'){
41520             return this.editors['number'];
41521         }else if(typeof val == 'boolean'){
41522             return this.editors['boolean'];
41523         }else{
41524             return this.editors['string'];
41525         }
41526     }
41527 });
41528
41529 /**
41530  * @class Roo.grid.PropertyGrid
41531  * @extends Roo.grid.EditorGrid
41532  * This class represents the  interface of a component based property grid control.
41533  * <br><br>Usage:<pre><code>
41534  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41535       
41536  });
41537  // set any options
41538  grid.render();
41539  * </code></pre>
41540   
41541  * @constructor
41542  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41543  * The container MUST have some type of size defined for the grid to fill. The container will be
41544  * automatically set to position relative if it isn't already.
41545  * @param {Object} config A config object that sets properties on this grid.
41546  */
41547 Roo.grid.PropertyGrid = function(container, config){
41548     config = config || {};
41549     var store = new Roo.grid.PropertyStore(this);
41550     this.store = store;
41551     var cm = new Roo.grid.PropertyColumnModel(this, store);
41552     store.store.sort('name', 'ASC');
41553     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41554         ds: store.store,
41555         cm: cm,
41556         enableColLock:false,
41557         enableColumnMove:false,
41558         stripeRows:false,
41559         trackMouseOver: false,
41560         clicksToEdit:1
41561     }, config));
41562     this.getGridEl().addClass('x-props-grid');
41563     this.lastEditRow = null;
41564     this.on('columnresize', this.onColumnResize, this);
41565     this.addEvents({
41566          /**
41567              * @event beforepropertychange
41568              * Fires before a property changes (return false to stop?)
41569              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41570              * @param {String} id Record Id
41571              * @param {String} newval New Value
41572          * @param {String} oldval Old Value
41573              */
41574         "beforepropertychange": true,
41575         /**
41576              * @event propertychange
41577              * Fires after a property changes
41578              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41579              * @param {String} id Record Id
41580              * @param {String} newval New Value
41581          * @param {String} oldval Old Value
41582              */
41583         "propertychange": true
41584     });
41585     this.customEditors = this.customEditors || {};
41586 };
41587 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41588     
41589      /**
41590      * @cfg {Object} customEditors map of colnames=> custom editors.
41591      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41592      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41593      * false disables editing of the field.
41594          */
41595     
41596       /**
41597      * @cfg {Object} propertyNames map of property Names to their displayed value
41598          */
41599     
41600     render : function(){
41601         Roo.grid.PropertyGrid.superclass.render.call(this);
41602         this.autoSize.defer(100, this);
41603     },
41604
41605     autoSize : function(){
41606         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41607         if(this.view){
41608             this.view.fitColumns();
41609         }
41610     },
41611
41612     onColumnResize : function(){
41613         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41614         this.autoSize();
41615     },
41616     /**
41617      * Sets the data for the Grid
41618      * accepts a Key => Value object of all the elements avaiable.
41619      * @param {Object} data  to appear in grid.
41620      */
41621     setSource : function(source){
41622         this.store.setSource(source);
41623         //this.autoSize();
41624     },
41625     /**
41626      * Gets all the data from the grid.
41627      * @return {Object} data  data stored in grid
41628      */
41629     getSource : function(){
41630         return this.store.getSource();
41631     }
41632 });/*
41633   
41634  * Licence LGPL
41635  
41636  */
41637  
41638 /**
41639  * @class Roo.grid.Calendar
41640  * @extends Roo.grid.Grid
41641  * This class extends the Grid to provide a calendar widget
41642  * <br><br>Usage:<pre><code>
41643  var grid = new Roo.grid.Calendar("my-container-id", {
41644      ds: myDataStore,
41645      cm: myColModel,
41646      selModel: mySelectionModel,
41647      autoSizeColumns: true,
41648      monitorWindowResize: false,
41649      trackMouseOver: true
41650      eventstore : real data store..
41651  });
41652  // set any options
41653  grid.render();
41654   
41655   * @constructor
41656  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41657  * The container MUST have some type of size defined for the grid to fill. The container will be
41658  * automatically set to position relative if it isn't already.
41659  * @param {Object} config A config object that sets properties on this grid.
41660  */
41661 Roo.grid.Calendar = function(container, config){
41662         // initialize the container
41663         this.container = Roo.get(container);
41664         this.container.update("");
41665         this.container.setStyle("overflow", "hidden");
41666     this.container.addClass('x-grid-container');
41667
41668     this.id = this.container.id;
41669
41670     Roo.apply(this, config);
41671     // check and correct shorthanded configs
41672     
41673     var rows = [];
41674     var d =1;
41675     for (var r = 0;r < 6;r++) {
41676         
41677         rows[r]=[];
41678         for (var c =0;c < 7;c++) {
41679             rows[r][c]= '';
41680         }
41681     }
41682     if (this.eventStore) {
41683         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41684         this.eventStore.on('load',this.onLoad, this);
41685         this.eventStore.on('beforeload',this.clearEvents, this);
41686          
41687     }
41688     
41689     this.dataSource = new Roo.data.Store({
41690             proxy: new Roo.data.MemoryProxy(rows),
41691             reader: new Roo.data.ArrayReader({}, [
41692                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41693     });
41694
41695     this.dataSource.load();
41696     this.ds = this.dataSource;
41697     this.ds.xmodule = this.xmodule || false;
41698     
41699     
41700     var cellRender = function(v,x,r)
41701     {
41702         return String.format(
41703             '<div class="fc-day  fc-widget-content"><div>' +
41704                 '<div class="fc-event-container"></div>' +
41705                 '<div class="fc-day-number">{0}</div>'+
41706                 
41707                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41708             '</div></div>', v);
41709     
41710     }
41711     
41712     
41713     this.colModel = new Roo.grid.ColumnModel( [
41714         {
41715             xtype: 'ColumnModel',
41716             xns: Roo.grid,
41717             dataIndex : 'weekday0',
41718             header : 'Sunday',
41719             renderer : cellRender
41720         },
41721         {
41722             xtype: 'ColumnModel',
41723             xns: Roo.grid,
41724             dataIndex : 'weekday1',
41725             header : 'Monday',
41726             renderer : cellRender
41727         },
41728         {
41729             xtype: 'ColumnModel',
41730             xns: Roo.grid,
41731             dataIndex : 'weekday2',
41732             header : 'Tuesday',
41733             renderer : cellRender
41734         },
41735         {
41736             xtype: 'ColumnModel',
41737             xns: Roo.grid,
41738             dataIndex : 'weekday3',
41739             header : 'Wednesday',
41740             renderer : cellRender
41741         },
41742         {
41743             xtype: 'ColumnModel',
41744             xns: Roo.grid,
41745             dataIndex : 'weekday4',
41746             header : 'Thursday',
41747             renderer : cellRender
41748         },
41749         {
41750             xtype: 'ColumnModel',
41751             xns: Roo.grid,
41752             dataIndex : 'weekday5',
41753             header : 'Friday',
41754             renderer : cellRender
41755         },
41756         {
41757             xtype: 'ColumnModel',
41758             xns: Roo.grid,
41759             dataIndex : 'weekday6',
41760             header : 'Saturday',
41761             renderer : cellRender
41762         }
41763     ]);
41764     this.cm = this.colModel;
41765     this.cm.xmodule = this.xmodule || false;
41766  
41767         
41768           
41769     //this.selModel = new Roo.grid.CellSelectionModel();
41770     //this.sm = this.selModel;
41771     //this.selModel.init(this);
41772     
41773     
41774     if(this.width){
41775         this.container.setWidth(this.width);
41776     }
41777
41778     if(this.height){
41779         this.container.setHeight(this.height);
41780     }
41781     /** @private */
41782         this.addEvents({
41783         // raw events
41784         /**
41785          * @event click
41786          * The raw click event for the entire grid.
41787          * @param {Roo.EventObject} e
41788          */
41789         "click" : true,
41790         /**
41791          * @event dblclick
41792          * The raw dblclick event for the entire grid.
41793          * @param {Roo.EventObject} e
41794          */
41795         "dblclick" : true,
41796         /**
41797          * @event contextmenu
41798          * The raw contextmenu event for the entire grid.
41799          * @param {Roo.EventObject} e
41800          */
41801         "contextmenu" : true,
41802         /**
41803          * @event mousedown
41804          * The raw mousedown event for the entire grid.
41805          * @param {Roo.EventObject} e
41806          */
41807         "mousedown" : true,
41808         /**
41809          * @event mouseup
41810          * The raw mouseup event for the entire grid.
41811          * @param {Roo.EventObject} e
41812          */
41813         "mouseup" : true,
41814         /**
41815          * @event mouseover
41816          * The raw mouseover event for the entire grid.
41817          * @param {Roo.EventObject} e
41818          */
41819         "mouseover" : true,
41820         /**
41821          * @event mouseout
41822          * The raw mouseout event for the entire grid.
41823          * @param {Roo.EventObject} e
41824          */
41825         "mouseout" : true,
41826         /**
41827          * @event keypress
41828          * The raw keypress event for the entire grid.
41829          * @param {Roo.EventObject} e
41830          */
41831         "keypress" : true,
41832         /**
41833          * @event keydown
41834          * The raw keydown event for the entire grid.
41835          * @param {Roo.EventObject} e
41836          */
41837         "keydown" : true,
41838
41839         // custom events
41840
41841         /**
41842          * @event cellclick
41843          * Fires when a cell is clicked
41844          * @param {Grid} this
41845          * @param {Number} rowIndex
41846          * @param {Number} columnIndex
41847          * @param {Roo.EventObject} e
41848          */
41849         "cellclick" : true,
41850         /**
41851          * @event celldblclick
41852          * Fires when a cell is double clicked
41853          * @param {Grid} this
41854          * @param {Number} rowIndex
41855          * @param {Number} columnIndex
41856          * @param {Roo.EventObject} e
41857          */
41858         "celldblclick" : true,
41859         /**
41860          * @event rowclick
41861          * Fires when a row is clicked
41862          * @param {Grid} this
41863          * @param {Number} rowIndex
41864          * @param {Roo.EventObject} e
41865          */
41866         "rowclick" : true,
41867         /**
41868          * @event rowdblclick
41869          * Fires when a row is double clicked
41870          * @param {Grid} this
41871          * @param {Number} rowIndex
41872          * @param {Roo.EventObject} e
41873          */
41874         "rowdblclick" : true,
41875         /**
41876          * @event headerclick
41877          * Fires when a header is clicked
41878          * @param {Grid} this
41879          * @param {Number} columnIndex
41880          * @param {Roo.EventObject} e
41881          */
41882         "headerclick" : true,
41883         /**
41884          * @event headerdblclick
41885          * Fires when a header cell is double clicked
41886          * @param {Grid} this
41887          * @param {Number} columnIndex
41888          * @param {Roo.EventObject} e
41889          */
41890         "headerdblclick" : true,
41891         /**
41892          * @event rowcontextmenu
41893          * Fires when a row is right clicked
41894          * @param {Grid} this
41895          * @param {Number} rowIndex
41896          * @param {Roo.EventObject} e
41897          */
41898         "rowcontextmenu" : true,
41899         /**
41900          * @event cellcontextmenu
41901          * Fires when a cell is right clicked
41902          * @param {Grid} this
41903          * @param {Number} rowIndex
41904          * @param {Number} cellIndex
41905          * @param {Roo.EventObject} e
41906          */
41907          "cellcontextmenu" : true,
41908         /**
41909          * @event headercontextmenu
41910          * Fires when a header is right clicked
41911          * @param {Grid} this
41912          * @param {Number} columnIndex
41913          * @param {Roo.EventObject} e
41914          */
41915         "headercontextmenu" : true,
41916         /**
41917          * @event bodyscroll
41918          * Fires when the body element is scrolled
41919          * @param {Number} scrollLeft
41920          * @param {Number} scrollTop
41921          */
41922         "bodyscroll" : true,
41923         /**
41924          * @event columnresize
41925          * Fires when the user resizes a column
41926          * @param {Number} columnIndex
41927          * @param {Number} newSize
41928          */
41929         "columnresize" : true,
41930         /**
41931          * @event columnmove
41932          * Fires when the user moves a column
41933          * @param {Number} oldIndex
41934          * @param {Number} newIndex
41935          */
41936         "columnmove" : true,
41937         /**
41938          * @event startdrag
41939          * Fires when row(s) start being dragged
41940          * @param {Grid} this
41941          * @param {Roo.GridDD} dd The drag drop object
41942          * @param {event} e The raw browser event
41943          */
41944         "startdrag" : true,
41945         /**
41946          * @event enddrag
41947          * Fires when a drag operation is complete
41948          * @param {Grid} this
41949          * @param {Roo.GridDD} dd The drag drop object
41950          * @param {event} e The raw browser event
41951          */
41952         "enddrag" : true,
41953         /**
41954          * @event dragdrop
41955          * Fires when dragged row(s) are dropped on a valid DD target
41956          * @param {Grid} this
41957          * @param {Roo.GridDD} dd The drag drop object
41958          * @param {String} targetId The target drag drop object
41959          * @param {event} e The raw browser event
41960          */
41961         "dragdrop" : true,
41962         /**
41963          * @event dragover
41964          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41965          * @param {Grid} this
41966          * @param {Roo.GridDD} dd The drag drop object
41967          * @param {String} targetId The target drag drop object
41968          * @param {event} e The raw browser event
41969          */
41970         "dragover" : true,
41971         /**
41972          * @event dragenter
41973          *  Fires when the dragged row(s) first cross another DD target while being dragged
41974          * @param {Grid} this
41975          * @param {Roo.GridDD} dd The drag drop object
41976          * @param {String} targetId The target drag drop object
41977          * @param {event} e The raw browser event
41978          */
41979         "dragenter" : true,
41980         /**
41981          * @event dragout
41982          * Fires when the dragged row(s) leave another DD target while being dragged
41983          * @param {Grid} this
41984          * @param {Roo.GridDD} dd The drag drop object
41985          * @param {String} targetId The target drag drop object
41986          * @param {event} e The raw browser event
41987          */
41988         "dragout" : true,
41989         /**
41990          * @event rowclass
41991          * Fires when a row is rendered, so you can change add a style to it.
41992          * @param {GridView} gridview   The grid view
41993          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41994          */
41995         'rowclass' : true,
41996
41997         /**
41998          * @event render
41999          * Fires when the grid is rendered
42000          * @param {Grid} grid
42001          */
42002         'render' : true,
42003             /**
42004              * @event select
42005              * Fires when a date is selected
42006              * @param {DatePicker} this
42007              * @param {Date} date The selected date
42008              */
42009         'select': true,
42010         /**
42011              * @event monthchange
42012              * Fires when the displayed month changes 
42013              * @param {DatePicker} this
42014              * @param {Date} date The selected month
42015              */
42016         'monthchange': true,
42017         /**
42018              * @event evententer
42019              * Fires when mouse over an event
42020              * @param {Calendar} this
42021              * @param {event} Event
42022              */
42023         'evententer': true,
42024         /**
42025              * @event eventleave
42026              * Fires when the mouse leaves an
42027              * @param {Calendar} this
42028              * @param {event}
42029              */
42030         'eventleave': true,
42031         /**
42032              * @event eventclick
42033              * Fires when the mouse click an
42034              * @param {Calendar} this
42035              * @param {event}
42036              */
42037         'eventclick': true,
42038         /**
42039              * @event eventrender
42040              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42041              * @param {Calendar} this
42042              * @param {data} data to be modified
42043              */
42044         'eventrender': true
42045         
42046     });
42047
42048     Roo.grid.Grid.superclass.constructor.call(this);
42049     this.on('render', function() {
42050         this.view.el.addClass('x-grid-cal'); 
42051         
42052         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42053
42054     },this);
42055     
42056     if (!Roo.grid.Calendar.style) {
42057         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42058             
42059             
42060             '.x-grid-cal .x-grid-col' :  {
42061                 height: 'auto !important',
42062                 'vertical-align': 'top'
42063             },
42064             '.x-grid-cal  .fc-event-hori' : {
42065                 height: '14px'
42066             }
42067              
42068             
42069         }, Roo.id());
42070     }
42071
42072     
42073     
42074 };
42075 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42076     /**
42077      * @cfg {Store} eventStore The store that loads events.
42078      */
42079     eventStore : 25,
42080
42081      
42082     activeDate : false,
42083     startDay : 0,
42084     autoWidth : true,
42085     monitorWindowResize : false,
42086
42087     
42088     resizeColumns : function() {
42089         var col = (this.view.el.getWidth() / 7) - 3;
42090         // loop through cols, and setWidth
42091         for(var i =0 ; i < 7 ; i++){
42092             this.cm.setColumnWidth(i, col);
42093         }
42094     },
42095      setDate :function(date) {
42096         
42097         Roo.log('setDate?');
42098         
42099         this.resizeColumns();
42100         var vd = this.activeDate;
42101         this.activeDate = date;
42102 //        if(vd && this.el){
42103 //            var t = date.getTime();
42104 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42105 //                Roo.log('using add remove');
42106 //                
42107 //                this.fireEvent('monthchange', this, date);
42108 //                
42109 //                this.cells.removeClass("fc-state-highlight");
42110 //                this.cells.each(function(c){
42111 //                   if(c.dateValue == t){
42112 //                       c.addClass("fc-state-highlight");
42113 //                       setTimeout(function(){
42114 //                            try{c.dom.firstChild.focus();}catch(e){}
42115 //                       }, 50);
42116 //                       return false;
42117 //                   }
42118 //                   return true;
42119 //                });
42120 //                return;
42121 //            }
42122 //        }
42123         
42124         var days = date.getDaysInMonth();
42125         
42126         var firstOfMonth = date.getFirstDateOfMonth();
42127         var startingPos = firstOfMonth.getDay()-this.startDay;
42128         
42129         if(startingPos < this.startDay){
42130             startingPos += 7;
42131         }
42132         
42133         var pm = date.add(Date.MONTH, -1);
42134         var prevStart = pm.getDaysInMonth()-startingPos;
42135 //        
42136         
42137         
42138         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42139         
42140         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42141         //this.cells.addClassOnOver('fc-state-hover');
42142         
42143         var cells = this.cells.elements;
42144         var textEls = this.textNodes;
42145         
42146         //Roo.each(cells, function(cell){
42147         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42148         //});
42149         
42150         days += startingPos;
42151
42152         // convert everything to numbers so it's fast
42153         var day = 86400000;
42154         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42155         //Roo.log(d);
42156         //Roo.log(pm);
42157         //Roo.log(prevStart);
42158         
42159         var today = new Date().clearTime().getTime();
42160         var sel = date.clearTime().getTime();
42161         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42162         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42163         var ddMatch = this.disabledDatesRE;
42164         var ddText = this.disabledDatesText;
42165         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42166         var ddaysText = this.disabledDaysText;
42167         var format = this.format;
42168         
42169         var setCellClass = function(cal, cell){
42170             
42171             //Roo.log('set Cell Class');
42172             cell.title = "";
42173             var t = d.getTime();
42174             
42175             //Roo.log(d);
42176             
42177             
42178             cell.dateValue = t;
42179             if(t == today){
42180                 cell.className += " fc-today";
42181                 cell.className += " fc-state-highlight";
42182                 cell.title = cal.todayText;
42183             }
42184             if(t == sel){
42185                 // disable highlight in other month..
42186                 cell.className += " fc-state-highlight";
42187                 
42188             }
42189             // disabling
42190             if(t < min) {
42191                 //cell.className = " fc-state-disabled";
42192                 cell.title = cal.minText;
42193                 return;
42194             }
42195             if(t > max) {
42196                 //cell.className = " fc-state-disabled";
42197                 cell.title = cal.maxText;
42198                 return;
42199             }
42200             if(ddays){
42201                 if(ddays.indexOf(d.getDay()) != -1){
42202                     // cell.title = ddaysText;
42203                    // cell.className = " fc-state-disabled";
42204                 }
42205             }
42206             if(ddMatch && format){
42207                 var fvalue = d.dateFormat(format);
42208                 if(ddMatch.test(fvalue)){
42209                     cell.title = ddText.replace("%0", fvalue);
42210                    cell.className = " fc-state-disabled";
42211                 }
42212             }
42213             
42214             if (!cell.initialClassName) {
42215                 cell.initialClassName = cell.dom.className;
42216             }
42217             
42218             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42219         };
42220
42221         var i = 0;
42222         
42223         for(; i < startingPos; i++) {
42224             cells[i].dayName =  (++prevStart);
42225             Roo.log(textEls[i]);
42226             d.setDate(d.getDate()+1);
42227             
42228             //cells[i].className = "fc-past fc-other-month";
42229             setCellClass(this, cells[i]);
42230         }
42231         
42232         var intDay = 0;
42233         
42234         for(; i < days; i++){
42235             intDay = i - startingPos + 1;
42236             cells[i].dayName =  (intDay);
42237             d.setDate(d.getDate()+1);
42238             
42239             cells[i].className = ''; // "x-date-active";
42240             setCellClass(this, cells[i]);
42241         }
42242         var extraDays = 0;
42243         
42244         for(; i < 42; i++) {
42245             //textEls[i].innerHTML = (++extraDays);
42246             
42247             d.setDate(d.getDate()+1);
42248             cells[i].dayName = (++extraDays);
42249             cells[i].className = "fc-future fc-other-month";
42250             setCellClass(this, cells[i]);
42251         }
42252         
42253         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42254         
42255         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42256         
42257         // this will cause all the cells to mis
42258         var rows= [];
42259         var i =0;
42260         for (var r = 0;r < 6;r++) {
42261             for (var c =0;c < 7;c++) {
42262                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42263             }    
42264         }
42265         
42266         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42267         for(i=0;i<cells.length;i++) {
42268             
42269             this.cells.elements[i].dayName = cells[i].dayName ;
42270             this.cells.elements[i].className = cells[i].className;
42271             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42272             this.cells.elements[i].title = cells[i].title ;
42273             this.cells.elements[i].dateValue = cells[i].dateValue ;
42274         }
42275         
42276         
42277         
42278         
42279         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42280         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42281         
42282         ////if(totalRows != 6){
42283             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42284            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42285        // }
42286         
42287         this.fireEvent('monthchange', this, date);
42288         
42289         
42290     },
42291  /**
42292      * Returns the grid's SelectionModel.
42293      * @return {SelectionModel}
42294      */
42295     getSelectionModel : function(){
42296         if(!this.selModel){
42297             this.selModel = new Roo.grid.CellSelectionModel();
42298         }
42299         return this.selModel;
42300     },
42301
42302     load: function() {
42303         this.eventStore.load()
42304         
42305         
42306         
42307     },
42308     
42309     findCell : function(dt) {
42310         dt = dt.clearTime().getTime();
42311         var ret = false;
42312         this.cells.each(function(c){
42313             //Roo.log("check " +c.dateValue + '?=' + dt);
42314             if(c.dateValue == dt){
42315                 ret = c;
42316                 return false;
42317             }
42318             return true;
42319         });
42320         
42321         return ret;
42322     },
42323     
42324     findCells : function(rec) {
42325         var s = rec.data.start_dt.clone().clearTime().getTime();
42326        // Roo.log(s);
42327         var e= rec.data.end_dt.clone().clearTime().getTime();
42328        // Roo.log(e);
42329         var ret = [];
42330         this.cells.each(function(c){
42331              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42332             
42333             if(c.dateValue > e){
42334                 return ;
42335             }
42336             if(c.dateValue < s){
42337                 return ;
42338             }
42339             ret.push(c);
42340         });
42341         
42342         return ret;    
42343     },
42344     
42345     findBestRow: function(cells)
42346     {
42347         var ret = 0;
42348         
42349         for (var i =0 ; i < cells.length;i++) {
42350             ret  = Math.max(cells[i].rows || 0,ret);
42351         }
42352         return ret;
42353         
42354     },
42355     
42356     
42357     addItem : function(rec)
42358     {
42359         // look for vertical location slot in
42360         var cells = this.findCells(rec);
42361         
42362         rec.row = this.findBestRow(cells);
42363         
42364         // work out the location.
42365         
42366         var crow = false;
42367         var rows = [];
42368         for(var i =0; i < cells.length; i++) {
42369             if (!crow) {
42370                 crow = {
42371                     start : cells[i],
42372                     end :  cells[i]
42373                 };
42374                 continue;
42375             }
42376             if (crow.start.getY() == cells[i].getY()) {
42377                 // on same row.
42378                 crow.end = cells[i];
42379                 continue;
42380             }
42381             // different row.
42382             rows.push(crow);
42383             crow = {
42384                 start: cells[i],
42385                 end : cells[i]
42386             };
42387             
42388         }
42389         
42390         rows.push(crow);
42391         rec.els = [];
42392         rec.rows = rows;
42393         rec.cells = cells;
42394         for (var i = 0; i < cells.length;i++) {
42395             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42396             
42397         }
42398         
42399         
42400     },
42401     
42402     clearEvents: function() {
42403         
42404         if (!this.eventStore.getCount()) {
42405             return;
42406         }
42407         // reset number of rows in cells.
42408         Roo.each(this.cells.elements, function(c){
42409             c.rows = 0;
42410         });
42411         
42412         this.eventStore.each(function(e) {
42413             this.clearEvent(e);
42414         },this);
42415         
42416     },
42417     
42418     clearEvent : function(ev)
42419     {
42420         if (ev.els) {
42421             Roo.each(ev.els, function(el) {
42422                 el.un('mouseenter' ,this.onEventEnter, this);
42423                 el.un('mouseleave' ,this.onEventLeave, this);
42424                 el.remove();
42425             },this);
42426             ev.els = [];
42427         }
42428     },
42429     
42430     
42431     renderEvent : function(ev,ctr) {
42432         if (!ctr) {
42433              ctr = this.view.el.select('.fc-event-container',true).first();
42434         }
42435         
42436          
42437         this.clearEvent(ev);
42438             //code
42439        
42440         
42441         
42442         ev.els = [];
42443         var cells = ev.cells;
42444         var rows = ev.rows;
42445         this.fireEvent('eventrender', this, ev);
42446         
42447         for(var i =0; i < rows.length; i++) {
42448             
42449             cls = '';
42450             if (i == 0) {
42451                 cls += ' fc-event-start';
42452             }
42453             if ((i+1) == rows.length) {
42454                 cls += ' fc-event-end';
42455             }
42456             
42457             //Roo.log(ev.data);
42458             // how many rows should it span..
42459             var cg = this.eventTmpl.append(ctr,Roo.apply({
42460                 fccls : cls
42461                 
42462             }, ev.data) , true);
42463             
42464             
42465             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42466             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42467             cg.on('click', this.onEventClick, this, ev);
42468             
42469             ev.els.push(cg);
42470             
42471             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42472             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42473             //Roo.log(cg);
42474              
42475             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42476             cg.setWidth(ebox.right - sbox.x -2);
42477         }
42478     },
42479     
42480     renderEvents: function()
42481     {   
42482         // first make sure there is enough space..
42483         
42484         if (!this.eventTmpl) {
42485             this.eventTmpl = new Roo.Template(
42486                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42487                     '<div class="fc-event-inner">' +
42488                         '<span class="fc-event-time">{time}</span>' +
42489                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42490                     '</div>' +
42491                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42492                 '</div>'
42493             );
42494                 
42495         }
42496                
42497         
42498         
42499         this.cells.each(function(c) {
42500             //Roo.log(c.select('.fc-day-content div',true).first());
42501             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42502         });
42503         
42504         var ctr = this.view.el.select('.fc-event-container',true).first();
42505         
42506         var cls;
42507         this.eventStore.each(function(ev){
42508             
42509             this.renderEvent(ev);
42510              
42511              
42512         }, this);
42513         this.view.layout();
42514         
42515     },
42516     
42517     onEventEnter: function (e, el,event,d) {
42518         this.fireEvent('evententer', this, el, event);
42519     },
42520     
42521     onEventLeave: function (e, el,event,d) {
42522         this.fireEvent('eventleave', this, el, event);
42523     },
42524     
42525     onEventClick: function (e, el,event,d) {
42526         this.fireEvent('eventclick', this, el, event);
42527     },
42528     
42529     onMonthChange: function () {
42530         this.store.load();
42531     },
42532     
42533     onLoad: function () {
42534         
42535         //Roo.log('calendar onload');
42536 //         
42537         if(this.eventStore.getCount() > 0){
42538             
42539            
42540             
42541             this.eventStore.each(function(d){
42542                 
42543                 
42544                 // FIXME..
42545                 var add =   d.data;
42546                 if (typeof(add.end_dt) == 'undefined')  {
42547                     Roo.log("Missing End time in calendar data: ");
42548                     Roo.log(d);
42549                     return;
42550                 }
42551                 if (typeof(add.start_dt) == 'undefined')  {
42552                     Roo.log("Missing Start time in calendar data: ");
42553                     Roo.log(d);
42554                     return;
42555                 }
42556                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42557                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42558                 add.id = add.id || d.id;
42559                 add.title = add.title || '??';
42560                 
42561                 this.addItem(d);
42562                 
42563              
42564             },this);
42565         }
42566         
42567         this.renderEvents();
42568     }
42569     
42570
42571 });
42572 /*
42573  grid : {
42574                 xtype: 'Grid',
42575                 xns: Roo.grid,
42576                 listeners : {
42577                     render : function ()
42578                     {
42579                         _this.grid = this;
42580                         
42581                         if (!this.view.el.hasClass('course-timesheet')) {
42582                             this.view.el.addClass('course-timesheet');
42583                         }
42584                         if (this.tsStyle) {
42585                             this.ds.load({});
42586                             return; 
42587                         }
42588                         Roo.log('width');
42589                         Roo.log(_this.grid.view.el.getWidth());
42590                         
42591                         
42592                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42593                             '.course-timesheet .x-grid-row' : {
42594                                 height: '80px'
42595                             },
42596                             '.x-grid-row td' : {
42597                                 'vertical-align' : 0
42598                             },
42599                             '.course-edit-link' : {
42600                                 'color' : 'blue',
42601                                 'text-overflow' : 'ellipsis',
42602                                 'overflow' : 'hidden',
42603                                 'white-space' : 'nowrap',
42604                                 'cursor' : 'pointer'
42605                             },
42606                             '.sub-link' : {
42607                                 'color' : 'green'
42608                             },
42609                             '.de-act-sup-link' : {
42610                                 'color' : 'purple',
42611                                 'text-decoration' : 'line-through'
42612                             },
42613                             '.de-act-link' : {
42614                                 'color' : 'red',
42615                                 'text-decoration' : 'line-through'
42616                             },
42617                             '.course-timesheet .course-highlight' : {
42618                                 'border-top-style': 'dashed !important',
42619                                 'border-bottom-bottom': 'dashed !important'
42620                             },
42621                             '.course-timesheet .course-item' : {
42622                                 'font-family'   : 'tahoma, arial, helvetica',
42623                                 'font-size'     : '11px',
42624                                 'overflow'      : 'hidden',
42625                                 'padding-left'  : '10px',
42626                                 'padding-right' : '10px',
42627                                 'padding-top' : '10px' 
42628                             }
42629                             
42630                         }, Roo.id());
42631                                 this.ds.load({});
42632                     }
42633                 },
42634                 autoWidth : true,
42635                 monitorWindowResize : false,
42636                 cellrenderer : function(v,x,r)
42637                 {
42638                     return v;
42639                 },
42640                 sm : {
42641                     xtype: 'CellSelectionModel',
42642                     xns: Roo.grid
42643                 },
42644                 dataSource : {
42645                     xtype: 'Store',
42646                     xns: Roo.data,
42647                     listeners : {
42648                         beforeload : function (_self, options)
42649                         {
42650                             options.params = options.params || {};
42651                             options.params._month = _this.monthField.getValue();
42652                             options.params.limit = 9999;
42653                             options.params['sort'] = 'when_dt';    
42654                             options.params['dir'] = 'ASC';    
42655                             this.proxy.loadResponse = this.loadResponse;
42656                             Roo.log("load?");
42657                             //this.addColumns();
42658                         },
42659                         load : function (_self, records, options)
42660                         {
42661                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42662                                 // if you click on the translation.. you can edit it...
42663                                 var el = Roo.get(this);
42664                                 var id = el.dom.getAttribute('data-id');
42665                                 var d = el.dom.getAttribute('data-date');
42666                                 var t = el.dom.getAttribute('data-time');
42667                                 //var id = this.child('span').dom.textContent;
42668                                 
42669                                 //Roo.log(this);
42670                                 Pman.Dialog.CourseCalendar.show({
42671                                     id : id,
42672                                     when_d : d,
42673                                     when_t : t,
42674                                     productitem_active : id ? 1 : 0
42675                                 }, function() {
42676                                     _this.grid.ds.load({});
42677                                 });
42678                            
42679                            });
42680                            
42681                            _this.panel.fireEvent('resize', [ '', '' ]);
42682                         }
42683                     },
42684                     loadResponse : function(o, success, response){
42685                             // this is overridden on before load..
42686                             
42687                             Roo.log("our code?");       
42688                             //Roo.log(success);
42689                             //Roo.log(response)
42690                             delete this.activeRequest;
42691                             if(!success){
42692                                 this.fireEvent("loadexception", this, o, response);
42693                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42694                                 return;
42695                             }
42696                             var result;
42697                             try {
42698                                 result = o.reader.read(response);
42699                             }catch(e){
42700                                 Roo.log("load exception?");
42701                                 this.fireEvent("loadexception", this, o, response, e);
42702                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42703                                 return;
42704                             }
42705                             Roo.log("ready...");        
42706                             // loop through result.records;
42707                             // and set this.tdate[date] = [] << array of records..
42708                             _this.tdata  = {};
42709                             Roo.each(result.records, function(r){
42710                                 //Roo.log(r.data);
42711                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42712                                     _this.tdata[r.data.when_dt.format('j')] = [];
42713                                 }
42714                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42715                             });
42716                             
42717                             //Roo.log(_this.tdata);
42718                             
42719                             result.records = [];
42720                             result.totalRecords = 6;
42721                     
42722                             // let's generate some duumy records for the rows.
42723                             //var st = _this.dateField.getValue();
42724                             
42725                             // work out monday..
42726                             //st = st.add(Date.DAY, -1 * st.format('w'));
42727                             
42728                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42729                             
42730                             var firstOfMonth = date.getFirstDayOfMonth();
42731                             var days = date.getDaysInMonth();
42732                             var d = 1;
42733                             var firstAdded = false;
42734                             for (var i = 0; i < result.totalRecords ; i++) {
42735                                 //var d= st.add(Date.DAY, i);
42736                                 var row = {};
42737                                 var added = 0;
42738                                 for(var w = 0 ; w < 7 ; w++){
42739                                     if(!firstAdded && firstOfMonth != w){
42740                                         continue;
42741                                     }
42742                                     if(d > days){
42743                                         continue;
42744                                     }
42745                                     firstAdded = true;
42746                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42747                                     row['weekday'+w] = String.format(
42748                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42749                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42750                                                     d,
42751                                                     date.format('Y-m-')+dd
42752                                                 );
42753                                     added++;
42754                                     if(typeof(_this.tdata[d]) != 'undefined'){
42755                                         Roo.each(_this.tdata[d], function(r){
42756                                             var is_sub = '';
42757                                             var deactive = '';
42758                                             var id = r.id;
42759                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42760                                             if(r.parent_id*1>0){
42761                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42762                                                 id = r.parent_id;
42763                                             }
42764                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42765                                                 deactive = 'de-act-link';
42766                                             }
42767                                             
42768                                             row['weekday'+w] += String.format(
42769                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42770                                                     id, //0
42771                                                     r.product_id_name, //1
42772                                                     r.when_dt.format('h:ia'), //2
42773                                                     is_sub, //3
42774                                                     deactive, //4
42775                                                     desc // 5
42776                                             );
42777                                         });
42778                                     }
42779                                     d++;
42780                                 }
42781                                 
42782                                 // only do this if something added..
42783                                 if(added > 0){ 
42784                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42785                                 }
42786                                 
42787                                 
42788                                 // push it twice. (second one with an hour..
42789                                 
42790                             }
42791                             //Roo.log(result);
42792                             this.fireEvent("load", this, o, o.request.arg);
42793                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42794                         },
42795                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42796                     proxy : {
42797                         xtype: 'HttpProxy',
42798                         xns: Roo.data,
42799                         method : 'GET',
42800                         url : baseURL + '/Roo/Shop_course.php'
42801                     },
42802                     reader : {
42803                         xtype: 'JsonReader',
42804                         xns: Roo.data,
42805                         id : 'id',
42806                         fields : [
42807                             {
42808                                 'name': 'id',
42809                                 'type': 'int'
42810                             },
42811                             {
42812                                 'name': 'when_dt',
42813                                 'type': 'string'
42814                             },
42815                             {
42816                                 'name': 'end_dt',
42817                                 'type': 'string'
42818                             },
42819                             {
42820                                 'name': 'parent_id',
42821                                 'type': 'int'
42822                             },
42823                             {
42824                                 'name': 'product_id',
42825                                 'type': 'int'
42826                             },
42827                             {
42828                                 'name': 'productitem_id',
42829                                 'type': 'int'
42830                             },
42831                             {
42832                                 'name': 'guid',
42833                                 'type': 'int'
42834                             }
42835                         ]
42836                     }
42837                 },
42838                 toolbar : {
42839                     xtype: 'Toolbar',
42840                     xns: Roo,
42841                     items : [
42842                         {
42843                             xtype: 'Button',
42844                             xns: Roo.Toolbar,
42845                             listeners : {
42846                                 click : function (_self, e)
42847                                 {
42848                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42849                                     sd.setMonth(sd.getMonth()-1);
42850                                     _this.monthField.setValue(sd.format('Y-m-d'));
42851                                     _this.grid.ds.load({});
42852                                 }
42853                             },
42854                             text : "Back"
42855                         },
42856                         {
42857                             xtype: 'Separator',
42858                             xns: Roo.Toolbar
42859                         },
42860                         {
42861                             xtype: 'MonthField',
42862                             xns: Roo.form,
42863                             listeners : {
42864                                 render : function (_self)
42865                                 {
42866                                     _this.monthField = _self;
42867                                    // _this.monthField.set  today
42868                                 },
42869                                 select : function (combo, date)
42870                                 {
42871                                     _this.grid.ds.load({});
42872                                 }
42873                             },
42874                             value : (function() { return new Date(); })()
42875                         },
42876                         {
42877                             xtype: 'Separator',
42878                             xns: Roo.Toolbar
42879                         },
42880                         {
42881                             xtype: 'TextItem',
42882                             xns: Roo.Toolbar,
42883                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42884                         },
42885                         {
42886                             xtype: 'Fill',
42887                             xns: Roo.Toolbar
42888                         },
42889                         {
42890                             xtype: 'Button',
42891                             xns: Roo.Toolbar,
42892                             listeners : {
42893                                 click : function (_self, e)
42894                                 {
42895                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42896                                     sd.setMonth(sd.getMonth()+1);
42897                                     _this.monthField.setValue(sd.format('Y-m-d'));
42898                                     _this.grid.ds.load({});
42899                                 }
42900                             },
42901                             text : "Next"
42902                         }
42903                     ]
42904                 },
42905                  
42906             }
42907         };
42908         
42909         *//*
42910  * Based on:
42911  * Ext JS Library 1.1.1
42912  * Copyright(c) 2006-2007, Ext JS, LLC.
42913  *
42914  * Originally Released Under LGPL - original licence link has changed is not relivant.
42915  *
42916  * Fork - LGPL
42917  * <script type="text/javascript">
42918  */
42919  
42920 /**
42921  * @class Roo.LoadMask
42922  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42923  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42924  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42925  * element's UpdateManager load indicator and will be destroyed after the initial load.
42926  * @constructor
42927  * Create a new LoadMask
42928  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42929  * @param {Object} config The config object
42930  */
42931 Roo.LoadMask = function(el, config){
42932     this.el = Roo.get(el);
42933     Roo.apply(this, config);
42934     if(this.store){
42935         this.store.on('beforeload', this.onBeforeLoad, this);
42936         this.store.on('load', this.onLoad, this);
42937         this.store.on('loadexception', this.onLoadException, this);
42938         this.removeMask = false;
42939     }else{
42940         var um = this.el.getUpdateManager();
42941         um.showLoadIndicator = false; // disable the default indicator
42942         um.on('beforeupdate', this.onBeforeLoad, this);
42943         um.on('update', this.onLoad, this);
42944         um.on('failure', this.onLoad, this);
42945         this.removeMask = true;
42946     }
42947 };
42948
42949 Roo.LoadMask.prototype = {
42950     /**
42951      * @cfg {Boolean} removeMask
42952      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42953      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42954      */
42955     removeMask : false,
42956     /**
42957      * @cfg {String} msg
42958      * The text to display in a centered loading message box (defaults to 'Loading...')
42959      */
42960     msg : 'Loading...',
42961     /**
42962      * @cfg {String} msgCls
42963      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42964      */
42965     msgCls : 'x-mask-loading',
42966
42967     /**
42968      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42969      * @type Boolean
42970      */
42971     disabled: false,
42972
42973     /**
42974      * Disables the mask to prevent it from being displayed
42975      */
42976     disable : function(){
42977        this.disabled = true;
42978     },
42979
42980     /**
42981      * Enables the mask so that it can be displayed
42982      */
42983     enable : function(){
42984         this.disabled = false;
42985     },
42986     
42987     onLoadException : function()
42988     {
42989         Roo.log(arguments);
42990         
42991         if (typeof(arguments[3]) != 'undefined') {
42992             Roo.MessageBox.alert("Error loading",arguments[3]);
42993         } 
42994         /*
42995         try {
42996             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42997                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42998             }   
42999         } catch(e) {
43000             
43001         }
43002         */
43003     
43004         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43005     },
43006     // private
43007     onLoad : function()
43008     {
43009         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43010     },
43011
43012     // private
43013     onBeforeLoad : function(){
43014         if(!this.disabled){
43015             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43016         }
43017     },
43018
43019     // private
43020     destroy : function(){
43021         if(this.store){
43022             this.store.un('beforeload', this.onBeforeLoad, this);
43023             this.store.un('load', this.onLoad, this);
43024             this.store.un('loadexception', this.onLoadException, this);
43025         }else{
43026             var um = this.el.getUpdateManager();
43027             um.un('beforeupdate', this.onBeforeLoad, this);
43028             um.un('update', this.onLoad, this);
43029             um.un('failure', this.onLoad, this);
43030         }
43031     }
43032 };/*
43033  * Based on:
43034  * Ext JS Library 1.1.1
43035  * Copyright(c) 2006-2007, Ext JS, LLC.
43036  *
43037  * Originally Released Under LGPL - original licence link has changed is not relivant.
43038  *
43039  * Fork - LGPL
43040  * <script type="text/javascript">
43041  */
43042
43043
43044 /**
43045  * @class Roo.XTemplate
43046  * @extends Roo.Template
43047  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43048 <pre><code>
43049 var t = new Roo.XTemplate(
43050         '&lt;select name="{name}"&gt;',
43051                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43052         '&lt;/select&gt;'
43053 );
43054  
43055 // then append, applying the master template values
43056  </code></pre>
43057  *
43058  * Supported features:
43059  *
43060  *  Tags:
43061
43062 <pre><code>
43063       {a_variable} - output encoded.
43064       {a_variable.format:("Y-m-d")} - call a method on the variable
43065       {a_variable:raw} - unencoded output
43066       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43067       {a_variable:this.method_on_template(...)} - call a method on the template object.
43068  
43069 </code></pre>
43070  *  The tpl tag:
43071 <pre><code>
43072         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43073         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43074         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43075         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43076   
43077         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43078         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43079 </code></pre>
43080  *      
43081  */
43082 Roo.XTemplate = function()
43083 {
43084     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43085     if (this.html) {
43086         this.compile();
43087     }
43088 };
43089
43090
43091 Roo.extend(Roo.XTemplate, Roo.Template, {
43092
43093     /**
43094      * The various sub templates
43095      */
43096     tpls : false,
43097     /**
43098      *
43099      * basic tag replacing syntax
43100      * WORD:WORD()
43101      *
43102      * // you can fake an object call by doing this
43103      *  x.t:(test,tesT) 
43104      * 
43105      */
43106     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43107
43108     /**
43109      * compile the template
43110      *
43111      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43112      *
43113      */
43114     compile: function()
43115     {
43116         var s = this.html;
43117      
43118         s = ['<tpl>', s, '</tpl>'].join('');
43119     
43120         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43121             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43122             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43123             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43124             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43125             m,
43126             id     = 0,
43127             tpls   = [];
43128     
43129         while(true == !!(m = s.match(re))){
43130             var forMatch   = m[0].match(nameRe),
43131                 ifMatch   = m[0].match(ifRe),
43132                 execMatch   = m[0].match(execRe),
43133                 namedMatch   = m[0].match(namedRe),
43134                 
43135                 exp  = null, 
43136                 fn   = null,
43137                 exec = null,
43138                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43139                 
43140             if (ifMatch) {
43141                 // if - puts fn into test..
43142                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43143                 if(exp){
43144                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43145                 }
43146             }
43147             
43148             if (execMatch) {
43149                 // exec - calls a function... returns empty if true is  returned.
43150                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43151                 if(exp){
43152                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43153                 }
43154             }
43155             
43156             
43157             if (name) {
43158                 // for = 
43159                 switch(name){
43160                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43161                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43162                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43163                 }
43164             }
43165             var uid = namedMatch ? namedMatch[1] : id;
43166             
43167             
43168             tpls.push({
43169                 id:     namedMatch ? namedMatch[1] : id,
43170                 target: name,
43171                 exec:   exec,
43172                 test:   fn,
43173                 body:   m[1] || ''
43174             });
43175             if (namedMatch) {
43176                 s = s.replace(m[0], '');
43177             } else { 
43178                 s = s.replace(m[0], '{xtpl'+ id + '}');
43179             }
43180             ++id;
43181         }
43182         this.tpls = [];
43183         for(var i = tpls.length-1; i >= 0; --i){
43184             this.compileTpl(tpls[i]);
43185             this.tpls[tpls[i].id] = tpls[i];
43186         }
43187         this.master = tpls[tpls.length-1];
43188         return this;
43189     },
43190     /**
43191      * same as applyTemplate, except it's done to one of the subTemplates
43192      * when using named templates, you can do:
43193      *
43194      * var str = pl.applySubTemplate('your-name', values);
43195      *
43196      * 
43197      * @param {Number} id of the template
43198      * @param {Object} values to apply to template
43199      * @param {Object} parent (normaly the instance of this object)
43200      */
43201     applySubTemplate : function(id, values, parent)
43202     {
43203         
43204         
43205         var t = this.tpls[id];
43206         
43207         
43208         try { 
43209             if(t.test && !t.test.call(this, values, parent)){
43210                 return '';
43211             }
43212         } catch(e) {
43213             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43214             Roo.log(e.toString());
43215             Roo.log(t.test);
43216             return ''
43217         }
43218         try { 
43219             
43220             if(t.exec && t.exec.call(this, values, parent)){
43221                 return '';
43222             }
43223         } catch(e) {
43224             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43225             Roo.log(e.toString());
43226             Roo.log(t.exec);
43227             return ''
43228         }
43229         try {
43230             var vs = t.target ? t.target.call(this, values, parent) : values;
43231             parent = t.target ? values : parent;
43232             if(t.target && vs instanceof Array){
43233                 var buf = [];
43234                 for(var i = 0, len = vs.length; i < len; i++){
43235                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43236                 }
43237                 return buf.join('');
43238             }
43239             return t.compiled.call(this, vs, parent);
43240         } catch (e) {
43241             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43242             Roo.log(e.toString());
43243             Roo.log(t.compiled);
43244             return '';
43245         }
43246     },
43247
43248     compileTpl : function(tpl)
43249     {
43250         var fm = Roo.util.Format;
43251         var useF = this.disableFormats !== true;
43252         var sep = Roo.isGecko ? "+" : ",";
43253         var undef = function(str) {
43254             Roo.log("Property not found :"  + str);
43255             return '';
43256         };
43257         
43258         var fn = function(m, name, format, args)
43259         {
43260             //Roo.log(arguments);
43261             args = args ? args.replace(/\\'/g,"'") : args;
43262             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43263             if (typeof(format) == 'undefined') {
43264                 format= 'htmlEncode';
43265             }
43266             if (format == 'raw' ) {
43267                 format = false;
43268             }
43269             
43270             if(name.substr(0, 4) == 'xtpl'){
43271                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43272             }
43273             
43274             // build an array of options to determine if value is undefined..
43275             
43276             // basically get 'xxxx.yyyy' then do
43277             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43278             //    (function () { Roo.log("Property not found"); return ''; })() :
43279             //    ......
43280             
43281             var udef_ar = [];
43282             var lookfor = '';
43283             Roo.each(name.split('.'), function(st) {
43284                 lookfor += (lookfor.length ? '.': '') + st;
43285                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43286             });
43287             
43288             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43289             
43290             
43291             if(format && useF){
43292                 
43293                 args = args ? ',' + args : "";
43294                  
43295                 if(format.substr(0, 5) != "this."){
43296                     format = "fm." + format + '(';
43297                 }else{
43298                     format = 'this.call("'+ format.substr(5) + '", ';
43299                     args = ", values";
43300                 }
43301                 
43302                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43303             }
43304              
43305             if (args.length) {
43306                 // called with xxyx.yuu:(test,test)
43307                 // change to ()
43308                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43309             }
43310             // raw.. - :raw modifier..
43311             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43312             
43313         };
43314         var body;
43315         // branched to use + in gecko and [].join() in others
43316         if(Roo.isGecko){
43317             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43318                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43319                     "';};};";
43320         }else{
43321             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43322             body.push(tpl.body.replace(/(\r\n|\n)/g,
43323                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43324             body.push("'].join('');};};");
43325             body = body.join('');
43326         }
43327         
43328         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43329        
43330         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43331         eval(body);
43332         
43333         return this;
43334     },
43335
43336     applyTemplate : function(values){
43337         return this.master.compiled.call(this, values, {});
43338         //var s = this.subs;
43339     },
43340
43341     apply : function(){
43342         return this.applyTemplate.apply(this, arguments);
43343     }
43344
43345  });
43346
43347 Roo.XTemplate.from = function(el){
43348     el = Roo.getDom(el);
43349     return new Roo.XTemplate(el.value || el.innerHTML);
43350 };