roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * @extends Roo.data.DataProxy
1398  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1399  * to the Reader when its load method is called.
1400  * @constructor
1401  * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1402  */
1403 Roo.data.MemoryProxy = function(data){
1404     if (data.data) {
1405         data = data.data;
1406     }
1407     Roo.data.MemoryProxy.superclass.constructor.call(this);
1408     this.data = data;
1409 };
1410
1411 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1412     
1413     /**
1414      * Load data from the requested source (in this case an in-memory
1415      * data object passed to the constructor), read the data object into
1416      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1417      * process that block using the passed callback.
1418      * @param {Object} params This parameter is not used by the MemoryProxy class.
1419      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1420      * object into a block of Roo.data.Records.
1421      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1422      * The function must be passed <ul>
1423      * <li>The Record block object</li>
1424      * <li>The "arg" argument from the load function</li>
1425      * <li>A boolean success indicator</li>
1426      * </ul>
1427      * @param {Object} scope The scope in which to call the callback
1428      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1429      */
1430     load : function(params, reader, callback, scope, arg){
1431         params = params || {};
1432         var result;
1433         try {
1434             result = reader.readRecords(params.data ? params.data :this.data);
1435         }catch(e){
1436             this.fireEvent("loadexception", this, arg, null, e);
1437             callback.call(scope, null, arg, false);
1438             return;
1439         }
1440         callback.call(scope, result, arg, true);
1441     },
1442     
1443     // private
1444     update : function(params, records){
1445         
1446     }
1447 });/*
1448  * Based on:
1449  * Ext JS Library 1.1.1
1450  * Copyright(c) 2006-2007, Ext JS, LLC.
1451  *
1452  * Originally Released Under LGPL - original licence link has changed is not relivant.
1453  *
1454  * Fork - LGPL
1455  * <script type="text/javascript">
1456  */
1457 /**
1458  * @class Roo.data.HttpProxy
1459  * @extends Roo.data.DataProxy
1460  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1461  * configured to reference a certain URL.<br><br>
1462  * <p>
1463  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1464  * from which the running page was served.<br><br>
1465  * <p>
1466  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1467  * <p>
1468  * Be aware that to enable the browser to parse an XML document, the server must set
1469  * the Content-Type header in the HTTP response to "text/xml".
1470  * @constructor
1471  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1472  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1473  * will be used to make the request.
1474  */
1475 Roo.data.HttpProxy = function(conn){
1476     Roo.data.HttpProxy.superclass.constructor.call(this);
1477     // is conn a conn config or a real conn?
1478     this.conn = conn;
1479     this.useAjax = !conn || !conn.events;
1480   
1481 };
1482
1483 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1484     // thse are take from connection...
1485     
1486     /**
1487      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1488      */
1489     /**
1490      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1491      * extra parameters to each request made by this object. (defaults to undefined)
1492      */
1493     /**
1494      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1495      *  to each request made by this object. (defaults to undefined)
1496      */
1497     /**
1498      * @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)
1499      */
1500     /**
1501      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1502      */
1503      /**
1504      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1505      * @type Boolean
1506      */
1507   
1508
1509     /**
1510      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1511      * @type Boolean
1512      */
1513     /**
1514      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1515      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1516      * a finer-grained basis than the DataProxy events.
1517      */
1518     getConnection : function(){
1519         return this.useAjax ? Roo.Ajax : this.conn;
1520     },
1521
1522     /**
1523      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1524      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1525      * process that block using the passed callback.
1526      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1527      * for the request to the remote server.
1528      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1529      * object into a block of Roo.data.Records.
1530      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1531      * The function must be passed <ul>
1532      * <li>The Record block object</li>
1533      * <li>The "arg" argument from the load function</li>
1534      * <li>A boolean success indicator</li>
1535      * </ul>
1536      * @param {Object} scope The scope in which to call the callback
1537      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1538      */
1539     load : function(params, reader, callback, scope, arg){
1540         if(this.fireEvent("beforeload", this, params) !== false){
1541             var  o = {
1542                 params : params || {},
1543                 request: {
1544                     callback : callback,
1545                     scope : scope,
1546                     arg : arg
1547                 },
1548                 reader: reader,
1549                 callback : this.loadResponse,
1550                 scope: this
1551             };
1552             if(this.useAjax){
1553                 Roo.applyIf(o, this.conn);
1554                 if(this.activeRequest){
1555                     Roo.Ajax.abort(this.activeRequest);
1556                 }
1557                 this.activeRequest = Roo.Ajax.request(o);
1558             }else{
1559                 this.conn.request(o);
1560             }
1561         }else{
1562             callback.call(scope||this, null, arg, false);
1563         }
1564     },
1565
1566     // private
1567     loadResponse : function(o, success, response){
1568         delete this.activeRequest;
1569         if(!success){
1570             this.fireEvent("loadexception", this, o, response);
1571             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1572             return;
1573         }
1574         var result;
1575         try {
1576             result = o.reader.read(response);
1577         }catch(e){
1578             o.success = false;
1579             o.raw = { errorMsg : response.responseText };
1580             this.fireEvent("loadexception", this, o, response, e);
1581             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1582             return;
1583         }
1584         
1585         this.fireEvent("load", this, o, o.request.arg);
1586         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1587     },
1588
1589     // private
1590     update : function(dataSet){
1591
1592     },
1593
1594     // private
1595     updateResponse : function(dataSet){
1596
1597     }
1598 });/*
1599  * Based on:
1600  * Ext JS Library 1.1.1
1601  * Copyright(c) 2006-2007, Ext JS, LLC.
1602  *
1603  * Originally Released Under LGPL - original licence link has changed is not relivant.
1604  *
1605  * Fork - LGPL
1606  * <script type="text/javascript">
1607  */
1608
1609 /**
1610  * @class Roo.data.ScriptTagProxy
1611  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1612  * other than the originating domain of the running page.<br><br>
1613  * <p>
1614  * <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
1615  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1616  * <p>
1617  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1618  * source code that is used as the source inside a &lt;script> tag.<br><br>
1619  * <p>
1620  * In order for the browser to process the returned data, the server must wrap the data object
1621  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1622  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1623  * depending on whether the callback name was passed:
1624  * <p>
1625  * <pre><code>
1626 boolean scriptTag = false;
1627 String cb = request.getParameter("callback");
1628 if (cb != null) {
1629     scriptTag = true;
1630     response.setContentType("text/javascript");
1631 } else {
1632     response.setContentType("application/x-json");
1633 }
1634 Writer out = response.getWriter();
1635 if (scriptTag) {
1636     out.write(cb + "(");
1637 }
1638 out.print(dataBlock.toJsonString());
1639 if (scriptTag) {
1640     out.write(");");
1641 }
1642 </pre></code>
1643  *
1644  * @constructor
1645  * @param {Object} config A configuration object.
1646  */
1647 Roo.data.ScriptTagProxy = function(config){
1648     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1649     Roo.apply(this, config);
1650     this.head = document.getElementsByTagName("head")[0];
1651 };
1652
1653 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1654
1655 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1656     /**
1657      * @cfg {String} url The URL from which to request the data object.
1658      */
1659     /**
1660      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1661      */
1662     timeout : 30000,
1663     /**
1664      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1665      * the server the name of the callback function set up by the load call to process the returned data object.
1666      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1667      * javascript output which calls this named function passing the data object as its only parameter.
1668      */
1669     callbackParam : "callback",
1670     /**
1671      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1672      * name to the request.
1673      */
1674     nocache : true,
1675
1676     /**
1677      * Load data from the configured URL, read the data object into
1678      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1679      * process that block using the passed callback.
1680      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1681      * for the request to the remote server.
1682      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1683      * object into a block of Roo.data.Records.
1684      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1685      * The function must be passed <ul>
1686      * <li>The Record block object</li>
1687      * <li>The "arg" argument from the load function</li>
1688      * <li>A boolean success indicator</li>
1689      * </ul>
1690      * @param {Object} scope The scope in which to call the callback
1691      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1692      */
1693     load : function(params, reader, callback, scope, arg){
1694         if(this.fireEvent("beforeload", this, params) !== false){
1695
1696             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1697
1698             var url = this.url;
1699             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1700             if(this.nocache){
1701                 url += "&_dc=" + (new Date().getTime());
1702             }
1703             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1704             var trans = {
1705                 id : transId,
1706                 cb : "stcCallback"+transId,
1707                 scriptId : "stcScript"+transId,
1708                 params : params,
1709                 arg : arg,
1710                 url : url,
1711                 callback : callback,
1712                 scope : scope,
1713                 reader : reader
1714             };
1715             var conn = this;
1716
1717             window[trans.cb] = function(o){
1718                 conn.handleResponse(o, trans);
1719             };
1720
1721             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1722
1723             if(this.autoAbort !== false){
1724                 this.abort();
1725             }
1726
1727             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1728
1729             var script = document.createElement("script");
1730             script.setAttribute("src", url);
1731             script.setAttribute("type", "text/javascript");
1732             script.setAttribute("id", trans.scriptId);
1733             this.head.appendChild(script);
1734
1735             this.trans = trans;
1736         }else{
1737             callback.call(scope||this, null, arg, false);
1738         }
1739     },
1740
1741     // private
1742     isLoading : function(){
1743         return this.trans ? true : false;
1744     },
1745
1746     /**
1747      * Abort the current server request.
1748      */
1749     abort : function(){
1750         if(this.isLoading()){
1751             this.destroyTrans(this.trans);
1752         }
1753     },
1754
1755     // private
1756     destroyTrans : function(trans, isLoaded){
1757         this.head.removeChild(document.getElementById(trans.scriptId));
1758         clearTimeout(trans.timeoutId);
1759         if(isLoaded){
1760             window[trans.cb] = undefined;
1761             try{
1762                 delete window[trans.cb];
1763             }catch(e){}
1764         }else{
1765             // if hasn't been loaded, wait for load to remove it to prevent script error
1766             window[trans.cb] = function(){
1767                 window[trans.cb] = undefined;
1768                 try{
1769                     delete window[trans.cb];
1770                 }catch(e){}
1771             };
1772         }
1773     },
1774
1775     // private
1776     handleResponse : function(o, trans){
1777         this.trans = false;
1778         this.destroyTrans(trans, true);
1779         var result;
1780         try {
1781             result = trans.reader.readRecords(o);
1782         }catch(e){
1783             this.fireEvent("loadexception", this, o, trans.arg, e);
1784             trans.callback.call(trans.scope||window, null, trans.arg, false);
1785             return;
1786         }
1787         this.fireEvent("load", this, o, trans.arg);
1788         trans.callback.call(trans.scope||window, result, trans.arg, true);
1789     },
1790
1791     // private
1792     handleFailure : function(trans){
1793         this.trans = false;
1794         this.destroyTrans(trans, false);
1795         this.fireEvent("loadexception", this, null, trans.arg);
1796         trans.callback.call(trans.scope||window, null, trans.arg, false);
1797     }
1798 });/*
1799  * Based on:
1800  * Ext JS Library 1.1.1
1801  * Copyright(c) 2006-2007, Ext JS, LLC.
1802  *
1803  * Originally Released Under LGPL - original licence link has changed is not relivant.
1804  *
1805  * Fork - LGPL
1806  * <script type="text/javascript">
1807  */
1808
1809 /**
1810  * @class Roo.data.JsonReader
1811  * @extends Roo.data.DataReader
1812  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1813  * based on mappings in a provided Roo.data.Record constructor.
1814  * 
1815  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1816  * in the reply previously. 
1817  * 
1818  * <p>
1819  * Example code:
1820  * <pre><code>
1821 var RecordDef = Roo.data.Record.create([
1822     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1823     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1824 ]);
1825 var myReader = new Roo.data.JsonReader({
1826     totalProperty: "results",    // The property which contains the total dataset size (optional)
1827     root: "rows",                // The property which contains an Array of row objects
1828     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1829 }, RecordDef);
1830 </code></pre>
1831  * <p>
1832  * This would consume a JSON file like this:
1833  * <pre><code>
1834 { 'results': 2, 'rows': [
1835     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1836     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1837 }
1838 </code></pre>
1839  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1840  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1841  * paged from the remote server.
1842  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1843  * @cfg {String} root name of the property which contains the Array of row objects.
1844  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1845  * @cfg {Array} fields Array of field definition objects
1846  * @constructor
1847  * Create a new JsonReader
1848  * @param {Object} meta Metadata configuration options
1849  * @param {Object} recordType Either an Array of field definition objects,
1850  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1851  */
1852 Roo.data.JsonReader = function(meta, recordType){
1853     
1854     meta = meta || {};
1855     // set some defaults:
1856     Roo.applyIf(meta, {
1857         totalProperty: 'total',
1858         successProperty : 'success',
1859         root : 'data',
1860         id : 'id'
1861     });
1862     
1863     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1864 };
1865 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1866     
1867     readerType : 'Json',
1868     
1869     /**
1870      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1871      * Used by Store query builder to append _requestMeta to params.
1872      * 
1873      */
1874     metaFromRemote : false,
1875     /**
1876      * This method is only used by a DataProxy which has retrieved data from a remote server.
1877      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1878      * @return {Object} data A data block which is used by an Roo.data.Store object as
1879      * a cache of Roo.data.Records.
1880      */
1881     read : function(response){
1882         var json = response.responseText;
1883        
1884         var o = /* eval:var:o */ eval("("+json+")");
1885         if(!o) {
1886             throw {message: "JsonReader.read: Json object not found"};
1887         }
1888         
1889         if(o.metaData){
1890             
1891             delete this.ef;
1892             this.metaFromRemote = true;
1893             this.meta = o.metaData;
1894             this.recordType = Roo.data.Record.create(o.metaData.fields);
1895             this.onMetaChange(this.meta, this.recordType, o);
1896         }
1897         return this.readRecords(o);
1898     },
1899
1900     // private function a store will implement
1901     onMetaChange : function(meta, recordType, o){
1902
1903     },
1904
1905     /**
1906          * @ignore
1907          */
1908     simpleAccess: function(obj, subsc) {
1909         return obj[subsc];
1910     },
1911
1912         /**
1913          * @ignore
1914          */
1915     getJsonAccessor: function(){
1916         var re = /[\[\.]/;
1917         return function(expr) {
1918             try {
1919                 return(re.test(expr))
1920                     ? new Function("obj", "return obj." + expr)
1921                     : function(obj){
1922                         return obj[expr];
1923                     };
1924             } catch(e){}
1925             return Roo.emptyFn;
1926         };
1927     }(),
1928
1929     /**
1930      * Create a data block containing Roo.data.Records from an XML document.
1931      * @param {Object} o An object which contains an Array of row objects in the property specified
1932      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1933      * which contains the total size of the dataset.
1934      * @return {Object} data A data block which is used by an Roo.data.Store object as
1935      * a cache of Roo.data.Records.
1936      */
1937     readRecords : function(o){
1938         /**
1939          * After any data loads, the raw JSON data is available for further custom processing.
1940          * @type Object
1941          */
1942         this.o = o;
1943         var s = this.meta, Record = this.recordType,
1944             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1945
1946 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1947         if (!this.ef) {
1948             if(s.totalProperty) {
1949                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1950                 }
1951                 if(s.successProperty) {
1952                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1953                 }
1954                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1955                 if (s.id) {
1956                         var g = this.getJsonAccessor(s.id);
1957                         this.getId = function(rec) {
1958                                 var r = g(rec);  
1959                                 return (r === undefined || r === "") ? null : r;
1960                         };
1961                 } else {
1962                         this.getId = function(){return null;};
1963                 }
1964             this.ef = [];
1965             for(var jj = 0; jj < fl; jj++){
1966                 f = fi[jj];
1967                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1968                 this.ef[jj] = this.getJsonAccessor(map);
1969             }
1970         }
1971
1972         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1973         if(s.totalProperty){
1974             var vt = parseInt(this.getTotal(o), 10);
1975             if(!isNaN(vt)){
1976                 totalRecords = vt;
1977             }
1978         }
1979         if(s.successProperty){
1980             var vs = this.getSuccess(o);
1981             if(vs === false || vs === 'false'){
1982                 success = false;
1983             }
1984         }
1985         var records = [];
1986         for(var i = 0; i < c; i++){
1987             var n = root[i];
1988             var values = {};
1989             var id = this.getId(n);
1990             for(var j = 0; j < fl; j++){
1991                 f = fi[j];
1992                                 var v = this.ef[j](n);
1993                                 if (!f.convert) {
1994                                         Roo.log('missing convert for ' + f.name);
1995                                         Roo.log(f);
1996                                         continue;
1997                                 }
1998                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1999             }
2000                         if (!Record) {
2001                                 return {
2002                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2003                                         success : false,
2004                                         records : [],
2005                                         totalRecords : 0
2006                                 };
2007                         }
2008             var record = new Record(values, id);
2009             record.json = n;
2010             records[i] = record;
2011         }
2012         return {
2013             raw : o,
2014             success : success,
2015             records : records,
2016             totalRecords : totalRecords
2017         };
2018     },
2019     // used when loading children.. @see loadDataFromChildren
2020     toLoadData: function(rec)
2021     {
2022         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2023         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2024         return { data : data, total : data.length };
2025         
2026     }
2027 });/*
2028  * Based on:
2029  * Ext JS Library 1.1.1
2030  * Copyright(c) 2006-2007, Ext JS, LLC.
2031  *
2032  * Originally Released Under LGPL - original licence link has changed is not relivant.
2033  *
2034  * Fork - LGPL
2035  * <script type="text/javascript">
2036  */
2037
2038 /**
2039  * @class Roo.data.XmlReader
2040  * @extends Roo.data.DataReader
2041  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2042  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2043  * <p>
2044  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2045  * header in the HTTP response must be set to "text/xml".</em>
2046  * <p>
2047  * Example code:
2048  * <pre><code>
2049 var RecordDef = Roo.data.Record.create([
2050    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2051    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2052 ]);
2053 var myReader = new Roo.data.XmlReader({
2054    totalRecords: "results", // The element which contains the total dataset size (optional)
2055    record: "row",           // The repeated element which contains row information
2056    id: "id"                 // The element within the row that provides an ID for the record (optional)
2057 }, RecordDef);
2058 </code></pre>
2059  * <p>
2060  * This would consume an XML file like this:
2061  * <pre><code>
2062 &lt;?xml?>
2063 &lt;dataset>
2064  &lt;results>2&lt;/results>
2065  &lt;row>
2066    &lt;id>1&lt;/id>
2067    &lt;name>Bill&lt;/name>
2068    &lt;occupation>Gardener&lt;/occupation>
2069  &lt;/row>
2070  &lt;row>
2071    &lt;id>2&lt;/id>
2072    &lt;name>Ben&lt;/name>
2073    &lt;occupation>Horticulturalist&lt;/occupation>
2074  &lt;/row>
2075 &lt;/dataset>
2076 </code></pre>
2077  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2078  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2079  * paged from the remote server.
2080  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2081  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2082  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2083  * a record identifier value.
2084  * @constructor
2085  * Create a new XmlReader
2086  * @param {Object} meta Metadata configuration options
2087  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2088  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2089  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2090  */
2091 Roo.data.XmlReader = function(meta, recordType){
2092     meta = meta || {};
2093     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2094 };
2095 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2096     
2097     readerType : 'Xml',
2098     
2099     /**
2100      * This method is only used by a DataProxy which has retrieved data from a remote server.
2101          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2102          * to contain a method called 'responseXML' that returns an XML document object.
2103      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2104      * a cache of Roo.data.Records.
2105      */
2106     read : function(response){
2107         var doc = response.responseXML;
2108         if(!doc) {
2109             throw {message: "XmlReader.read: XML Document not available"};
2110         }
2111         return this.readRecords(doc);
2112     },
2113
2114     /**
2115      * Create a data block containing Roo.data.Records from an XML document.
2116          * @param {Object} doc A parsed XML document.
2117      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2118      * a cache of Roo.data.Records.
2119      */
2120     readRecords : function(doc){
2121         /**
2122          * After any data loads/reads, the raw XML Document is available for further custom processing.
2123          * @type XMLDocument
2124          */
2125         this.xmlData = doc;
2126         var root = doc.documentElement || doc;
2127         var q = Roo.DomQuery;
2128         var recordType = this.recordType, fields = recordType.prototype.fields;
2129         var sid = this.meta.id;
2130         var totalRecords = 0, success = true;
2131         if(this.meta.totalRecords){
2132             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2133         }
2134         
2135         if(this.meta.success){
2136             var sv = q.selectValue(this.meta.success, root, true);
2137             success = sv !== false && sv !== 'false';
2138         }
2139         var records = [];
2140         var ns = q.select(this.meta.record, root);
2141         for(var i = 0, len = ns.length; i < len; i++) {
2142                 var n = ns[i];
2143                 var values = {};
2144                 var id = sid ? q.selectValue(sid, n) : undefined;
2145                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2146                     var f = fields.items[j];
2147                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2148                     v = f.convert(v);
2149                     values[f.name] = v;
2150                 }
2151                 var record = new recordType(values, id);
2152                 record.node = n;
2153                 records[records.length] = record;
2154             }
2155
2156             return {
2157                 success : success,
2158                 records : records,
2159                 totalRecords : totalRecords || records.length
2160             };
2161     }
2162 });/*
2163  * Based on:
2164  * Ext JS Library 1.1.1
2165  * Copyright(c) 2006-2007, Ext JS, LLC.
2166  *
2167  * Originally Released Under LGPL - original licence link has changed is not relivant.
2168  *
2169  * Fork - LGPL
2170  * <script type="text/javascript">
2171  */
2172
2173 /**
2174  * @class Roo.data.ArrayReader
2175  * @extends Roo.data.DataReader
2176  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2177  * Each element of that Array represents a row of data fields. The
2178  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2179  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2180  * <p>
2181  * Example code:.
2182  * <pre><code>
2183 var RecordDef = Roo.data.Record.create([
2184     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2185     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2186 ]);
2187 var myReader = new Roo.data.ArrayReader({
2188     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2189 }, RecordDef);
2190 </code></pre>
2191  * <p>
2192  * This would consume an Array like this:
2193  * <pre><code>
2194 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2195   </code></pre>
2196  
2197  * @constructor
2198  * Create a new JsonReader
2199  * @param {Object} meta Metadata configuration options.
2200  * @param {Object|Array} recordType Either an Array of field definition objects
2201  * 
2202  * @cfg {Array} fields Array of field definition objects
2203  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2204  * as specified to {@link Roo.data.Record#create},
2205  * or an {@link Roo.data.Record} object
2206  *
2207  * 
2208  * created using {@link Roo.data.Record#create}.
2209  */
2210 Roo.data.ArrayReader = function(meta, recordType)
2211 {    
2212     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2213 };
2214
2215 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2216     
2217       /**
2218      * Create a data block containing Roo.data.Records from an XML document.
2219      * @param {Object} o An Array of row objects which represents the dataset.
2220      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2221      * a cache of Roo.data.Records.
2222      */
2223     readRecords : function(o)
2224     {
2225         var sid = this.meta ? this.meta.id : null;
2226         var recordType = this.recordType, fields = recordType.prototype.fields;
2227         var records = [];
2228         var root = o;
2229         for(var i = 0; i < root.length; i++){
2230             var n = root[i];
2231             var values = {};
2232             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2233             for(var j = 0, jlen = fields.length; j < jlen; j++){
2234                 var f = fields.items[j];
2235                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2236                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2237                 v = f.convert(v);
2238                 values[f.name] = v;
2239             }
2240             var record = new recordType(values, id);
2241             record.json = n;
2242             records[records.length] = record;
2243         }
2244         return {
2245             records : records,
2246             totalRecords : records.length
2247         };
2248     },
2249     // used when loading children.. @see loadDataFromChildren
2250     toLoadData: function(rec)
2251     {
2252         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2253         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2254         
2255     }
2256     
2257     
2258 });/*
2259  * Based on:
2260  * Ext JS Library 1.1.1
2261  * Copyright(c) 2006-2007, Ext JS, LLC.
2262  *
2263  * Originally Released Under LGPL - original licence link has changed is not relivant.
2264  *
2265  * Fork - LGPL
2266  * <script type="text/javascript">
2267  */
2268
2269
2270 /**
2271  * @class Roo.data.Tree
2272  * @extends Roo.util.Observable
2273  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2274  * in the tree have most standard DOM functionality.
2275  * @constructor
2276  * @param {Node} root (optional) The root node
2277  */
2278 Roo.data.Tree = function(root){
2279    this.nodeHash = {};
2280    /**
2281     * The root node for this tree
2282     * @type Node
2283     */
2284    this.root = null;
2285    if(root){
2286        this.setRootNode(root);
2287    }
2288    this.addEvents({
2289        /**
2290         * @event append
2291         * Fires when a new child node is appended to a node in this tree.
2292         * @param {Tree} tree The owner tree
2293         * @param {Node} parent The parent node
2294         * @param {Node} node The newly appended node
2295         * @param {Number} index The index of the newly appended node
2296         */
2297        "append" : true,
2298        /**
2299         * @event remove
2300         * Fires when a child node is removed from a node in this tree.
2301         * @param {Tree} tree The owner tree
2302         * @param {Node} parent The parent node
2303         * @param {Node} node The child node removed
2304         */
2305        "remove" : true,
2306        /**
2307         * @event move
2308         * Fires when a node is moved to a new location in the tree
2309         * @param {Tree} tree The owner tree
2310         * @param {Node} node The node moved
2311         * @param {Node} oldParent The old parent of this node
2312         * @param {Node} newParent The new parent of this node
2313         * @param {Number} index The index it was moved to
2314         */
2315        "move" : true,
2316        /**
2317         * @event insert
2318         * Fires when a new child node is inserted in a node in this tree.
2319         * @param {Tree} tree The owner tree
2320         * @param {Node} parent The parent node
2321         * @param {Node} node The child node inserted
2322         * @param {Node} refNode The child node the node was inserted before
2323         */
2324        "insert" : true,
2325        /**
2326         * @event beforeappend
2327         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be appended
2331         */
2332        "beforeappend" : true,
2333        /**
2334         * @event beforeremove
2335         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2336         * @param {Tree} tree The owner tree
2337         * @param {Node} parent The parent node
2338         * @param {Node} node The child node to be removed
2339         */
2340        "beforeremove" : true,
2341        /**
2342         * @event beforemove
2343         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2344         * @param {Tree} tree The owner tree
2345         * @param {Node} node The node being moved
2346         * @param {Node} oldParent The parent of the node
2347         * @param {Node} newParent The new parent the node is moving to
2348         * @param {Number} index The index it is being moved to
2349         */
2350        "beforemove" : true,
2351        /**
2352         * @event beforeinsert
2353         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2354         * @param {Tree} tree The owner tree
2355         * @param {Node} parent The parent node
2356         * @param {Node} node The child node to be inserted
2357         * @param {Node} refNode The child node the node is being inserted before
2358         */
2359        "beforeinsert" : true
2360    });
2361
2362     Roo.data.Tree.superclass.constructor.call(this);
2363 };
2364
2365 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2366     pathSeparator: "/",
2367
2368     proxyNodeEvent : function(){
2369         return this.fireEvent.apply(this, arguments);
2370     },
2371
2372     /**
2373      * Returns the root node for this tree.
2374      * @return {Node}
2375      */
2376     getRootNode : function(){
2377         return this.root;
2378     },
2379
2380     /**
2381      * Sets the root node for this tree.
2382      * @param {Node} node
2383      * @return {Node}
2384      */
2385     setRootNode : function(node){
2386         this.root = node;
2387         node.ownerTree = this;
2388         node.isRoot = true;
2389         this.registerNode(node);
2390         return node;
2391     },
2392
2393     /**
2394      * Gets a node in this tree by its id.
2395      * @param {String} id
2396      * @return {Node}
2397      */
2398     getNodeById : function(id){
2399         return this.nodeHash[id];
2400     },
2401
2402     registerNode : function(node){
2403         this.nodeHash[node.id] = node;
2404     },
2405
2406     unregisterNode : function(node){
2407         delete this.nodeHash[node.id];
2408     },
2409
2410     toString : function(){
2411         return "[Tree"+(this.id?" "+this.id:"")+"]";
2412     }
2413 });
2414
2415 /**
2416  * @class Roo.data.Node
2417  * @extends Roo.util.Observable
2418  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2419  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2420  * @constructor
2421  * @param {Object} attributes The attributes/config for the node
2422  */
2423 Roo.data.Node = function(attributes){
2424     /**
2425      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2426      * @type {Object}
2427      */
2428     this.attributes = attributes || {};
2429     this.leaf = this.attributes.leaf;
2430     /**
2431      * The node id. @type String
2432      */
2433     this.id = this.attributes.id;
2434     if(!this.id){
2435         this.id = Roo.id(null, "ynode-");
2436         this.attributes.id = this.id;
2437     }
2438      
2439     
2440     /**
2441      * All child nodes of this node. @type Array
2442      */
2443     this.childNodes = [];
2444     if(!this.childNodes.indexOf){ // indexOf is a must
2445         this.childNodes.indexOf = function(o){
2446             for(var i = 0, len = this.length; i < len; i++){
2447                 if(this[i] == o) {
2448                     return i;
2449                 }
2450             }
2451             return -1;
2452         };
2453     }
2454     /**
2455      * The parent node for this node. @type Node
2456      */
2457     this.parentNode = null;
2458     /**
2459      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2460      */
2461     this.firstChild = null;
2462     /**
2463      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2464      */
2465     this.lastChild = null;
2466     /**
2467      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2468      */
2469     this.previousSibling = null;
2470     /**
2471      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2472      */
2473     this.nextSibling = null;
2474
2475     this.addEvents({
2476        /**
2477         * @event append
2478         * Fires when a new child node is appended
2479         * @param {Tree} tree The owner tree
2480         * @param {Node} this This node
2481         * @param {Node} node The newly appended node
2482         * @param {Number} index The index of the newly appended node
2483         */
2484        "append" : true,
2485        /**
2486         * @event remove
2487         * Fires when a child node is removed
2488         * @param {Tree} tree The owner tree
2489         * @param {Node} this This node
2490         * @param {Node} node The removed node
2491         */
2492        "remove" : true,
2493        /**
2494         * @event move
2495         * Fires when this node is moved to a new location in the tree
2496         * @param {Tree} tree The owner tree
2497         * @param {Node} this This node
2498         * @param {Node} oldParent The old parent of this node
2499         * @param {Node} newParent The new parent of this node
2500         * @param {Number} index The index it was moved to
2501         */
2502        "move" : true,
2503        /**
2504         * @event insert
2505         * Fires when a new child node is inserted.
2506         * @param {Tree} tree The owner tree
2507         * @param {Node} this This node
2508         * @param {Node} node The child node inserted
2509         * @param {Node} refNode The child node the node was inserted before
2510         */
2511        "insert" : true,
2512        /**
2513         * @event beforeappend
2514         * Fires before a new child is appended, return false to cancel the append.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be appended
2518         */
2519        "beforeappend" : true,
2520        /**
2521         * @event beforeremove
2522         * Fires before a child is removed, return false to cancel the remove.
2523         * @param {Tree} tree The owner tree
2524         * @param {Node} this This node
2525         * @param {Node} node The child node to be removed
2526         */
2527        "beforeremove" : true,
2528        /**
2529         * @event beforemove
2530         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2531         * @param {Tree} tree The owner tree
2532         * @param {Node} this This node
2533         * @param {Node} oldParent The parent of this node
2534         * @param {Node} newParent The new parent this node is moving to
2535         * @param {Number} index The index it is being moved to
2536         */
2537        "beforemove" : true,
2538        /**
2539         * @event beforeinsert
2540         * Fires before a new child is inserted, return false to cancel the insert.
2541         * @param {Tree} tree The owner tree
2542         * @param {Node} this This node
2543         * @param {Node} node The child node to be inserted
2544         * @param {Node} refNode The child node the node is being inserted before
2545         */
2546        "beforeinsert" : true
2547    });
2548     this.listeners = this.attributes.listeners;
2549     Roo.data.Node.superclass.constructor.call(this);
2550 };
2551
2552 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2553     fireEvent : function(evtName){
2554         // first do standard event for this node
2555         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2556             return false;
2557         }
2558         // then bubble it up to the tree if the event wasn't cancelled
2559         var ot = this.getOwnerTree();
2560         if(ot){
2561             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2562                 return false;
2563             }
2564         }
2565         return true;
2566     },
2567
2568     /**
2569      * Returns true if this node is a leaf
2570      * @return {Boolean}
2571      */
2572     isLeaf : function(){
2573         return this.leaf === true;
2574     },
2575
2576     // private
2577     setFirstChild : function(node){
2578         this.firstChild = node;
2579     },
2580
2581     //private
2582     setLastChild : function(node){
2583         this.lastChild = node;
2584     },
2585
2586
2587     /**
2588      * Returns true if this node is the last child of its parent
2589      * @return {Boolean}
2590      */
2591     isLast : function(){
2592        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2593     },
2594
2595     /**
2596      * Returns true if this node is the first child of its parent
2597      * @return {Boolean}
2598      */
2599     isFirst : function(){
2600        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2601     },
2602
2603     hasChildNodes : function(){
2604         return !this.isLeaf() && this.childNodes.length > 0;
2605     },
2606
2607     /**
2608      * Insert node(s) as the last child node of this node.
2609      * @param {Node/Array} node The node or Array of nodes to append
2610      * @return {Node} The appended node if single append, or null if an array was passed
2611      */
2612     appendChild : function(node){
2613         var multi = false;
2614         if(node instanceof Array){
2615             multi = node;
2616         }else if(arguments.length > 1){
2617             multi = arguments;
2618         }
2619         
2620         // if passed an array or multiple args do them one by one
2621         if(multi){
2622             for(var i = 0, len = multi.length; i < len; i++) {
2623                 this.appendChild(multi[i]);
2624             }
2625         }else{
2626             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2627                 return false;
2628             }
2629             var index = this.childNodes.length;
2630             var oldParent = node.parentNode;
2631             // it's a move, make sure we move it cleanly
2632             if(oldParent){
2633                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2634                     return false;
2635                 }
2636                 oldParent.removeChild(node);
2637             }
2638             
2639             index = this.childNodes.length;
2640             if(index == 0){
2641                 this.setFirstChild(node);
2642             }
2643             this.childNodes.push(node);
2644             node.parentNode = this;
2645             var ps = this.childNodes[index-1];
2646             if(ps){
2647                 node.previousSibling = ps;
2648                 ps.nextSibling = node;
2649             }else{
2650                 node.previousSibling = null;
2651             }
2652             node.nextSibling = null;
2653             this.setLastChild(node);
2654             node.setOwnerTree(this.getOwnerTree());
2655             this.fireEvent("append", this.ownerTree, this, node, index);
2656             if(this.ownerTree) {
2657                 this.ownerTree.fireEvent("appendnode", this, node, index);
2658             }
2659             if(oldParent){
2660                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2661             }
2662             return node;
2663         }
2664     },
2665
2666     /**
2667      * Removes a child node from this node.
2668      * @param {Node} node The node to remove
2669      * @return {Node} The removed node
2670      */
2671     removeChild : function(node){
2672         var index = this.childNodes.indexOf(node);
2673         if(index == -1){
2674             return false;
2675         }
2676         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2677             return false;
2678         }
2679
2680         // remove it from childNodes collection
2681         this.childNodes.splice(index, 1);
2682
2683         // update siblings
2684         if(node.previousSibling){
2685             node.previousSibling.nextSibling = node.nextSibling;
2686         }
2687         if(node.nextSibling){
2688             node.nextSibling.previousSibling = node.previousSibling;
2689         }
2690
2691         // update child refs
2692         if(this.firstChild == node){
2693             this.setFirstChild(node.nextSibling);
2694         }
2695         if(this.lastChild == node){
2696             this.setLastChild(node.previousSibling);
2697         }
2698
2699         node.setOwnerTree(null);
2700         // clear any references from the node
2701         node.parentNode = null;
2702         node.previousSibling = null;
2703         node.nextSibling = null;
2704         this.fireEvent("remove", this.ownerTree, this, node);
2705         return node;
2706     },
2707
2708     /**
2709      * Inserts the first node before the second node in this nodes childNodes collection.
2710      * @param {Node} node The node to insert
2711      * @param {Node} refNode The node to insert before (if null the node is appended)
2712      * @return {Node} The inserted node
2713      */
2714     insertBefore : function(node, refNode){
2715         if(!refNode){ // like standard Dom, refNode can be null for append
2716             return this.appendChild(node);
2717         }
2718         // nothing to do
2719         if(node == refNode){
2720             return false;
2721         }
2722
2723         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2724             return false;
2725         }
2726         var index = this.childNodes.indexOf(refNode);
2727         var oldParent = node.parentNode;
2728         var refIndex = index;
2729
2730         // when moving internally, indexes will change after remove
2731         if(oldParent == this && this.childNodes.indexOf(node) < index){
2732             refIndex--;
2733         }
2734
2735         // it's a move, make sure we move it cleanly
2736         if(oldParent){
2737             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2738                 return false;
2739             }
2740             oldParent.removeChild(node);
2741         }
2742         if(refIndex == 0){
2743             this.setFirstChild(node);
2744         }
2745         this.childNodes.splice(refIndex, 0, node);
2746         node.parentNode = this;
2747         var ps = this.childNodes[refIndex-1];
2748         if(ps){
2749             node.previousSibling = ps;
2750             ps.nextSibling = node;
2751         }else{
2752             node.previousSibling = null;
2753         }
2754         node.nextSibling = refNode;
2755         refNode.previousSibling = node;
2756         node.setOwnerTree(this.getOwnerTree());
2757         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2758         if(oldParent){
2759             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2760         }
2761         return node;
2762     },
2763
2764     /**
2765      * Returns the child node at the specified index.
2766      * @param {Number} index
2767      * @return {Node}
2768      */
2769     item : function(index){
2770         return this.childNodes[index];
2771     },
2772
2773     /**
2774      * Replaces one child node in this node with another.
2775      * @param {Node} newChild The replacement node
2776      * @param {Node} oldChild The node to replace
2777      * @return {Node} The replaced node
2778      */
2779     replaceChild : function(newChild, oldChild){
2780         this.insertBefore(newChild, oldChild);
2781         this.removeChild(oldChild);
2782         return oldChild;
2783     },
2784
2785     /**
2786      * Returns the index of a child node
2787      * @param {Node} node
2788      * @return {Number} The index of the node or -1 if it was not found
2789      */
2790     indexOf : function(child){
2791         return this.childNodes.indexOf(child);
2792     },
2793
2794     /**
2795      * Returns the tree this node is in.
2796      * @return {Tree}
2797      */
2798     getOwnerTree : function(){
2799         // if it doesn't have one, look for one
2800         if(!this.ownerTree){
2801             var p = this;
2802             while(p){
2803                 if(p.ownerTree){
2804                     this.ownerTree = p.ownerTree;
2805                     break;
2806                 }
2807                 p = p.parentNode;
2808             }
2809         }
2810         return this.ownerTree;
2811     },
2812
2813     /**
2814      * Returns depth of this node (the root node has a depth of 0)
2815      * @return {Number}
2816      */
2817     getDepth : function(){
2818         var depth = 0;
2819         var p = this;
2820         while(p.parentNode){
2821             ++depth;
2822             p = p.parentNode;
2823         }
2824         return depth;
2825     },
2826
2827     // private
2828     setOwnerTree : function(tree){
2829         // if it's move, we need to update everyone
2830         if(tree != this.ownerTree){
2831             if(this.ownerTree){
2832                 this.ownerTree.unregisterNode(this);
2833             }
2834             this.ownerTree = tree;
2835             var cs = this.childNodes;
2836             for(var i = 0, len = cs.length; i < len; i++) {
2837                 cs[i].setOwnerTree(tree);
2838             }
2839             if(tree){
2840                 tree.registerNode(this);
2841             }
2842         }
2843     },
2844
2845     /**
2846      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2847      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2848      * @return {String} The path
2849      */
2850     getPath : function(attr){
2851         attr = attr || "id";
2852         var p = this.parentNode;
2853         var b = [this.attributes[attr]];
2854         while(p){
2855             b.unshift(p.attributes[attr]);
2856             p = p.parentNode;
2857         }
2858         var sep = this.getOwnerTree().pathSeparator;
2859         return sep + b.join(sep);
2860     },
2861
2862     /**
2863      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2864      * function call will be the scope provided or the current node. The arguments to the function
2865      * will be the args provided or the current node. If the function returns false at any point,
2866      * the bubble is stopped.
2867      * @param {Function} fn The function to call
2868      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2869      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2870      */
2871     bubble : function(fn, scope, args){
2872         var p = this;
2873         while(p){
2874             if(fn.call(scope || p, args || p) === false){
2875                 break;
2876             }
2877             p = p.parentNode;
2878         }
2879     },
2880
2881     /**
2882      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2883      * function call will be the scope provided or the current node. The arguments to the function
2884      * will be the args provided or the current node. If the function returns false at any point,
2885      * the cascade is stopped on that branch.
2886      * @param {Function} fn The function to call
2887      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2888      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2889      */
2890     cascade : function(fn, scope, args){
2891         if(fn.call(scope || this, args || this) !== false){
2892             var cs = this.childNodes;
2893             for(var i = 0, len = cs.length; i < len; i++) {
2894                 cs[i].cascade(fn, scope, args);
2895             }
2896         }
2897     },
2898
2899     /**
2900      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2901      * function call will be the scope provided or the current node. The arguments to the function
2902      * will be the args provided or the current node. If the function returns false at any point,
2903      * the iteration stops.
2904      * @param {Function} fn The function to call
2905      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2906      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2907      */
2908     eachChild : function(fn, scope, args){
2909         var cs = this.childNodes;
2910         for(var i = 0, len = cs.length; i < len; i++) {
2911                 if(fn.call(scope || this, args || cs[i]) === false){
2912                     break;
2913                 }
2914         }
2915     },
2916
2917     /**
2918      * Finds the first child that has the attribute with the specified value.
2919      * @param {String} attribute The attribute name
2920      * @param {Mixed} value The value to search for
2921      * @return {Node} The found child or null if none was found
2922      */
2923     findChild : function(attribute, value){
2924         var cs = this.childNodes;
2925         for(var i = 0, len = cs.length; i < len; i++) {
2926                 if(cs[i].attributes[attribute] == value){
2927                     return cs[i];
2928                 }
2929         }
2930         return null;
2931     },
2932
2933     /**
2934      * Finds the first child by a custom function. The child matches if the function passed
2935      * returns true.
2936      * @param {Function} fn
2937      * @param {Object} scope (optional)
2938      * @return {Node} The found child or null if none was found
2939      */
2940     findChildBy : function(fn, scope){
2941         var cs = this.childNodes;
2942         for(var i = 0, len = cs.length; i < len; i++) {
2943                 if(fn.call(scope||cs[i], cs[i]) === true){
2944                     return cs[i];
2945                 }
2946         }
2947         return null;
2948     },
2949
2950     /**
2951      * Sorts this nodes children using the supplied sort function
2952      * @param {Function} fn
2953      * @param {Object} scope (optional)
2954      */
2955     sort : function(fn, scope){
2956         var cs = this.childNodes;
2957         var len = cs.length;
2958         if(len > 0){
2959             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2960             cs.sort(sortFn);
2961             for(var i = 0; i < len; i++){
2962                 var n = cs[i];
2963                 n.previousSibling = cs[i-1];
2964                 n.nextSibling = cs[i+1];
2965                 if(i == 0){
2966                     this.setFirstChild(n);
2967                 }
2968                 if(i == len-1){
2969                     this.setLastChild(n);
2970                 }
2971             }
2972         }
2973     },
2974
2975     /**
2976      * Returns true if this node is an ancestor (at any point) of the passed node.
2977      * @param {Node} node
2978      * @return {Boolean}
2979      */
2980     contains : function(node){
2981         return node.isAncestor(this);
2982     },
2983
2984     /**
2985      * Returns true if the passed node is an ancestor (at any point) of this node.
2986      * @param {Node} node
2987      * @return {Boolean}
2988      */
2989     isAncestor : function(node){
2990         var p = this.parentNode;
2991         while(p){
2992             if(p == node){
2993                 return true;
2994             }
2995             p = p.parentNode;
2996         }
2997         return false;
2998     },
2999
3000     toString : function(){
3001         return "[Node"+(this.id?" "+this.id:"")+"]";
3002     }
3003 });/*
3004  * Based on:
3005  * Ext JS Library 1.1.1
3006  * Copyright(c) 2006-2007, Ext JS, LLC.
3007  *
3008  * Originally Released Under LGPL - original licence link has changed is not relivant.
3009  *
3010  * Fork - LGPL
3011  * <script type="text/javascript">
3012  */
3013
3014
3015 /**
3016  * @class Roo.Shadow
3017  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3018  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3019  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3020  * @constructor
3021  * Create a new Shadow
3022  * @param {Object} config The config object
3023  */
3024 Roo.Shadow = function(config){
3025     Roo.apply(this, config);
3026     if(typeof this.mode != "string"){
3027         this.mode = this.defaultMode;
3028     }
3029     var o = this.offset, a = {h: 0};
3030     var rad = Math.floor(this.offset/2);
3031     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3032         case "drop":
3033             a.w = 0;
3034             a.l = a.t = o;
3035             a.t -= 1;
3036             if(Roo.isIE){
3037                 a.l -= this.offset + rad;
3038                 a.t -= this.offset + rad;
3039                 a.w -= rad;
3040                 a.h -= rad;
3041                 a.t += 1;
3042             }
3043         break;
3044         case "sides":
3045             a.w = (o*2);
3046             a.l = -o;
3047             a.t = o-1;
3048             if(Roo.isIE){
3049                 a.l -= (this.offset - rad);
3050                 a.t -= this.offset + rad;
3051                 a.l += 1;
3052                 a.w -= (this.offset - rad)*2;
3053                 a.w -= rad + 1;
3054                 a.h -= 1;
3055             }
3056         break;
3057         case "frame":
3058             a.w = a.h = (o*2);
3059             a.l = a.t = -o;
3060             a.t += 1;
3061             a.h -= 2;
3062             if(Roo.isIE){
3063                 a.l -= (this.offset - rad);
3064                 a.t -= (this.offset - rad);
3065                 a.l += 1;
3066                 a.w -= (this.offset + rad + 1);
3067                 a.h -= (this.offset + rad);
3068                 a.h += 1;
3069             }
3070         break;
3071     };
3072
3073     this.adjusts = a;
3074 };
3075
3076 Roo.Shadow.prototype = {
3077     /**
3078      * @cfg {String} mode
3079      * The shadow display mode.  Supports the following options:<br />
3080      * sides: Shadow displays on both sides and bottom only<br />
3081      * frame: Shadow displays equally on all four sides<br />
3082      * drop: Traditional bottom-right drop shadow (default)
3083      */
3084     mode: false,
3085     /**
3086      * @cfg {String} offset
3087      * The number of pixels to offset the shadow from the element (defaults to 4)
3088      */
3089     offset: 4,
3090
3091     // private
3092     defaultMode: "drop",
3093
3094     /**
3095      * Displays the shadow under the target element
3096      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3097      */
3098     show : function(target){
3099         target = Roo.get(target);
3100         if(!this.el){
3101             this.el = Roo.Shadow.Pool.pull();
3102             if(this.el.dom.nextSibling != target.dom){
3103                 this.el.insertBefore(target);
3104             }
3105         }
3106         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3107         if(Roo.isIE){
3108             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3109         }
3110         this.realign(
3111             target.getLeft(true),
3112             target.getTop(true),
3113             target.getWidth(),
3114             target.getHeight()
3115         );
3116         this.el.dom.style.display = "block";
3117     },
3118
3119     /**
3120      * Returns true if the shadow is visible, else false
3121      */
3122     isVisible : function(){
3123         return this.el ? true : false;  
3124     },
3125
3126     /**
3127      * Direct alignment when values are already available. Show must be called at least once before
3128      * calling this method to ensure it is initialized.
3129      * @param {Number} left The target element left position
3130      * @param {Number} top The target element top position
3131      * @param {Number} width The target element width
3132      * @param {Number} height The target element height
3133      */
3134     realign : function(l, t, w, h){
3135         if(!this.el){
3136             return;
3137         }
3138         var a = this.adjusts, d = this.el.dom, s = d.style;
3139         var iea = 0;
3140         s.left = (l+a.l)+"px";
3141         s.top = (t+a.t)+"px";
3142         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3143  
3144         if(s.width != sws || s.height != shs){
3145             s.width = sws;
3146             s.height = shs;
3147             if(!Roo.isIE){
3148                 var cn = d.childNodes;
3149                 var sww = Math.max(0, (sw-12))+"px";
3150                 cn[0].childNodes[1].style.width = sww;
3151                 cn[1].childNodes[1].style.width = sww;
3152                 cn[2].childNodes[1].style.width = sww;
3153                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3154             }
3155         }
3156     },
3157
3158     /**
3159      * Hides this shadow
3160      */
3161     hide : function(){
3162         if(this.el){
3163             this.el.dom.style.display = "none";
3164             Roo.Shadow.Pool.push(this.el);
3165             delete this.el;
3166         }
3167     },
3168
3169     /**
3170      * Adjust the z-index of this shadow
3171      * @param {Number} zindex The new z-index
3172      */
3173     setZIndex : function(z){
3174         this.zIndex = z;
3175         if(this.el){
3176             this.el.setStyle("z-index", z);
3177         }
3178     }
3179 };
3180
3181 // Private utility class that manages the internal Shadow cache
3182 Roo.Shadow.Pool = function(){
3183     var p = [];
3184     var markup = Roo.isIE ?
3185                  '<div class="x-ie-shadow"></div>' :
3186                  '<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>';
3187     return {
3188         pull : function(){
3189             var sh = p.shift();
3190             if(!sh){
3191                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3192                 sh.autoBoxAdjust = false;
3193             }
3194             return sh;
3195         },
3196
3197         push : function(sh){
3198             p.push(sh);
3199         }
3200     };
3201 }();/*
3202  * Based on:
3203  * Ext JS Library 1.1.1
3204  * Copyright(c) 2006-2007, Ext JS, LLC.
3205  *
3206  * Originally Released Under LGPL - original licence link has changed is not relivant.
3207  *
3208  * Fork - LGPL
3209  * <script type="text/javascript">
3210  */
3211
3212
3213 /**
3214  * @class Roo.SplitBar
3215  * @extends Roo.util.Observable
3216  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3217  * <br><br>
3218  * Usage:
3219  * <pre><code>
3220 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3221                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3222 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3223 split.minSize = 100;
3224 split.maxSize = 600;
3225 split.animate = true;
3226 split.on('moved', splitterMoved);
3227 </code></pre>
3228  * @constructor
3229  * Create a new SplitBar
3230  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3231  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3232  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3233  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3234                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3235                         position of the SplitBar).
3236  */
3237 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3238     
3239     /** @private */
3240     this.el = Roo.get(dragElement, true);
3241     this.el.dom.unselectable = "on";
3242     /** @private */
3243     this.resizingEl = Roo.get(resizingElement, true);
3244
3245     /**
3246      * @private
3247      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3248      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3249      * @type Number
3250      */
3251     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3252     
3253     /**
3254      * The minimum size of the resizing element. (Defaults to 0)
3255      * @type Number
3256      */
3257     this.minSize = 0;
3258     
3259     /**
3260      * The maximum size of the resizing element. (Defaults to 2000)
3261      * @type Number
3262      */
3263     this.maxSize = 2000;
3264     
3265     /**
3266      * Whether to animate the transition to the new size
3267      * @type Boolean
3268      */
3269     this.animate = false;
3270     
3271     /**
3272      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3273      * @type Boolean
3274      */
3275     this.useShim = false;
3276     
3277     /** @private */
3278     this.shim = null;
3279     
3280     if(!existingProxy){
3281         /** @private */
3282         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3283     }else{
3284         this.proxy = Roo.get(existingProxy).dom;
3285     }
3286     /** @private */
3287     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3288     
3289     /** @private */
3290     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3291     
3292     /** @private */
3293     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3294     
3295     /** @private */
3296     this.dragSpecs = {};
3297     
3298     /**
3299      * @private The adapter to use to positon and resize elements
3300      */
3301     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3302     this.adapter.init(this);
3303     
3304     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3305         /** @private */
3306         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3307         this.el.addClass("x-splitbar-h");
3308     }else{
3309         /** @private */
3310         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3311         this.el.addClass("x-splitbar-v");
3312     }
3313     
3314     this.addEvents({
3315         /**
3316          * @event resize
3317          * Fires when the splitter is moved (alias for {@link #event-moved})
3318          * @param {Roo.SplitBar} this
3319          * @param {Number} newSize the new width or height
3320          */
3321         "resize" : true,
3322         /**
3323          * @event moved
3324          * Fires when the splitter is moved
3325          * @param {Roo.SplitBar} this
3326          * @param {Number} newSize the new width or height
3327          */
3328         "moved" : true,
3329         /**
3330          * @event beforeresize
3331          * Fires before the splitter is dragged
3332          * @param {Roo.SplitBar} this
3333          */
3334         "beforeresize" : true,
3335
3336         "beforeapply" : true
3337     });
3338
3339     Roo.util.Observable.call(this);
3340 };
3341
3342 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3343     onStartProxyDrag : function(x, y){
3344         this.fireEvent("beforeresize", this);
3345         if(!this.overlay){
3346             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3347             o.unselectable();
3348             o.enableDisplayMode("block");
3349             // all splitbars share the same overlay
3350             Roo.SplitBar.prototype.overlay = o;
3351         }
3352         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3353         this.overlay.show();
3354         Roo.get(this.proxy).setDisplayed("block");
3355         var size = this.adapter.getElementSize(this);
3356         this.activeMinSize = this.getMinimumSize();;
3357         this.activeMaxSize = this.getMaximumSize();;
3358         var c1 = size - this.activeMinSize;
3359         var c2 = Math.max(this.activeMaxSize - size, 0);
3360         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3361             this.dd.resetConstraints();
3362             this.dd.setXConstraint(
3363                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3364                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3365             );
3366             this.dd.setYConstraint(0, 0);
3367         }else{
3368             this.dd.resetConstraints();
3369             this.dd.setXConstraint(0, 0);
3370             this.dd.setYConstraint(
3371                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3372                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3373             );
3374          }
3375         this.dragSpecs.startSize = size;
3376         this.dragSpecs.startPoint = [x, y];
3377         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3378     },
3379     
3380     /** 
3381      * @private Called after the drag operation by the DDProxy
3382      */
3383     onEndProxyDrag : function(e){
3384         Roo.get(this.proxy).setDisplayed(false);
3385         var endPoint = Roo.lib.Event.getXY(e);
3386         if(this.overlay){
3387             this.overlay.hide();
3388         }
3389         var newSize;
3390         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3391             newSize = this.dragSpecs.startSize + 
3392                 (this.placement == Roo.SplitBar.LEFT ?
3393                     endPoint[0] - this.dragSpecs.startPoint[0] :
3394                     this.dragSpecs.startPoint[0] - endPoint[0]
3395                 );
3396         }else{
3397             newSize = this.dragSpecs.startSize + 
3398                 (this.placement == Roo.SplitBar.TOP ?
3399                     endPoint[1] - this.dragSpecs.startPoint[1] :
3400                     this.dragSpecs.startPoint[1] - endPoint[1]
3401                 );
3402         }
3403         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3404         if(newSize != this.dragSpecs.startSize){
3405             if(this.fireEvent('beforeapply', this, newSize) !== false){
3406                 this.adapter.setElementSize(this, newSize);
3407                 this.fireEvent("moved", this, newSize);
3408                 this.fireEvent("resize", this, newSize);
3409             }
3410         }
3411     },
3412     
3413     /**
3414      * Get the adapter this SplitBar uses
3415      * @return The adapter object
3416      */
3417     getAdapter : function(){
3418         return this.adapter;
3419     },
3420     
3421     /**
3422      * Set the adapter this SplitBar uses
3423      * @param {Object} adapter A SplitBar adapter object
3424      */
3425     setAdapter : function(adapter){
3426         this.adapter = adapter;
3427         this.adapter.init(this);
3428     },
3429     
3430     /**
3431      * Gets the minimum size for the resizing element
3432      * @return {Number} The minimum size
3433      */
3434     getMinimumSize : function(){
3435         return this.minSize;
3436     },
3437     
3438     /**
3439      * Sets the minimum size for the resizing element
3440      * @param {Number} minSize The minimum size
3441      */
3442     setMinimumSize : function(minSize){
3443         this.minSize = minSize;
3444     },
3445     
3446     /**
3447      * Gets the maximum size for the resizing element
3448      * @return {Number} The maximum size
3449      */
3450     getMaximumSize : function(){
3451         return this.maxSize;
3452     },
3453     
3454     /**
3455      * Sets the maximum size for the resizing element
3456      * @param {Number} maxSize The maximum size
3457      */
3458     setMaximumSize : function(maxSize){
3459         this.maxSize = maxSize;
3460     },
3461     
3462     /**
3463      * Sets the initialize size for the resizing element
3464      * @param {Number} size The initial size
3465      */
3466     setCurrentSize : function(size){
3467         var oldAnimate = this.animate;
3468         this.animate = false;
3469         this.adapter.setElementSize(this, size);
3470         this.animate = oldAnimate;
3471     },
3472     
3473     /**
3474      * Destroy this splitbar. 
3475      * @param {Boolean} removeEl True to remove the element
3476      */
3477     destroy : function(removeEl){
3478         if(this.shim){
3479             this.shim.remove();
3480         }
3481         this.dd.unreg();
3482         this.proxy.parentNode.removeChild(this.proxy);
3483         if(removeEl){
3484             this.el.remove();
3485         }
3486     }
3487 });
3488
3489 /**
3490  * @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.
3491  */
3492 Roo.SplitBar.createProxy = function(dir){
3493     var proxy = new Roo.Element(document.createElement("div"));
3494     proxy.unselectable();
3495     var cls = 'x-splitbar-proxy';
3496     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3497     document.body.appendChild(proxy.dom);
3498     return proxy.dom;
3499 };
3500
3501 /** 
3502  * @class Roo.SplitBar.BasicLayoutAdapter
3503  * Default Adapter. It assumes the splitter and resizing element are not positioned
3504  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3505  */
3506 Roo.SplitBar.BasicLayoutAdapter = function(){
3507 };
3508
3509 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3510     // do nothing for now
3511     init : function(s){
3512     
3513     },
3514     /**
3515      * Called before drag operations to get the current size of the resizing element. 
3516      * @param {Roo.SplitBar} s The SplitBar using this adapter
3517      */
3518      getElementSize : function(s){
3519         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3520             return s.resizingEl.getWidth();
3521         }else{
3522             return s.resizingEl.getHeight();
3523         }
3524     },
3525     
3526     /**
3527      * Called after drag operations to set the size of the resizing element.
3528      * @param {Roo.SplitBar} s The SplitBar using this adapter
3529      * @param {Number} newSize The new size to set
3530      * @param {Function} onComplete A function to be invoked when resizing is complete
3531      */
3532     setElementSize : function(s, newSize, onComplete){
3533         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3534             if(!s.animate){
3535                 s.resizingEl.setWidth(newSize);
3536                 if(onComplete){
3537                     onComplete(s, newSize);
3538                 }
3539             }else{
3540                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3541             }
3542         }else{
3543             
3544             if(!s.animate){
3545                 s.resizingEl.setHeight(newSize);
3546                 if(onComplete){
3547                     onComplete(s, newSize);
3548                 }
3549             }else{
3550                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3551             }
3552         }
3553     }
3554 };
3555
3556 /** 
3557  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3558  * @extends Roo.SplitBar.BasicLayoutAdapter
3559  * Adapter that  moves the splitter element to align with the resized sizing element. 
3560  * Used with an absolute positioned SplitBar.
3561  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3562  * document.body, make sure you assign an id to the body element.
3563  */
3564 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3565     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3566     this.container = Roo.get(container);
3567 };
3568
3569 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3570     init : function(s){
3571         this.basic.init(s);
3572     },
3573     
3574     getElementSize : function(s){
3575         return this.basic.getElementSize(s);
3576     },
3577     
3578     setElementSize : function(s, newSize, onComplete){
3579         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3580     },
3581     
3582     moveSplitter : function(s){
3583         var yes = Roo.SplitBar;
3584         switch(s.placement){
3585             case yes.LEFT:
3586                 s.el.setX(s.resizingEl.getRight());
3587                 break;
3588             case yes.RIGHT:
3589                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3590                 break;
3591             case yes.TOP:
3592                 s.el.setY(s.resizingEl.getBottom());
3593                 break;
3594             case yes.BOTTOM:
3595                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3596                 break;
3597         }
3598     }
3599 };
3600
3601 /**
3602  * Orientation constant - Create a vertical SplitBar
3603  * @static
3604  * @type Number
3605  */
3606 Roo.SplitBar.VERTICAL = 1;
3607
3608 /**
3609  * Orientation constant - Create a horizontal SplitBar
3610  * @static
3611  * @type Number
3612  */
3613 Roo.SplitBar.HORIZONTAL = 2;
3614
3615 /**
3616  * Placement constant - The resizing element is to the left of the splitter element
3617  * @static
3618  * @type Number
3619  */
3620 Roo.SplitBar.LEFT = 1;
3621
3622 /**
3623  * Placement constant - The resizing element is to the right of the splitter element
3624  * @static
3625  * @type Number
3626  */
3627 Roo.SplitBar.RIGHT = 2;
3628
3629 /**
3630  * Placement constant - The resizing element is positioned above the splitter element
3631  * @static
3632  * @type Number
3633  */
3634 Roo.SplitBar.TOP = 3;
3635
3636 /**
3637  * Placement constant - The resizing element is positioned under splitter element
3638  * @static
3639  * @type Number
3640  */
3641 Roo.SplitBar.BOTTOM = 4;
3642 /*
3643  * Based on:
3644  * Ext JS Library 1.1.1
3645  * Copyright(c) 2006-2007, Ext JS, LLC.
3646  *
3647  * Originally Released Under LGPL - original licence link has changed is not relivant.
3648  *
3649  * Fork - LGPL
3650  * <script type="text/javascript">
3651  */
3652
3653 /**
3654  * @class Roo.View
3655  * @extends Roo.util.Observable
3656  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3657  * This class also supports single and multi selection modes. <br>
3658  * Create a data model bound view:
3659  <pre><code>
3660  var store = new Roo.data.Store(...);
3661
3662  var view = new Roo.View({
3663     el : "my-element",
3664     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3665  
3666     singleSelect: true,
3667     selectedClass: "ydataview-selected",
3668     store: store
3669  });
3670
3671  // listen for node click?
3672  view.on("click", function(vw, index, node, e){
3673  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3674  });
3675
3676  // load XML data
3677  dataModel.load("foobar.xml");
3678  </code></pre>
3679  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3680  * <br><br>
3681  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3682  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3683  * 
3684  * Note: old style constructor is still suported (container, template, config)
3685  * 
3686  * @constructor
3687  * Create a new View
3688  * @param {Object} config The config object
3689  * 
3690  */
3691 Roo.View = function(config, depreciated_tpl, depreciated_config){
3692     
3693     this.parent = false;
3694     
3695     if (typeof(depreciated_tpl) == 'undefined') {
3696         // new way.. - universal constructor.
3697         Roo.apply(this, config);
3698         this.el  = Roo.get(this.el);
3699     } else {
3700         // old format..
3701         this.el  = Roo.get(config);
3702         this.tpl = depreciated_tpl;
3703         Roo.apply(this, depreciated_config);
3704     }
3705     this.wrapEl  = this.el.wrap().wrap();
3706     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3707     
3708     
3709     if(typeof(this.tpl) == "string"){
3710         this.tpl = new Roo.Template(this.tpl);
3711     } else {
3712         // support xtype ctors..
3713         this.tpl = new Roo.factory(this.tpl, Roo);
3714     }
3715     
3716     
3717     this.tpl.compile();
3718     
3719     /** @private */
3720     this.addEvents({
3721         /**
3722          * @event beforeclick
3723          * Fires before a click is processed. Returns false to cancel the default action.
3724          * @param {Roo.View} this
3725          * @param {Number} index The index of the target node
3726          * @param {HTMLElement} node The target node
3727          * @param {Roo.EventObject} e The raw event object
3728          */
3729             "beforeclick" : true,
3730         /**
3731          * @event click
3732          * Fires when a template node is clicked.
3733          * @param {Roo.View} this
3734          * @param {Number} index The index of the target node
3735          * @param {HTMLElement} node The target node
3736          * @param {Roo.EventObject} e The raw event object
3737          */
3738             "click" : true,
3739         /**
3740          * @event dblclick
3741          * Fires when a template node is double clicked.
3742          * @param {Roo.View} this
3743          * @param {Number} index The index of the target node
3744          * @param {HTMLElement} node The target node
3745          * @param {Roo.EventObject} e The raw event object
3746          */
3747             "dblclick" : true,
3748         /**
3749          * @event contextmenu
3750          * Fires when a template node is right clicked.
3751          * @param {Roo.View} this
3752          * @param {Number} index The index of the target node
3753          * @param {HTMLElement} node The target node
3754          * @param {Roo.EventObject} e The raw event object
3755          */
3756             "contextmenu" : true,
3757         /**
3758          * @event selectionchange
3759          * Fires when the selected nodes change.
3760          * @param {Roo.View} this
3761          * @param {Array} selections Array of the selected nodes
3762          */
3763             "selectionchange" : true,
3764     
3765         /**
3766          * @event beforeselect
3767          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3768          * @param {Roo.View} this
3769          * @param {HTMLElement} node The node to be selected
3770          * @param {Array} selections Array of currently selected nodes
3771          */
3772             "beforeselect" : true,
3773         /**
3774          * @event preparedata
3775          * Fires on every row to render, to allow you to change the data.
3776          * @param {Roo.View} this
3777          * @param {Object} data to be rendered (change this)
3778          */
3779           "preparedata" : true
3780           
3781           
3782         });
3783
3784
3785
3786     this.el.on({
3787         "click": this.onClick,
3788         "dblclick": this.onDblClick,
3789         "contextmenu": this.onContextMenu,
3790         scope:this
3791     });
3792
3793     this.selections = [];
3794     this.nodes = [];
3795     this.cmp = new Roo.CompositeElementLite([]);
3796     if(this.store){
3797         this.store = Roo.factory(this.store, Roo.data);
3798         this.setStore(this.store, true);
3799     }
3800     
3801     if ( this.footer && this.footer.xtype) {
3802            
3803          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3804         
3805         this.footer.dataSource = this.store;
3806         this.footer.container = fctr;
3807         this.footer = Roo.factory(this.footer, Roo);
3808         fctr.insertFirst(this.el);
3809         
3810         // this is a bit insane - as the paging toolbar seems to detach the el..
3811 //        dom.parentNode.parentNode.parentNode
3812          // they get detached?
3813     }
3814     
3815     
3816     Roo.View.superclass.constructor.call(this);
3817     
3818     
3819 };
3820
3821 Roo.extend(Roo.View, Roo.util.Observable, {
3822     
3823      /**
3824      * @cfg {Roo.data.Store} store Data store to load data from.
3825      */
3826     store : false,
3827     
3828     /**
3829      * @cfg {String|Roo.Element} el The container element.
3830      */
3831     el : '',
3832     
3833     /**
3834      * @cfg {String|Roo.Template} tpl The template used by this View 
3835      */
3836     tpl : false,
3837     /**
3838      * @cfg {String} dataName the named area of the template to use as the data area
3839      *                          Works with domtemplates roo-name="name"
3840      */
3841     dataName: false,
3842     /**
3843      * @cfg {String} selectedClass The css class to add to selected nodes
3844      */
3845     selectedClass : "x-view-selected",
3846      /**
3847      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3848      */
3849     emptyText : "",
3850     
3851     /**
3852      * @cfg {String} text to display on mask (default Loading)
3853      */
3854     mask : false,
3855     /**
3856      * @cfg {Boolean} multiSelect Allow multiple selection
3857      */
3858     multiSelect : false,
3859     /**
3860      * @cfg {Boolean} singleSelect Allow single selection
3861      */
3862     singleSelect:  false,
3863     
3864     /**
3865      * @cfg {Boolean} toggleSelect - selecting 
3866      */
3867     toggleSelect : false,
3868     
3869     /**
3870      * @cfg {Boolean} tickable - selecting 
3871      */
3872     tickable : false,
3873     
3874     /**
3875      * Returns the element this view is bound to.
3876      * @return {Roo.Element}
3877      */
3878     getEl : function(){
3879         return this.wrapEl;
3880     },
3881     
3882     
3883
3884     /**
3885      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3886      */
3887     refresh : function(){
3888         //Roo.log('refresh');
3889         var t = this.tpl;
3890         
3891         // if we are using something like 'domtemplate', then
3892         // the what gets used is:
3893         // t.applySubtemplate(NAME, data, wrapping data..)
3894         // the outer template then get' applied with
3895         //     the store 'extra data'
3896         // and the body get's added to the
3897         //      roo-name="data" node?
3898         //      <span class='roo-tpl-{name}'></span> ?????
3899         
3900         
3901         
3902         this.clearSelections();
3903         this.el.update("");
3904         var html = [];
3905         var records = this.store.getRange();
3906         if(records.length < 1) {
3907             
3908             // is this valid??  = should it render a template??
3909             
3910             this.el.update(this.emptyText);
3911             return;
3912         }
3913         var el = this.el;
3914         if (this.dataName) {
3915             this.el.update(t.apply(this.store.meta)); //????
3916             el = this.el.child('.roo-tpl-' + this.dataName);
3917         }
3918         
3919         for(var i = 0, len = records.length; i < len; i++){
3920             var data = this.prepareData(records[i].data, i, records[i]);
3921             this.fireEvent("preparedata", this, data, i, records[i]);
3922             
3923             var d = Roo.apply({}, data);
3924             
3925             if(this.tickable){
3926                 Roo.apply(d, {'roo-id' : Roo.id()});
3927                 
3928                 var _this = this;
3929             
3930                 Roo.each(this.parent.item, function(item){
3931                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3932                         return;
3933                     }
3934                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3935                 });
3936             }
3937             
3938             html[html.length] = Roo.util.Format.trim(
3939                 this.dataName ?
3940                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3941                     t.apply(d)
3942             );
3943         }
3944         
3945         
3946         
3947         el.update(html.join(""));
3948         this.nodes = el.dom.childNodes;
3949         this.updateIndexes(0);
3950     },
3951     
3952
3953     /**
3954      * Function to override to reformat the data that is sent to
3955      * the template for each node.
3956      * DEPRICATED - use the preparedata event handler.
3957      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3958      * a JSON object for an UpdateManager bound view).
3959      */
3960     prepareData : function(data, index, record)
3961     {
3962         this.fireEvent("preparedata", this, data, index, record);
3963         return data;
3964     },
3965
3966     onUpdate : function(ds, record){
3967         // Roo.log('on update');   
3968         this.clearSelections();
3969         var index = this.store.indexOf(record);
3970         var n = this.nodes[index];
3971         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3972         n.parentNode.removeChild(n);
3973         this.updateIndexes(index, index);
3974     },
3975
3976     
3977     
3978 // --------- FIXME     
3979     onAdd : function(ds, records, index)
3980     {
3981         //Roo.log(['on Add', ds, records, index] );        
3982         this.clearSelections();
3983         if(this.nodes.length == 0){
3984             this.refresh();
3985             return;
3986         }
3987         var n = this.nodes[index];
3988         for(var i = 0, len = records.length; i < len; i++){
3989             var d = this.prepareData(records[i].data, i, records[i]);
3990             if(n){
3991                 this.tpl.insertBefore(n, d);
3992             }else{
3993                 
3994                 this.tpl.append(this.el, d);
3995             }
3996         }
3997         this.updateIndexes(index);
3998     },
3999
4000     onRemove : function(ds, record, index){
4001        // Roo.log('onRemove');
4002         this.clearSelections();
4003         var el = this.dataName  ?
4004             this.el.child('.roo-tpl-' + this.dataName) :
4005             this.el; 
4006         
4007         el.dom.removeChild(this.nodes[index]);
4008         this.updateIndexes(index);
4009     },
4010
4011     /**
4012      * Refresh an individual node.
4013      * @param {Number} index
4014      */
4015     refreshNode : function(index){
4016         this.onUpdate(this.store, this.store.getAt(index));
4017     },
4018
4019     updateIndexes : function(startIndex, endIndex){
4020         var ns = this.nodes;
4021         startIndex = startIndex || 0;
4022         endIndex = endIndex || ns.length - 1;
4023         for(var i = startIndex; i <= endIndex; i++){
4024             ns[i].nodeIndex = i;
4025         }
4026     },
4027
4028     /**
4029      * Changes the data store this view uses and refresh the view.
4030      * @param {Store} store
4031      */
4032     setStore : function(store, initial){
4033         if(!initial && this.store){
4034             this.store.un("datachanged", this.refresh);
4035             this.store.un("add", this.onAdd);
4036             this.store.un("remove", this.onRemove);
4037             this.store.un("update", this.onUpdate);
4038             this.store.un("clear", this.refresh);
4039             this.store.un("beforeload", this.onBeforeLoad);
4040             this.store.un("load", this.onLoad);
4041             this.store.un("loadexception", this.onLoad);
4042         }
4043         if(store){
4044           
4045             store.on("datachanged", this.refresh, this);
4046             store.on("add", this.onAdd, this);
4047             store.on("remove", this.onRemove, this);
4048             store.on("update", this.onUpdate, this);
4049             store.on("clear", this.refresh, this);
4050             store.on("beforeload", this.onBeforeLoad, this);
4051             store.on("load", this.onLoad, this);
4052             store.on("loadexception", this.onLoad, this);
4053         }
4054         
4055         if(store){
4056             this.refresh();
4057         }
4058     },
4059     /**
4060      * onbeforeLoad - masks the loading area.
4061      *
4062      */
4063     onBeforeLoad : function(store,opts)
4064     {
4065          //Roo.log('onBeforeLoad');   
4066         if (!opts.add) {
4067             this.el.update("");
4068         }
4069         this.el.mask(this.mask ? this.mask : "Loading" ); 
4070     },
4071     onLoad : function ()
4072     {
4073         this.el.unmask();
4074     },
4075     
4076
4077     /**
4078      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4079      * @param {HTMLElement} node
4080      * @return {HTMLElement} The template node
4081      */
4082     findItemFromChild : function(node){
4083         var el = this.dataName  ?
4084             this.el.child('.roo-tpl-' + this.dataName,true) :
4085             this.el.dom; 
4086         
4087         if(!node || node.parentNode == el){
4088                     return node;
4089             }
4090             var p = node.parentNode;
4091             while(p && p != el){
4092             if(p.parentNode == el){
4093                 return p;
4094             }
4095             p = p.parentNode;
4096         }
4097             return null;
4098     },
4099
4100     /** @ignore */
4101     onClick : function(e){
4102         var item = this.findItemFromChild(e.getTarget());
4103         if(item){
4104             var index = this.indexOf(item);
4105             if(this.onItemClick(item, index, e) !== false){
4106                 this.fireEvent("click", this, index, item, e);
4107             }
4108         }else{
4109             this.clearSelections();
4110         }
4111     },
4112
4113     /** @ignore */
4114     onContextMenu : function(e){
4115         var item = this.findItemFromChild(e.getTarget());
4116         if(item){
4117             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4118         }
4119     },
4120
4121     /** @ignore */
4122     onDblClick : function(e){
4123         var item = this.findItemFromChild(e.getTarget());
4124         if(item){
4125             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4126         }
4127     },
4128
4129     onItemClick : function(item, index, e)
4130     {
4131         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4132             return false;
4133         }
4134         if (this.toggleSelect) {
4135             var m = this.isSelected(item) ? 'unselect' : 'select';
4136             //Roo.log(m);
4137             var _t = this;
4138             _t[m](item, true, false);
4139             return true;
4140         }
4141         if(this.multiSelect || this.singleSelect){
4142             if(this.multiSelect && e.shiftKey && this.lastSelection){
4143                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4144             }else{
4145                 this.select(item, this.multiSelect && e.ctrlKey);
4146                 this.lastSelection = item;
4147             }
4148             
4149             if(!this.tickable){
4150                 e.preventDefault();
4151             }
4152             
4153         }
4154         return true;
4155     },
4156
4157     /**
4158      * Get the number of selected nodes.
4159      * @return {Number}
4160      */
4161     getSelectionCount : function(){
4162         return this.selections.length;
4163     },
4164
4165     /**
4166      * Get the currently selected nodes.
4167      * @return {Array} An array of HTMLElements
4168      */
4169     getSelectedNodes : function(){
4170         return this.selections;
4171     },
4172
4173     /**
4174      * Get the indexes of the selected nodes.
4175      * @return {Array}
4176      */
4177     getSelectedIndexes : function(){
4178         var indexes = [], s = this.selections;
4179         for(var i = 0, len = s.length; i < len; i++){
4180             indexes.push(s[i].nodeIndex);
4181         }
4182         return indexes;
4183     },
4184
4185     /**
4186      * Clear all selections
4187      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4188      */
4189     clearSelections : function(suppressEvent){
4190         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4191             this.cmp.elements = this.selections;
4192             this.cmp.removeClass(this.selectedClass);
4193             this.selections = [];
4194             if(!suppressEvent){
4195                 this.fireEvent("selectionchange", this, this.selections);
4196             }
4197         }
4198     },
4199
4200     /**
4201      * Returns true if the passed node is selected
4202      * @param {HTMLElement/Number} node The node or node index
4203      * @return {Boolean}
4204      */
4205     isSelected : function(node){
4206         var s = this.selections;
4207         if(s.length < 1){
4208             return false;
4209         }
4210         node = this.getNode(node);
4211         return s.indexOf(node) !== -1;
4212     },
4213
4214     /**
4215      * Selects nodes.
4216      * @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
4217      * @param {Boolean} keepExisting (optional) true to keep existing selections
4218      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4219      */
4220     select : function(nodeInfo, keepExisting, suppressEvent){
4221         if(nodeInfo instanceof Array){
4222             if(!keepExisting){
4223                 this.clearSelections(true);
4224             }
4225             for(var i = 0, len = nodeInfo.length; i < len; i++){
4226                 this.select(nodeInfo[i], true, true);
4227             }
4228             return;
4229         } 
4230         var node = this.getNode(nodeInfo);
4231         if(!node || this.isSelected(node)){
4232             return; // already selected.
4233         }
4234         if(!keepExisting){
4235             this.clearSelections(true);
4236         }
4237         
4238         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4239             Roo.fly(node).addClass(this.selectedClass);
4240             this.selections.push(node);
4241             if(!suppressEvent){
4242                 this.fireEvent("selectionchange", this, this.selections);
4243             }
4244         }
4245         
4246         
4247     },
4248       /**
4249      * Unselects nodes.
4250      * @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
4251      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4252      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4253      */
4254     unselect : function(nodeInfo, keepExisting, suppressEvent)
4255     {
4256         if(nodeInfo instanceof Array){
4257             Roo.each(this.selections, function(s) {
4258                 this.unselect(s, nodeInfo);
4259             }, this);
4260             return;
4261         }
4262         var node = this.getNode(nodeInfo);
4263         if(!node || !this.isSelected(node)){
4264             //Roo.log("not selected");
4265             return; // not selected.
4266         }
4267         // fireevent???
4268         var ns = [];
4269         Roo.each(this.selections, function(s) {
4270             if (s == node ) {
4271                 Roo.fly(node).removeClass(this.selectedClass);
4272
4273                 return;
4274             }
4275             ns.push(s);
4276         },this);
4277         
4278         this.selections= ns;
4279         this.fireEvent("selectionchange", this, this.selections);
4280     },
4281
4282     /**
4283      * Gets a template node.
4284      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4285      * @return {HTMLElement} The node or null if it wasn't found
4286      */
4287     getNode : function(nodeInfo){
4288         if(typeof nodeInfo == "string"){
4289             return document.getElementById(nodeInfo);
4290         }else if(typeof nodeInfo == "number"){
4291             return this.nodes[nodeInfo];
4292         }
4293         return nodeInfo;
4294     },
4295
4296     /**
4297      * Gets a range template nodes.
4298      * @param {Number} startIndex
4299      * @param {Number} endIndex
4300      * @return {Array} An array of nodes
4301      */
4302     getNodes : function(start, end){
4303         var ns = this.nodes;
4304         start = start || 0;
4305         end = typeof end == "undefined" ? ns.length - 1 : end;
4306         var nodes = [];
4307         if(start <= end){
4308             for(var i = start; i <= end; i++){
4309                 nodes.push(ns[i]);
4310             }
4311         } else{
4312             for(var i = start; i >= end; i--){
4313                 nodes.push(ns[i]);
4314             }
4315         }
4316         return nodes;
4317     },
4318
4319     /**
4320      * Finds the index of the passed node
4321      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4322      * @return {Number} The index of the node or -1
4323      */
4324     indexOf : function(node){
4325         node = this.getNode(node);
4326         if(typeof node.nodeIndex == "number"){
4327             return node.nodeIndex;
4328         }
4329         var ns = this.nodes;
4330         for(var i = 0, len = ns.length; i < len; i++){
4331             if(ns[i] == node){
4332                 return i;
4333             }
4334         }
4335         return -1;
4336     }
4337 });
4338 /*
4339  * Based on:
4340  * Ext JS Library 1.1.1
4341  * Copyright(c) 2006-2007, Ext JS, LLC.
4342  *
4343  * Originally Released Under LGPL - original licence link has changed is not relivant.
4344  *
4345  * Fork - LGPL
4346  * <script type="text/javascript">
4347  */
4348
4349 /**
4350  * @class Roo.JsonView
4351  * @extends Roo.View
4352  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4353 <pre><code>
4354 var view = new Roo.JsonView({
4355     container: "my-element",
4356     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4357     multiSelect: true, 
4358     jsonRoot: "data" 
4359 });
4360
4361 // listen for node click?
4362 view.on("click", function(vw, index, node, e){
4363     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4364 });
4365
4366 // direct load of JSON data
4367 view.load("foobar.php");
4368
4369 // Example from my blog list
4370 var tpl = new Roo.Template(
4371     '&lt;div class="entry"&gt;' +
4372     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4373     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4374     "&lt;/div&gt;&lt;hr /&gt;"
4375 );
4376
4377 var moreView = new Roo.JsonView({
4378     container :  "entry-list", 
4379     template : tpl,
4380     jsonRoot: "posts"
4381 });
4382 moreView.on("beforerender", this.sortEntries, this);
4383 moreView.load({
4384     url: "/blog/get-posts.php",
4385     params: "allposts=true",
4386     text: "Loading Blog Entries..."
4387 });
4388 </code></pre>
4389
4390 * Note: old code is supported with arguments : (container, template, config)
4391
4392
4393  * @constructor
4394  * Create a new JsonView
4395  * 
4396  * @param {Object} config The config object
4397  * 
4398  */
4399 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4400     
4401     
4402     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4403
4404     var um = this.el.getUpdateManager();
4405     um.setRenderer(this);
4406     um.on("update", this.onLoad, this);
4407     um.on("failure", this.onLoadException, this);
4408
4409     /**
4410      * @event beforerender
4411      * Fires before rendering of the downloaded JSON data.
4412      * @param {Roo.JsonView} this
4413      * @param {Object} data The JSON data loaded
4414      */
4415     /**
4416      * @event load
4417      * Fires when data is loaded.
4418      * @param {Roo.JsonView} this
4419      * @param {Object} data The JSON data loaded
4420      * @param {Object} response The raw Connect response object
4421      */
4422     /**
4423      * @event loadexception
4424      * Fires when loading fails.
4425      * @param {Roo.JsonView} this
4426      * @param {Object} response The raw Connect response object
4427      */
4428     this.addEvents({
4429         'beforerender' : true,
4430         'load' : true,
4431         'loadexception' : true
4432     });
4433 };
4434 Roo.extend(Roo.JsonView, Roo.View, {
4435     /**
4436      * @type {String} The root property in the loaded JSON object that contains the data
4437      */
4438     jsonRoot : "",
4439
4440     /**
4441      * Refreshes the view.
4442      */
4443     refresh : function(){
4444         this.clearSelections();
4445         this.el.update("");
4446         var html = [];
4447         var o = this.jsonData;
4448         if(o && o.length > 0){
4449             for(var i = 0, len = o.length; i < len; i++){
4450                 var data = this.prepareData(o[i], i, o);
4451                 html[html.length] = this.tpl.apply(data);
4452             }
4453         }else{
4454             html.push(this.emptyText);
4455         }
4456         this.el.update(html.join(""));
4457         this.nodes = this.el.dom.childNodes;
4458         this.updateIndexes(0);
4459     },
4460
4461     /**
4462      * 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.
4463      * @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:
4464      <pre><code>
4465      view.load({
4466          url: "your-url.php",
4467          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4468          callback: yourFunction,
4469          scope: yourObject, //(optional scope)
4470          discardUrl: false,
4471          nocache: false,
4472          text: "Loading...",
4473          timeout: 30,
4474          scripts: false
4475      });
4476      </code></pre>
4477      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4478      * 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.
4479      * @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}
4480      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4481      * @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.
4482      */
4483     load : function(){
4484         var um = this.el.getUpdateManager();
4485         um.update.apply(um, arguments);
4486     },
4487
4488     // note - render is a standard framework call...
4489     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4490     render : function(el, response){
4491         
4492         this.clearSelections();
4493         this.el.update("");
4494         var o;
4495         try{
4496             if (response != '') {
4497                 o = Roo.util.JSON.decode(response.responseText);
4498                 if(this.jsonRoot){
4499                     
4500                     o = o[this.jsonRoot];
4501                 }
4502             }
4503         } catch(e){
4504         }
4505         /**
4506          * The current JSON data or null
4507          */
4508         this.jsonData = o;
4509         this.beforeRender();
4510         this.refresh();
4511     },
4512
4513 /**
4514  * Get the number of records in the current JSON dataset
4515  * @return {Number}
4516  */
4517     getCount : function(){
4518         return this.jsonData ? this.jsonData.length : 0;
4519     },
4520
4521 /**
4522  * Returns the JSON object for the specified node(s)
4523  * @param {HTMLElement/Array} node The node or an array of nodes
4524  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4525  * you get the JSON object for the node
4526  */
4527     getNodeData : function(node){
4528         if(node instanceof Array){
4529             var data = [];
4530             for(var i = 0, len = node.length; i < len; i++){
4531                 data.push(this.getNodeData(node[i]));
4532             }
4533             return data;
4534         }
4535         return this.jsonData[this.indexOf(node)] || null;
4536     },
4537
4538     beforeRender : function(){
4539         this.snapshot = this.jsonData;
4540         if(this.sortInfo){
4541             this.sort.apply(this, this.sortInfo);
4542         }
4543         this.fireEvent("beforerender", this, this.jsonData);
4544     },
4545
4546     onLoad : function(el, o){
4547         this.fireEvent("load", this, this.jsonData, o);
4548     },
4549
4550     onLoadException : function(el, o){
4551         this.fireEvent("loadexception", this, o);
4552     },
4553
4554 /**
4555  * Filter the data by a specific property.
4556  * @param {String} property A property on your JSON objects
4557  * @param {String/RegExp} value Either string that the property values
4558  * should start with, or a RegExp to test against the property
4559  */
4560     filter : function(property, value){
4561         if(this.jsonData){
4562             var data = [];
4563             var ss = this.snapshot;
4564             if(typeof value == "string"){
4565                 var vlen = value.length;
4566                 if(vlen == 0){
4567                     this.clearFilter();
4568                     return;
4569                 }
4570                 value = value.toLowerCase();
4571                 for(var i = 0, len = ss.length; i < len; i++){
4572                     var o = ss[i];
4573                     if(o[property].substr(0, vlen).toLowerCase() == value){
4574                         data.push(o);
4575                     }
4576                 }
4577             } else if(value.exec){ // regex?
4578                 for(var i = 0, len = ss.length; i < len; i++){
4579                     var o = ss[i];
4580                     if(value.test(o[property])){
4581                         data.push(o);
4582                     }
4583                 }
4584             } else{
4585                 return;
4586             }
4587             this.jsonData = data;
4588             this.refresh();
4589         }
4590     },
4591
4592 /**
4593  * Filter by a function. The passed function will be called with each
4594  * object in the current dataset. If the function returns true the value is kept,
4595  * otherwise it is filtered.
4596  * @param {Function} fn
4597  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4598  */
4599     filterBy : function(fn, scope){
4600         if(this.jsonData){
4601             var data = [];
4602             var ss = this.snapshot;
4603             for(var i = 0, len = ss.length; i < len; i++){
4604                 var o = ss[i];
4605                 if(fn.call(scope || this, o)){
4606                     data.push(o);
4607                 }
4608             }
4609             this.jsonData = data;
4610             this.refresh();
4611         }
4612     },
4613
4614 /**
4615  * Clears the current filter.
4616  */
4617     clearFilter : function(){
4618         if(this.snapshot && this.jsonData != this.snapshot){
4619             this.jsonData = this.snapshot;
4620             this.refresh();
4621         }
4622     },
4623
4624
4625 /**
4626  * Sorts the data for this view and refreshes it.
4627  * @param {String} property A property on your JSON objects to sort on
4628  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4629  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4630  */
4631     sort : function(property, dir, sortType){
4632         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4633         if(this.jsonData){
4634             var p = property;
4635             var dsc = dir && dir.toLowerCase() == "desc";
4636             var f = function(o1, o2){
4637                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4638                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4639                 ;
4640                 if(v1 < v2){
4641                     return dsc ? +1 : -1;
4642                 } else if(v1 > v2){
4643                     return dsc ? -1 : +1;
4644                 } else{
4645                     return 0;
4646                 }
4647             };
4648             this.jsonData.sort(f);
4649             this.refresh();
4650             if(this.jsonData != this.snapshot){
4651                 this.snapshot.sort(f);
4652             }
4653         }
4654     }
4655 });/*
4656  * Based on:
4657  * Ext JS Library 1.1.1
4658  * Copyright(c) 2006-2007, Ext JS, LLC.
4659  *
4660  * Originally Released Under LGPL - original licence link has changed is not relivant.
4661  *
4662  * Fork - LGPL
4663  * <script type="text/javascript">
4664  */
4665  
4666
4667 /**
4668  * @class Roo.ColorPalette
4669  * @extends Roo.Component
4670  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4671  * Here's an example of typical usage:
4672  * <pre><code>
4673 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4674 cp.render('my-div');
4675
4676 cp.on('select', function(palette, selColor){
4677     // do something with selColor
4678 });
4679 </code></pre>
4680  * @constructor
4681  * Create a new ColorPalette
4682  * @param {Object} config The config object
4683  */
4684 Roo.ColorPalette = function(config){
4685     Roo.ColorPalette.superclass.constructor.call(this, config);
4686     this.addEvents({
4687         /**
4688              * @event select
4689              * Fires when a color is selected
4690              * @param {ColorPalette} this
4691              * @param {String} color The 6-digit color hex code (without the # symbol)
4692              */
4693         select: true
4694     });
4695
4696     if(this.handler){
4697         this.on("select", this.handler, this.scope, true);
4698     }
4699 };
4700 Roo.extend(Roo.ColorPalette, Roo.Component, {
4701     /**
4702      * @cfg {String} itemCls
4703      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4704      */
4705     itemCls : "x-color-palette",
4706     /**
4707      * @cfg {String} value
4708      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4709      * the hex codes are case-sensitive.
4710      */
4711     value : null,
4712     clickEvent:'click',
4713     // private
4714     ctype: "Roo.ColorPalette",
4715
4716     /**
4717      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4718      */
4719     allowReselect : false,
4720
4721     /**
4722      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4723      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4724      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4725      * of colors with the width setting until the box is symmetrical.</p>
4726      * <p>You can override individual colors if needed:</p>
4727      * <pre><code>
4728 var cp = new Roo.ColorPalette();
4729 cp.colors[0] = "FF0000";  // change the first box to red
4730 </code></pre>
4731
4732 Or you can provide a custom array of your own for complete control:
4733 <pre><code>
4734 var cp = new Roo.ColorPalette();
4735 cp.colors = ["000000", "993300", "333300"];
4736 </code></pre>
4737      * @type Array
4738      */
4739     colors : [
4740         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4741         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4742         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4743         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4744         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4745     ],
4746
4747     // private
4748     onRender : function(container, position){
4749         var t = new Roo.MasterTemplate(
4750             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4751         );
4752         var c = this.colors;
4753         for(var i = 0, len = c.length; i < len; i++){
4754             t.add([c[i]]);
4755         }
4756         var el = document.createElement("div");
4757         el.className = this.itemCls;
4758         t.overwrite(el);
4759         container.dom.insertBefore(el, position);
4760         this.el = Roo.get(el);
4761         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4762         if(this.clickEvent != 'click'){
4763             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4764         }
4765     },
4766
4767     // private
4768     afterRender : function(){
4769         Roo.ColorPalette.superclass.afterRender.call(this);
4770         if(this.value){
4771             var s = this.value;
4772             this.value = null;
4773             this.select(s);
4774         }
4775     },
4776
4777     // private
4778     handleClick : function(e, t){
4779         e.preventDefault();
4780         if(!this.disabled){
4781             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4782             this.select(c.toUpperCase());
4783         }
4784     },
4785
4786     /**
4787      * Selects the specified color in the palette (fires the select event)
4788      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4789      */
4790     select : function(color){
4791         color = color.replace("#", "");
4792         if(color != this.value || this.allowReselect){
4793             var el = this.el;
4794             if(this.value){
4795                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4796             }
4797             el.child("a.color-"+color).addClass("x-color-palette-sel");
4798             this.value = color;
4799             this.fireEvent("select", this, color);
4800         }
4801     }
4802 });/*
4803  * Based on:
4804  * Ext JS Library 1.1.1
4805  * Copyright(c) 2006-2007, Ext JS, LLC.
4806  *
4807  * Originally Released Under LGPL - original licence link has changed is not relivant.
4808  *
4809  * Fork - LGPL
4810  * <script type="text/javascript">
4811  */
4812  
4813 /**
4814  * @class Roo.DatePicker
4815  * @extends Roo.Component
4816  * Simple date picker class.
4817  * @constructor
4818  * Create a new DatePicker
4819  * @param {Object} config The config object
4820  */
4821 Roo.DatePicker = function(config){
4822     Roo.DatePicker.superclass.constructor.call(this, config);
4823
4824     this.value = config && config.value ?
4825                  config.value.clearTime() : new Date().clearTime();
4826
4827     this.addEvents({
4828         /**
4829              * @event select
4830              * Fires when a date is selected
4831              * @param {DatePicker} this
4832              * @param {Date} date The selected date
4833              */
4834         'select': true,
4835         /**
4836              * @event monthchange
4837              * Fires when the displayed month changes 
4838              * @param {DatePicker} this
4839              * @param {Date} date The selected month
4840              */
4841         'monthchange': true
4842     });
4843
4844     if(this.handler){
4845         this.on("select", this.handler,  this.scope || this);
4846     }
4847     // build the disabledDatesRE
4848     if(!this.disabledDatesRE && this.disabledDates){
4849         var dd = this.disabledDates;
4850         var re = "(?:";
4851         for(var i = 0; i < dd.length; i++){
4852             re += dd[i];
4853             if(i != dd.length-1) {
4854                 re += "|";
4855             }
4856         }
4857         this.disabledDatesRE = new RegExp(re + ")");
4858     }
4859 };
4860
4861 Roo.extend(Roo.DatePicker, Roo.Component, {
4862     /**
4863      * @cfg {String} todayText
4864      * The text to display on the button that selects the current date (defaults to "Today")
4865      */
4866     todayText : "Today",
4867     /**
4868      * @cfg {String} okText
4869      * The text to display on the ok button
4870      */
4871     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4872     /**
4873      * @cfg {String} cancelText
4874      * The text to display on the cancel button
4875      */
4876     cancelText : "Cancel",
4877     /**
4878      * @cfg {String} todayTip
4879      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4880      */
4881     todayTip : "{0} (Spacebar)",
4882     /**
4883      * @cfg {Date} minDate
4884      * Minimum allowable date (JavaScript date object, defaults to null)
4885      */
4886     minDate : null,
4887     /**
4888      * @cfg {Date} maxDate
4889      * Maximum allowable date (JavaScript date object, defaults to null)
4890      */
4891     maxDate : null,
4892     /**
4893      * @cfg {String} minText
4894      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4895      */
4896     minText : "This date is before the minimum date",
4897     /**
4898      * @cfg {String} maxText
4899      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4900      */
4901     maxText : "This date is after the maximum date",
4902     /**
4903      * @cfg {String} format
4904      * The default date format string which can be overriden for localization support.  The format must be
4905      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4906      */
4907     format : "m/d/y",
4908     /**
4909      * @cfg {Array} disabledDays
4910      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4911      */
4912     disabledDays : null,
4913     /**
4914      * @cfg {String} disabledDaysText
4915      * The tooltip to display when the date falls on a disabled day (defaults to "")
4916      */
4917     disabledDaysText : "",
4918     /**
4919      * @cfg {RegExp} disabledDatesRE
4920      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4921      */
4922     disabledDatesRE : null,
4923     /**
4924      * @cfg {String} disabledDatesText
4925      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4926      */
4927     disabledDatesText : "",
4928     /**
4929      * @cfg {Boolean} constrainToViewport
4930      * True to constrain the date picker to the viewport (defaults to true)
4931      */
4932     constrainToViewport : true,
4933     /**
4934      * @cfg {Array} monthNames
4935      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4936      */
4937     monthNames : Date.monthNames,
4938     /**
4939      * @cfg {Array} dayNames
4940      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4941      */
4942     dayNames : Date.dayNames,
4943     /**
4944      * @cfg {String} nextText
4945      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4946      */
4947     nextText: 'Next Month (Control+Right)',
4948     /**
4949      * @cfg {String} prevText
4950      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4951      */
4952     prevText: 'Previous Month (Control+Left)',
4953     /**
4954      * @cfg {String} monthYearText
4955      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4956      */
4957     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4958     /**
4959      * @cfg {Number} startDay
4960      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4961      */
4962     startDay : 0,
4963     /**
4964      * @cfg {Bool} showClear
4965      * Show a clear button (usefull for date form elements that can be blank.)
4966      */
4967     
4968     showClear: false,
4969     
4970     /**
4971      * Sets the value of the date field
4972      * @param {Date} value The date to set
4973      */
4974     setValue : function(value){
4975         var old = this.value;
4976         
4977         if (typeof(value) == 'string') {
4978          
4979             value = Date.parseDate(value, this.format);
4980         }
4981         if (!value) {
4982             value = new Date();
4983         }
4984         
4985         this.value = value.clearTime(true);
4986         if(this.el){
4987             this.update(this.value);
4988         }
4989     },
4990
4991     /**
4992      * Gets the current selected value of the date field
4993      * @return {Date} The selected date
4994      */
4995     getValue : function(){
4996         return this.value;
4997     },
4998
4999     // private
5000     focus : function(){
5001         if(this.el){
5002             this.update(this.activeDate);
5003         }
5004     },
5005
5006     // privateval
5007     onRender : function(container, position){
5008         
5009         var m = [
5010              '<table cellspacing="0">',
5011                 '<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>',
5012                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5013         var dn = this.dayNames;
5014         for(var i = 0; i < 7; i++){
5015             var d = this.startDay+i;
5016             if(d > 6){
5017                 d = d-7;
5018             }
5019             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5020         }
5021         m[m.length] = "</tr></thead><tbody><tr>";
5022         for(var i = 0; i < 42; i++) {
5023             if(i % 7 == 0 && i != 0){
5024                 m[m.length] = "</tr><tr>";
5025             }
5026             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5027         }
5028         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5029             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5030
5031         var el = document.createElement("div");
5032         el.className = "x-date-picker";
5033         el.innerHTML = m.join("");
5034
5035         container.dom.insertBefore(el, position);
5036
5037         this.el = Roo.get(el);
5038         this.eventEl = Roo.get(el.firstChild);
5039
5040         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5041             handler: this.showPrevMonth,
5042             scope: this,
5043             preventDefault:true,
5044             stopDefault:true
5045         });
5046
5047         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5048             handler: this.showNextMonth,
5049             scope: this,
5050             preventDefault:true,
5051             stopDefault:true
5052         });
5053
5054         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5055
5056         this.monthPicker = this.el.down('div.x-date-mp');
5057         this.monthPicker.enableDisplayMode('block');
5058         
5059         var kn = new Roo.KeyNav(this.eventEl, {
5060             "left" : function(e){
5061                 e.ctrlKey ?
5062                     this.showPrevMonth() :
5063                     this.update(this.activeDate.add("d", -1));
5064             },
5065
5066             "right" : function(e){
5067                 e.ctrlKey ?
5068                     this.showNextMonth() :
5069                     this.update(this.activeDate.add("d", 1));
5070             },
5071
5072             "up" : function(e){
5073                 e.ctrlKey ?
5074                     this.showNextYear() :
5075                     this.update(this.activeDate.add("d", -7));
5076             },
5077
5078             "down" : function(e){
5079                 e.ctrlKey ?
5080                     this.showPrevYear() :
5081                     this.update(this.activeDate.add("d", 7));
5082             },
5083
5084             "pageUp" : function(e){
5085                 this.showNextMonth();
5086             },
5087
5088             "pageDown" : function(e){
5089                 this.showPrevMonth();
5090             },
5091
5092             "enter" : function(e){
5093                 e.stopPropagation();
5094                 return true;
5095             },
5096
5097             scope : this
5098         });
5099
5100         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5101
5102         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5103
5104         this.el.unselectable();
5105         
5106         this.cells = this.el.select("table.x-date-inner tbody td");
5107         this.textNodes = this.el.query("table.x-date-inner tbody span");
5108
5109         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5110             text: "&#160;",
5111             tooltip: this.monthYearText
5112         });
5113
5114         this.mbtn.on('click', this.showMonthPicker, this);
5115         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5116
5117
5118         var today = (new Date()).dateFormat(this.format);
5119         
5120         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5121         if (this.showClear) {
5122             baseTb.add( new Roo.Toolbar.Fill());
5123         }
5124         baseTb.add({
5125             text: String.format(this.todayText, today),
5126             tooltip: String.format(this.todayTip, today),
5127             handler: this.selectToday,
5128             scope: this
5129         });
5130         
5131         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5132             
5133         //});
5134         if (this.showClear) {
5135             
5136             baseTb.add( new Roo.Toolbar.Fill());
5137             baseTb.add({
5138                 text: '&#160;',
5139                 cls: 'x-btn-icon x-btn-clear',
5140                 handler: function() {
5141                     //this.value = '';
5142                     this.fireEvent("select", this, '');
5143                 },
5144                 scope: this
5145             });
5146         }
5147         
5148         
5149         if(Roo.isIE){
5150             this.el.repaint();
5151         }
5152         this.update(this.value);
5153     },
5154
5155     createMonthPicker : function(){
5156         if(!this.monthPicker.dom.firstChild){
5157             var buf = ['<table border="0" cellspacing="0">'];
5158             for(var i = 0; i < 6; i++){
5159                 buf.push(
5160                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5161                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5162                     i == 0 ?
5163                     '<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>' :
5164                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5165                 );
5166             }
5167             buf.push(
5168                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5169                     this.okText,
5170                     '</button><button type="button" class="x-date-mp-cancel">',
5171                     this.cancelText,
5172                     '</button></td></tr>',
5173                 '</table>'
5174             );
5175             this.monthPicker.update(buf.join(''));
5176             this.monthPicker.on('click', this.onMonthClick, this);
5177             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5178
5179             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5180             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5181
5182             this.mpMonths.each(function(m, a, i){
5183                 i += 1;
5184                 if((i%2) == 0){
5185                     m.dom.xmonth = 5 + Math.round(i * .5);
5186                 }else{
5187                     m.dom.xmonth = Math.round((i-1) * .5);
5188                 }
5189             });
5190         }
5191     },
5192
5193     showMonthPicker : function(){
5194         this.createMonthPicker();
5195         var size = this.el.getSize();
5196         this.monthPicker.setSize(size);
5197         this.monthPicker.child('table').setSize(size);
5198
5199         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5200         this.updateMPMonth(this.mpSelMonth);
5201         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5202         this.updateMPYear(this.mpSelYear);
5203
5204         this.monthPicker.slideIn('t', {duration:.2});
5205     },
5206
5207     updateMPYear : function(y){
5208         this.mpyear = y;
5209         var ys = this.mpYears.elements;
5210         for(var i = 1; i <= 10; i++){
5211             var td = ys[i-1], y2;
5212             if((i%2) == 0){
5213                 y2 = y + Math.round(i * .5);
5214                 td.firstChild.innerHTML = y2;
5215                 td.xyear = y2;
5216             }else{
5217                 y2 = y - (5-Math.round(i * .5));
5218                 td.firstChild.innerHTML = y2;
5219                 td.xyear = y2;
5220             }
5221             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5222         }
5223     },
5224
5225     updateMPMonth : function(sm){
5226         this.mpMonths.each(function(m, a, i){
5227             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5228         });
5229     },
5230
5231     selectMPMonth: function(m){
5232         
5233     },
5234
5235     onMonthClick : function(e, t){
5236         e.stopEvent();
5237         var el = new Roo.Element(t), pn;
5238         if(el.is('button.x-date-mp-cancel')){
5239             this.hideMonthPicker();
5240         }
5241         else if(el.is('button.x-date-mp-ok')){
5242             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5243             this.hideMonthPicker();
5244         }
5245         else if(pn = el.up('td.x-date-mp-month', 2)){
5246             this.mpMonths.removeClass('x-date-mp-sel');
5247             pn.addClass('x-date-mp-sel');
5248             this.mpSelMonth = pn.dom.xmonth;
5249         }
5250         else if(pn = el.up('td.x-date-mp-year', 2)){
5251             this.mpYears.removeClass('x-date-mp-sel');
5252             pn.addClass('x-date-mp-sel');
5253             this.mpSelYear = pn.dom.xyear;
5254         }
5255         else if(el.is('a.x-date-mp-prev')){
5256             this.updateMPYear(this.mpyear-10);
5257         }
5258         else if(el.is('a.x-date-mp-next')){
5259             this.updateMPYear(this.mpyear+10);
5260         }
5261     },
5262
5263     onMonthDblClick : function(e, t){
5264         e.stopEvent();
5265         var el = new Roo.Element(t), pn;
5266         if(pn = el.up('td.x-date-mp-month', 2)){
5267             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5268             this.hideMonthPicker();
5269         }
5270         else if(pn = el.up('td.x-date-mp-year', 2)){
5271             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5272             this.hideMonthPicker();
5273         }
5274     },
5275
5276     hideMonthPicker : function(disableAnim){
5277         if(this.monthPicker){
5278             if(disableAnim === true){
5279                 this.monthPicker.hide();
5280             }else{
5281                 this.monthPicker.slideOut('t', {duration:.2});
5282             }
5283         }
5284     },
5285
5286     // private
5287     showPrevMonth : function(e){
5288         this.update(this.activeDate.add("mo", -1));
5289     },
5290
5291     // private
5292     showNextMonth : function(e){
5293         this.update(this.activeDate.add("mo", 1));
5294     },
5295
5296     // private
5297     showPrevYear : function(){
5298         this.update(this.activeDate.add("y", -1));
5299     },
5300
5301     // private
5302     showNextYear : function(){
5303         this.update(this.activeDate.add("y", 1));
5304     },
5305
5306     // private
5307     handleMouseWheel : function(e){
5308         var delta = e.getWheelDelta();
5309         if(delta > 0){
5310             this.showPrevMonth();
5311             e.stopEvent();
5312         } else if(delta < 0){
5313             this.showNextMonth();
5314             e.stopEvent();
5315         }
5316     },
5317
5318     // private
5319     handleDateClick : function(e, t){
5320         e.stopEvent();
5321         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5322             this.setValue(new Date(t.dateValue));
5323             this.fireEvent("select", this, this.value);
5324         }
5325     },
5326
5327     // private
5328     selectToday : function(){
5329         this.setValue(new Date().clearTime());
5330         this.fireEvent("select", this, this.value);
5331     },
5332
5333     // private
5334     update : function(date)
5335     {
5336         var vd = this.activeDate;
5337         this.activeDate = date;
5338         if(vd && this.el){
5339             var t = date.getTime();
5340             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5341                 this.cells.removeClass("x-date-selected");
5342                 this.cells.each(function(c){
5343                    if(c.dom.firstChild.dateValue == t){
5344                        c.addClass("x-date-selected");
5345                        setTimeout(function(){
5346                             try{c.dom.firstChild.focus();}catch(e){}
5347                        }, 50);
5348                        return false;
5349                    }
5350                 });
5351                 return;
5352             }
5353         }
5354         
5355         var days = date.getDaysInMonth();
5356         var firstOfMonth = date.getFirstDateOfMonth();
5357         var startingPos = firstOfMonth.getDay()-this.startDay;
5358
5359         if(startingPos <= this.startDay){
5360             startingPos += 7;
5361         }
5362
5363         var pm = date.add("mo", -1);
5364         var prevStart = pm.getDaysInMonth()-startingPos;
5365
5366         var cells = this.cells.elements;
5367         var textEls = this.textNodes;
5368         days += startingPos;
5369
5370         // convert everything to numbers so it's fast
5371         var day = 86400000;
5372         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5373         var today = new Date().clearTime().getTime();
5374         var sel = date.clearTime().getTime();
5375         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5376         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5377         var ddMatch = this.disabledDatesRE;
5378         var ddText = this.disabledDatesText;
5379         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5380         var ddaysText = this.disabledDaysText;
5381         var format = this.format;
5382
5383         var setCellClass = function(cal, cell){
5384             cell.title = "";
5385             var t = d.getTime();
5386             cell.firstChild.dateValue = t;
5387             if(t == today){
5388                 cell.className += " x-date-today";
5389                 cell.title = cal.todayText;
5390             }
5391             if(t == sel){
5392                 cell.className += " x-date-selected";
5393                 setTimeout(function(){
5394                     try{cell.firstChild.focus();}catch(e){}
5395                 }, 50);
5396             }
5397             // disabling
5398             if(t < min) {
5399                 cell.className = " x-date-disabled";
5400                 cell.title = cal.minText;
5401                 return;
5402             }
5403             if(t > max) {
5404                 cell.className = " x-date-disabled";
5405                 cell.title = cal.maxText;
5406                 return;
5407             }
5408             if(ddays){
5409                 if(ddays.indexOf(d.getDay()) != -1){
5410                     cell.title = ddaysText;
5411                     cell.className = " x-date-disabled";
5412                 }
5413             }
5414             if(ddMatch && format){
5415                 var fvalue = d.dateFormat(format);
5416                 if(ddMatch.test(fvalue)){
5417                     cell.title = ddText.replace("%0", fvalue);
5418                     cell.className = " x-date-disabled";
5419                 }
5420             }
5421         };
5422
5423         var i = 0;
5424         for(; i < startingPos; i++) {
5425             textEls[i].innerHTML = (++prevStart);
5426             d.setDate(d.getDate()+1);
5427             cells[i].className = "x-date-prevday";
5428             setCellClass(this, cells[i]);
5429         }
5430         for(; i < days; i++){
5431             intDay = i - startingPos + 1;
5432             textEls[i].innerHTML = (intDay);
5433             d.setDate(d.getDate()+1);
5434             cells[i].className = "x-date-active";
5435             setCellClass(this, cells[i]);
5436         }
5437         var extraDays = 0;
5438         for(; i < 42; i++) {
5439              textEls[i].innerHTML = (++extraDays);
5440              d.setDate(d.getDate()+1);
5441              cells[i].className = "x-date-nextday";
5442              setCellClass(this, cells[i]);
5443         }
5444
5445         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5446         this.fireEvent('monthchange', this, date);
5447         
5448         if(!this.internalRender){
5449             var main = this.el.dom.firstChild;
5450             var w = main.offsetWidth;
5451             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5452             Roo.fly(main).setWidth(w);
5453             this.internalRender = true;
5454             // opera does not respect the auto grow header center column
5455             // then, after it gets a width opera refuses to recalculate
5456             // without a second pass
5457             if(Roo.isOpera && !this.secondPass){
5458                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5459                 this.secondPass = true;
5460                 this.update.defer(10, this, [date]);
5461             }
5462         }
5463         
5464         
5465     }
5466 });        /*
5467  * Based on:
5468  * Ext JS Library 1.1.1
5469  * Copyright(c) 2006-2007, Ext JS, LLC.
5470  *
5471  * Originally Released Under LGPL - original licence link has changed is not relivant.
5472  *
5473  * Fork - LGPL
5474  * <script type="text/javascript">
5475  */
5476 /**
5477  * @class Roo.TabPanel
5478  * @extends Roo.util.Observable
5479  * A lightweight tab container.
5480  * <br><br>
5481  * Usage:
5482  * <pre><code>
5483 // basic tabs 1, built from existing content
5484 var tabs = new Roo.TabPanel("tabs1");
5485 tabs.addTab("script", "View Script");
5486 tabs.addTab("markup", "View Markup");
5487 tabs.activate("script");
5488
5489 // more advanced tabs, built from javascript
5490 var jtabs = new Roo.TabPanel("jtabs");
5491 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5492
5493 // set up the UpdateManager
5494 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5495 var updater = tab2.getUpdateManager();
5496 updater.setDefaultUrl("ajax1.htm");
5497 tab2.on('activate', updater.refresh, updater, true);
5498
5499 // Use setUrl for Ajax loading
5500 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5501 tab3.setUrl("ajax2.htm", null, true);
5502
5503 // Disabled tab
5504 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5505 tab4.disable();
5506
5507 jtabs.activate("jtabs-1");
5508  * </code></pre>
5509  * @constructor
5510  * Create a new TabPanel.
5511  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5512  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5513  */
5514 Roo.TabPanel = function(container, config){
5515     /**
5516     * The container element for this TabPanel.
5517     * @type Roo.Element
5518     */
5519     this.el = Roo.get(container, true);
5520     if(config){
5521         if(typeof config == "boolean"){
5522             this.tabPosition = config ? "bottom" : "top";
5523         }else{
5524             Roo.apply(this, config);
5525         }
5526     }
5527     if(this.tabPosition == "bottom"){
5528         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5529         this.el.addClass("x-tabs-bottom");
5530     }
5531     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5532     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5533     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5534     if(Roo.isIE){
5535         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5536     }
5537     if(this.tabPosition != "bottom"){
5538         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5539          * @type Roo.Element
5540          */
5541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5542         this.el.addClass("x-tabs-top");
5543     }
5544     this.items = [];
5545
5546     this.bodyEl.setStyle("position", "relative");
5547
5548     this.active = null;
5549     this.activateDelegate = this.activate.createDelegate(this);
5550
5551     this.addEvents({
5552         /**
5553          * @event tabchange
5554          * Fires when the active tab changes
5555          * @param {Roo.TabPanel} this
5556          * @param {Roo.TabPanelItem} activePanel The new active tab
5557          */
5558         "tabchange": true,
5559         /**
5560          * @event beforetabchange
5561          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5562          * @param {Roo.TabPanel} this
5563          * @param {Object} e Set cancel to true on this object to cancel the tab change
5564          * @param {Roo.TabPanelItem} tab The tab being changed to
5565          */
5566         "beforetabchange" : true
5567     });
5568
5569     Roo.EventManager.onWindowResize(this.onResize, this);
5570     this.cpad = this.el.getPadding("lr");
5571     this.hiddenCount = 0;
5572
5573
5574     // toolbar on the tabbar support...
5575     if (this.toolbar) {
5576         var tcfg = this.toolbar;
5577         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5578         this.toolbar = new Roo.Toolbar(tcfg);
5579         if (Roo.isSafari) {
5580             var tbl = tcfg.container.child('table', true);
5581             tbl.setAttribute('width', '100%');
5582         }
5583         
5584     }
5585    
5586
5587
5588     Roo.TabPanel.superclass.constructor.call(this);
5589 };
5590
5591 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5592     /*
5593      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5594      */
5595     tabPosition : "top",
5596     /*
5597      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5598      */
5599     currentTabWidth : 0,
5600     /*
5601      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5602      */
5603     minTabWidth : 40,
5604     /*
5605      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5606      */
5607     maxTabWidth : 250,
5608     /*
5609      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5610      */
5611     preferredTabWidth : 175,
5612     /*
5613      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5614      */
5615     resizeTabs : false,
5616     /*
5617      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5618      */
5619     monitorResize : true,
5620     /*
5621      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5622      */
5623     toolbar : false,
5624
5625     /**
5626      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5627      * @param {String} id The id of the div to use <b>or create</b>
5628      * @param {String} text The text for the tab
5629      * @param {String} content (optional) Content to put in the TabPanelItem body
5630      * @param {Boolean} closable (optional) True to create a close icon on the tab
5631      * @return {Roo.TabPanelItem} The created TabPanelItem
5632      */
5633     addTab : function(id, text, content, closable){
5634         var item = new Roo.TabPanelItem(this, id, text, closable);
5635         this.addTabItem(item);
5636         if(content){
5637             item.setContent(content);
5638         }
5639         return item;
5640     },
5641
5642     /**
5643      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5644      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5645      * @return {Roo.TabPanelItem}
5646      */
5647     getTab : function(id){
5648         return this.items[id];
5649     },
5650
5651     /**
5652      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5653      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5654      */
5655     hideTab : function(id){
5656         var t = this.items[id];
5657         if(!t.isHidden()){
5658            t.setHidden(true);
5659            this.hiddenCount++;
5660            this.autoSizeTabs();
5661         }
5662     },
5663
5664     /**
5665      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5666      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5667      */
5668     unhideTab : function(id){
5669         var t = this.items[id];
5670         if(t.isHidden()){
5671            t.setHidden(false);
5672            this.hiddenCount--;
5673            this.autoSizeTabs();
5674         }
5675     },
5676
5677     /**
5678      * Adds an existing {@link Roo.TabPanelItem}.
5679      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5680      */
5681     addTabItem : function(item){
5682         this.items[item.id] = item;
5683         this.items.push(item);
5684         if(this.resizeTabs){
5685            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5686            this.autoSizeTabs();
5687         }else{
5688             item.autoSize();
5689         }
5690     },
5691
5692     /**
5693      * Removes a {@link Roo.TabPanelItem}.
5694      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5695      */
5696     removeTab : function(id){
5697         var items = this.items;
5698         var tab = items[id];
5699         if(!tab) { return; }
5700         var index = items.indexOf(tab);
5701         if(this.active == tab && items.length > 1){
5702             var newTab = this.getNextAvailable(index);
5703             if(newTab) {
5704                 newTab.activate();
5705             }
5706         }
5707         this.stripEl.dom.removeChild(tab.pnode.dom);
5708         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5709             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5710         }
5711         items.splice(index, 1);
5712         delete this.items[tab.id];
5713         tab.fireEvent("close", tab);
5714         tab.purgeListeners();
5715         this.autoSizeTabs();
5716     },
5717
5718     getNextAvailable : function(start){
5719         var items = this.items;
5720         var index = start;
5721         // look for a next tab that will slide over to
5722         // replace the one being removed
5723         while(index < items.length){
5724             var item = items[++index];
5725             if(item && !item.isHidden()){
5726                 return item;
5727             }
5728         }
5729         // if one isn't found select the previous tab (on the left)
5730         index = start;
5731         while(index >= 0){
5732             var item = items[--index];
5733             if(item && !item.isHidden()){
5734                 return item;
5735             }
5736         }
5737         return null;
5738     },
5739
5740     /**
5741      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5742      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5743      */
5744     disableTab : function(id){
5745         var tab = this.items[id];
5746         if(tab && this.active != tab){
5747             tab.disable();
5748         }
5749     },
5750
5751     /**
5752      * Enables a {@link Roo.TabPanelItem} that is disabled.
5753      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5754      */
5755     enableTab : function(id){
5756         var tab = this.items[id];
5757         tab.enable();
5758     },
5759
5760     /**
5761      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5762      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5763      * @return {Roo.TabPanelItem} The TabPanelItem.
5764      */
5765     activate : function(id){
5766         var tab = this.items[id];
5767         if(!tab){
5768             return null;
5769         }
5770         if(tab == this.active || tab.disabled){
5771             return tab;
5772         }
5773         var e = {};
5774         this.fireEvent("beforetabchange", this, e, tab);
5775         if(e.cancel !== true && !tab.disabled){
5776             if(this.active){
5777                 this.active.hide();
5778             }
5779             this.active = this.items[id];
5780             this.active.show();
5781             this.fireEvent("tabchange", this, this.active);
5782         }
5783         return tab;
5784     },
5785
5786     /**
5787      * Gets the active {@link Roo.TabPanelItem}.
5788      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5789      */
5790     getActiveTab : function(){
5791         return this.active;
5792     },
5793
5794     /**
5795      * Updates the tab body element to fit the height of the container element
5796      * for overflow scrolling
5797      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5798      */
5799     syncHeight : function(targetHeight){
5800         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5801         var bm = this.bodyEl.getMargins();
5802         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5803         this.bodyEl.setHeight(newHeight);
5804         return newHeight;
5805     },
5806
5807     onResize : function(){
5808         if(this.monitorResize){
5809             this.autoSizeTabs();
5810         }
5811     },
5812
5813     /**
5814      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5815      */
5816     beginUpdate : function(){
5817         this.updating = true;
5818     },
5819
5820     /**
5821      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5822      */
5823     endUpdate : function(){
5824         this.updating = false;
5825         this.autoSizeTabs();
5826     },
5827
5828     /**
5829      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5830      */
5831     autoSizeTabs : function(){
5832         var count = this.items.length;
5833         var vcount = count - this.hiddenCount;
5834         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5835             return;
5836         }
5837         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5838         var availWidth = Math.floor(w / vcount);
5839         var b = this.stripBody;
5840         if(b.getWidth() > w){
5841             var tabs = this.items;
5842             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5843             if(availWidth < this.minTabWidth){
5844                 /*if(!this.sleft){    // incomplete scrolling code
5845                     this.createScrollButtons();
5846                 }
5847                 this.showScroll();
5848                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5849             }
5850         }else{
5851             if(this.currentTabWidth < this.preferredTabWidth){
5852                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5853             }
5854         }
5855     },
5856
5857     /**
5858      * Returns the number of tabs in this TabPanel.
5859      * @return {Number}
5860      */
5861      getCount : function(){
5862          return this.items.length;
5863      },
5864
5865     /**
5866      * Resizes all the tabs to the passed width
5867      * @param {Number} The new width
5868      */
5869     setTabWidth : function(width){
5870         this.currentTabWidth = width;
5871         for(var i = 0, len = this.items.length; i < len; i++) {
5872                 if(!this.items[i].isHidden()) {
5873                 this.items[i].setWidth(width);
5874             }
5875         }
5876     },
5877
5878     /**
5879      * Destroys this TabPanel
5880      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5881      */
5882     destroy : function(removeEl){
5883         Roo.EventManager.removeResizeListener(this.onResize, this);
5884         for(var i = 0, len = this.items.length; i < len; i++){
5885             this.items[i].purgeListeners();
5886         }
5887         if(removeEl === true){
5888             this.el.update("");
5889             this.el.remove();
5890         }
5891     }
5892 });
5893
5894 /**
5895  * @class Roo.TabPanelItem
5896  * @extends Roo.util.Observable
5897  * Represents an individual item (tab plus body) in a TabPanel.
5898  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5899  * @param {String} id The id of this TabPanelItem
5900  * @param {String} text The text for the tab of this TabPanelItem
5901  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5902  */
5903 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5904     /**
5905      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5906      * @type Roo.TabPanel
5907      */
5908     this.tabPanel = tabPanel;
5909     /**
5910      * The id for this TabPanelItem
5911      * @type String
5912      */
5913     this.id = id;
5914     /** @private */
5915     this.disabled = false;
5916     /** @private */
5917     this.text = text;
5918     /** @private */
5919     this.loaded = false;
5920     this.closable = closable;
5921
5922     /**
5923      * The body element for this TabPanelItem.
5924      * @type Roo.Element
5925      */
5926     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5927     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5928     this.bodyEl.setStyle("display", "block");
5929     this.bodyEl.setStyle("zoom", "1");
5930     this.hideAction();
5931
5932     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5933     /** @private */
5934     this.el = Roo.get(els.el, true);
5935     this.inner = Roo.get(els.inner, true);
5936     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5937     this.pnode = Roo.get(els.el.parentNode, true);
5938     this.el.on("mousedown", this.onTabMouseDown, this);
5939     this.el.on("click", this.onTabClick, this);
5940     /** @private */
5941     if(closable){
5942         var c = Roo.get(els.close, true);
5943         c.dom.title = this.closeText;
5944         c.addClassOnOver("close-over");
5945         c.on("click", this.closeClick, this);
5946      }
5947
5948     this.addEvents({
5949          /**
5950          * @event activate
5951          * Fires when this tab becomes the active tab.
5952          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5953          * @param {Roo.TabPanelItem} this
5954          */
5955         "activate": true,
5956         /**
5957          * @event beforeclose
5958          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5959          * @param {Roo.TabPanelItem} this
5960          * @param {Object} e Set cancel to true on this object to cancel the close.
5961          */
5962         "beforeclose": true,
5963         /**
5964          * @event close
5965          * Fires when this tab is closed.
5966          * @param {Roo.TabPanelItem} this
5967          */
5968          "close": true,
5969         /**
5970          * @event deactivate
5971          * Fires when this tab is no longer the active tab.
5972          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5973          * @param {Roo.TabPanelItem} this
5974          */
5975          "deactivate" : true
5976     });
5977     this.hidden = false;
5978
5979     Roo.TabPanelItem.superclass.constructor.call(this);
5980 };
5981
5982 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5983     purgeListeners : function(){
5984        Roo.util.Observable.prototype.purgeListeners.call(this);
5985        this.el.removeAllListeners();
5986     },
5987     /**
5988      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5989      */
5990     show : function(){
5991         this.pnode.addClass("on");
5992         this.showAction();
5993         if(Roo.isOpera){
5994             this.tabPanel.stripWrap.repaint();
5995         }
5996         this.fireEvent("activate", this.tabPanel, this);
5997     },
5998
5999     /**
6000      * Returns true if this tab is the active tab.
6001      * @return {Boolean}
6002      */
6003     isActive : function(){
6004         return this.tabPanel.getActiveTab() == this;
6005     },
6006
6007     /**
6008      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6009      */
6010     hide : function(){
6011         this.pnode.removeClass("on");
6012         this.hideAction();
6013         this.fireEvent("deactivate", this.tabPanel, this);
6014     },
6015
6016     hideAction : function(){
6017         this.bodyEl.hide();
6018         this.bodyEl.setStyle("position", "absolute");
6019         this.bodyEl.setLeft("-20000px");
6020         this.bodyEl.setTop("-20000px");
6021     },
6022
6023     showAction : function(){
6024         this.bodyEl.setStyle("position", "relative");
6025         this.bodyEl.setTop("");
6026         this.bodyEl.setLeft("");
6027         this.bodyEl.show();
6028     },
6029
6030     /**
6031      * Set the tooltip for the tab.
6032      * @param {String} tooltip The tab's tooltip
6033      */
6034     setTooltip : function(text){
6035         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6036             this.textEl.dom.qtip = text;
6037             this.textEl.dom.removeAttribute('title');
6038         }else{
6039             this.textEl.dom.title = text;
6040         }
6041     },
6042
6043     onTabClick : function(e){
6044         e.preventDefault();
6045         this.tabPanel.activate(this.id);
6046     },
6047
6048     onTabMouseDown : function(e){
6049         e.preventDefault();
6050         this.tabPanel.activate(this.id);
6051     },
6052
6053     getWidth : function(){
6054         return this.inner.getWidth();
6055     },
6056
6057     setWidth : function(width){
6058         var iwidth = width - this.pnode.getPadding("lr");
6059         this.inner.setWidth(iwidth);
6060         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6061         this.pnode.setWidth(width);
6062     },
6063
6064     /**
6065      * Show or hide the tab
6066      * @param {Boolean} hidden True to hide or false to show.
6067      */
6068     setHidden : function(hidden){
6069         this.hidden = hidden;
6070         this.pnode.setStyle("display", hidden ? "none" : "");
6071     },
6072
6073     /**
6074      * Returns true if this tab is "hidden"
6075      * @return {Boolean}
6076      */
6077     isHidden : function(){
6078         return this.hidden;
6079     },
6080
6081     /**
6082      * Returns the text for this tab
6083      * @return {String}
6084      */
6085     getText : function(){
6086         return this.text;
6087     },
6088
6089     autoSize : function(){
6090         //this.el.beginMeasure();
6091         this.textEl.setWidth(1);
6092         /*
6093          *  #2804 [new] Tabs in Roojs
6094          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6095          */
6096         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6097         //this.el.endMeasure();
6098     },
6099
6100     /**
6101      * Sets the text for the tab (Note: this also sets the tooltip text)
6102      * @param {String} text The tab's text and tooltip
6103      */
6104     setText : function(text){
6105         this.text = text;
6106         this.textEl.update(text);
6107         this.setTooltip(text);
6108         if(!this.tabPanel.resizeTabs){
6109             this.autoSize();
6110         }
6111     },
6112     /**
6113      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6114      */
6115     activate : function(){
6116         this.tabPanel.activate(this.id);
6117     },
6118
6119     /**
6120      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6121      */
6122     disable : function(){
6123         if(this.tabPanel.active != this){
6124             this.disabled = true;
6125             this.pnode.addClass("disabled");
6126         }
6127     },
6128
6129     /**
6130      * Enables this TabPanelItem if it was previously disabled.
6131      */
6132     enable : function(){
6133         this.disabled = false;
6134         this.pnode.removeClass("disabled");
6135     },
6136
6137     /**
6138      * Sets the content for this TabPanelItem.
6139      * @param {String} content The content
6140      * @param {Boolean} loadScripts true to look for and load scripts
6141      */
6142     setContent : function(content, loadScripts){
6143         this.bodyEl.update(content, loadScripts);
6144     },
6145
6146     /**
6147      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6148      * @return {Roo.UpdateManager} The UpdateManager
6149      */
6150     getUpdateManager : function(){
6151         return this.bodyEl.getUpdateManager();
6152     },
6153
6154     /**
6155      * Set a URL to be used to load the content for this TabPanelItem.
6156      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6157      * @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)
6158      * @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)
6159      * @return {Roo.UpdateManager} The UpdateManager
6160      */
6161     setUrl : function(url, params, loadOnce){
6162         if(this.refreshDelegate){
6163             this.un('activate', this.refreshDelegate);
6164         }
6165         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6166         this.on("activate", this.refreshDelegate);
6167         return this.bodyEl.getUpdateManager();
6168     },
6169
6170     /** @private */
6171     _handleRefresh : function(url, params, loadOnce){
6172         if(!loadOnce || !this.loaded){
6173             var updater = this.bodyEl.getUpdateManager();
6174             updater.update(url, params, this._setLoaded.createDelegate(this));
6175         }
6176     },
6177
6178     /**
6179      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6180      *   Will fail silently if the setUrl method has not been called.
6181      *   This does not activate the panel, just updates its content.
6182      */
6183     refresh : function(){
6184         if(this.refreshDelegate){
6185            this.loaded = false;
6186            this.refreshDelegate();
6187         }
6188     },
6189
6190     /** @private */
6191     _setLoaded : function(){
6192         this.loaded = true;
6193     },
6194
6195     /** @private */
6196     closeClick : function(e){
6197         var o = {};
6198         e.stopEvent();
6199         this.fireEvent("beforeclose", this, o);
6200         if(o.cancel !== true){
6201             this.tabPanel.removeTab(this.id);
6202         }
6203     },
6204     /**
6205      * The text displayed in the tooltip for the close icon.
6206      * @type String
6207      */
6208     closeText : "Close this tab"
6209 });
6210
6211 /** @private */
6212 Roo.TabPanel.prototype.createStrip = function(container){
6213     var strip = document.createElement("div");
6214     strip.className = "x-tabs-wrap";
6215     container.appendChild(strip);
6216     return strip;
6217 };
6218 /** @private */
6219 Roo.TabPanel.prototype.createStripList = function(strip){
6220     // div wrapper for retard IE
6221     // returns the "tr" element.
6222     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6223         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6224         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6225     return strip.firstChild.firstChild.firstChild.firstChild;
6226 };
6227 /** @private */
6228 Roo.TabPanel.prototype.createBody = function(container){
6229     var body = document.createElement("div");
6230     Roo.id(body, "tab-body");
6231     Roo.fly(body).addClass("x-tabs-body");
6232     container.appendChild(body);
6233     return body;
6234 };
6235 /** @private */
6236 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6237     var body = Roo.getDom(id);
6238     if(!body){
6239         body = document.createElement("div");
6240         body.id = id;
6241     }
6242     Roo.fly(body).addClass("x-tabs-item-body");
6243     bodyEl.insertBefore(body, bodyEl.firstChild);
6244     return body;
6245 };
6246 /** @private */
6247 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6248     var td = document.createElement("td");
6249     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6250     //stripEl.appendChild(td);
6251     if(closable){
6252         td.className = "x-tabs-closable";
6253         if(!this.closeTpl){
6254             this.closeTpl = new Roo.Template(
6255                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6256                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6257                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6258             );
6259         }
6260         var el = this.closeTpl.overwrite(td, {"text": text});
6261         var close = el.getElementsByTagName("div")[0];
6262         var inner = el.getElementsByTagName("em")[0];
6263         return {"el": el, "close": close, "inner": inner};
6264     } else {
6265         if(!this.tabTpl){
6266             this.tabTpl = new Roo.Template(
6267                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6268                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6269             );
6270         }
6271         var el = this.tabTpl.overwrite(td, {"text": text});
6272         var inner = el.getElementsByTagName("em")[0];
6273         return {"el": el, "inner": inner};
6274     }
6275 };/*
6276  * Based on:
6277  * Ext JS Library 1.1.1
6278  * Copyright(c) 2006-2007, Ext JS, LLC.
6279  *
6280  * Originally Released Under LGPL - original licence link has changed is not relivant.
6281  *
6282  * Fork - LGPL
6283  * <script type="text/javascript">
6284  */
6285
6286 /**
6287  * @class Roo.Button
6288  * @extends Roo.util.Observable
6289  * Simple Button class
6290  * @cfg {String} text The button text
6291  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6292  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6293  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6294  * @cfg {Object} scope The scope of the handler
6295  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6296  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6297  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6298  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6299  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6300  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6301    applies if enableToggle = true)
6302  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6303  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6304   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6305  * @constructor
6306  * Create a new button
6307  * @param {Object} config The config object
6308  */
6309 Roo.Button = function(renderTo, config)
6310 {
6311     if (!config) {
6312         config = renderTo;
6313         renderTo = config.renderTo || false;
6314     }
6315     
6316     Roo.apply(this, config);
6317     this.addEvents({
6318         /**
6319              * @event click
6320              * Fires when this button is clicked
6321              * @param {Button} this
6322              * @param {EventObject} e The click event
6323              */
6324             "click" : true,
6325         /**
6326              * @event toggle
6327              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6328              * @param {Button} this
6329              * @param {Boolean} pressed
6330              */
6331             "toggle" : true,
6332         /**
6333              * @event mouseover
6334              * Fires when the mouse hovers over the button
6335              * @param {Button} this
6336              * @param {Event} e The event object
6337              */
6338         'mouseover' : true,
6339         /**
6340              * @event mouseout
6341              * Fires when the mouse exits the button
6342              * @param {Button} this
6343              * @param {Event} e The event object
6344              */
6345         'mouseout': true,
6346          /**
6347              * @event render
6348              * Fires when the button is rendered
6349              * @param {Button} this
6350              */
6351         'render': true
6352     });
6353     if(this.menu){
6354         this.menu = Roo.menu.MenuMgr.get(this.menu);
6355     }
6356     // register listeners first!!  - so render can be captured..
6357     Roo.util.Observable.call(this);
6358     if(renderTo){
6359         this.render(renderTo);
6360     }
6361     
6362   
6363 };
6364
6365 Roo.extend(Roo.Button, Roo.util.Observable, {
6366     /**
6367      * 
6368      */
6369     
6370     /**
6371      * Read-only. True if this button is hidden
6372      * @type Boolean
6373      */
6374     hidden : false,
6375     /**
6376      * Read-only. True if this button is disabled
6377      * @type Boolean
6378      */
6379     disabled : false,
6380     /**
6381      * Read-only. True if this button is pressed (only if enableToggle = true)
6382      * @type Boolean
6383      */
6384     pressed : false,
6385
6386     /**
6387      * @cfg {Number} tabIndex 
6388      * The DOM tabIndex for this button (defaults to undefined)
6389      */
6390     tabIndex : undefined,
6391
6392     /**
6393      * @cfg {Boolean} enableToggle
6394      * True to enable pressed/not pressed toggling (defaults to false)
6395      */
6396     enableToggle: false,
6397     /**
6398      * @cfg {Roo.menu.Menu} menu
6399      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6400      */
6401     menu : undefined,
6402     /**
6403      * @cfg {String} menuAlign
6404      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6405      */
6406     menuAlign : "tl-bl?",
6407
6408     /**
6409      * @cfg {String} iconCls
6410      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6411      */
6412     iconCls : undefined,
6413     /**
6414      * @cfg {String} type
6415      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6416      */
6417     type : 'button',
6418
6419     // private
6420     menuClassTarget: 'tr',
6421
6422     /**
6423      * @cfg {String} clickEvent
6424      * The type of event to map to the button's event handler (defaults to 'click')
6425      */
6426     clickEvent : 'click',
6427
6428     /**
6429      * @cfg {Boolean} handleMouseEvents
6430      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6431      */
6432     handleMouseEvents : true,
6433
6434     /**
6435      * @cfg {String} tooltipType
6436      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6437      */
6438     tooltipType : 'qtip',
6439
6440     /**
6441      * @cfg {String} cls
6442      * A CSS class to apply to the button's main element.
6443      */
6444     
6445     /**
6446      * @cfg {Roo.Template} template (Optional)
6447      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6448      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6449      * require code modifications if required elements (e.g. a button) aren't present.
6450      */
6451
6452     // private
6453     render : function(renderTo){
6454         var btn;
6455         if(this.hideParent){
6456             this.parentEl = Roo.get(renderTo);
6457         }
6458         if(!this.dhconfig){
6459             if(!this.template){
6460                 if(!Roo.Button.buttonTemplate){
6461                     // hideous table template
6462                     Roo.Button.buttonTemplate = new Roo.Template(
6463                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6464                         '<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>',
6465                         "</tr></tbody></table>");
6466                 }
6467                 this.template = Roo.Button.buttonTemplate;
6468             }
6469             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6470             var btnEl = btn.child("button:first");
6471             btnEl.on('focus', this.onFocus, this);
6472             btnEl.on('blur', this.onBlur, this);
6473             if(this.cls){
6474                 btn.addClass(this.cls);
6475             }
6476             if(this.icon){
6477                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6478             }
6479             if(this.iconCls){
6480                 btnEl.addClass(this.iconCls);
6481                 if(!this.cls){
6482                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6483                 }
6484             }
6485             if(this.tabIndex !== undefined){
6486                 btnEl.dom.tabIndex = this.tabIndex;
6487             }
6488             if(this.tooltip){
6489                 if(typeof this.tooltip == 'object'){
6490                     Roo.QuickTips.tips(Roo.apply({
6491                           target: btnEl.id
6492                     }, this.tooltip));
6493                 } else {
6494                     btnEl.dom[this.tooltipType] = this.tooltip;
6495                 }
6496             }
6497         }else{
6498             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6499         }
6500         this.el = btn;
6501         if(this.id){
6502             this.el.dom.id = this.el.id = this.id;
6503         }
6504         if(this.menu){
6505             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6506             this.menu.on("show", this.onMenuShow, this);
6507             this.menu.on("hide", this.onMenuHide, this);
6508         }
6509         btn.addClass("x-btn");
6510         if(Roo.isIE && !Roo.isIE7){
6511             this.autoWidth.defer(1, this);
6512         }else{
6513             this.autoWidth();
6514         }
6515         if(this.handleMouseEvents){
6516             btn.on("mouseover", this.onMouseOver, this);
6517             btn.on("mouseout", this.onMouseOut, this);
6518             btn.on("mousedown", this.onMouseDown, this);
6519         }
6520         btn.on(this.clickEvent, this.onClick, this);
6521         //btn.on("mouseup", this.onMouseUp, this);
6522         if(this.hidden){
6523             this.hide();
6524         }
6525         if(this.disabled){
6526             this.disable();
6527         }
6528         Roo.ButtonToggleMgr.register(this);
6529         if(this.pressed){
6530             this.el.addClass("x-btn-pressed");
6531         }
6532         if(this.repeat){
6533             var repeater = new Roo.util.ClickRepeater(btn,
6534                 typeof this.repeat == "object" ? this.repeat : {}
6535             );
6536             repeater.on("click", this.onClick,  this);
6537         }
6538         
6539         this.fireEvent('render', this);
6540         
6541     },
6542     /**
6543      * Returns the button's underlying element
6544      * @return {Roo.Element} The element
6545      */
6546     getEl : function(){
6547         return this.el;  
6548     },
6549     
6550     /**
6551      * Destroys this Button and removes any listeners.
6552      */
6553     destroy : function(){
6554         Roo.ButtonToggleMgr.unregister(this);
6555         this.el.removeAllListeners();
6556         this.purgeListeners();
6557         this.el.remove();
6558     },
6559
6560     // private
6561     autoWidth : function(){
6562         if(this.el){
6563             this.el.setWidth("auto");
6564             if(Roo.isIE7 && Roo.isStrict){
6565                 var ib = this.el.child('button');
6566                 if(ib && ib.getWidth() > 20){
6567                     ib.clip();
6568                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6569                 }
6570             }
6571             if(this.minWidth){
6572                 if(this.hidden){
6573                     this.el.beginMeasure();
6574                 }
6575                 if(this.el.getWidth() < this.minWidth){
6576                     this.el.setWidth(this.minWidth);
6577                 }
6578                 if(this.hidden){
6579                     this.el.endMeasure();
6580                 }
6581             }
6582         }
6583     },
6584
6585     /**
6586      * Assigns this button's click handler
6587      * @param {Function} handler The function to call when the button is clicked
6588      * @param {Object} scope (optional) Scope for the function passed in
6589      */
6590     setHandler : function(handler, scope){
6591         this.handler = handler;
6592         this.scope = scope;  
6593     },
6594     
6595     /**
6596      * Sets this button's text
6597      * @param {String} text The button text
6598      */
6599     setText : function(text){
6600         this.text = text;
6601         if(this.el){
6602             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6603         }
6604         this.autoWidth();
6605     },
6606     
6607     /**
6608      * Gets the text for this button
6609      * @return {String} The button text
6610      */
6611     getText : function(){
6612         return this.text;  
6613     },
6614     
6615     /**
6616      * Show this button
6617      */
6618     show: function(){
6619         this.hidden = false;
6620         if(this.el){
6621             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6622         }
6623     },
6624     
6625     /**
6626      * Hide this button
6627      */
6628     hide: function(){
6629         this.hidden = true;
6630         if(this.el){
6631             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6632         }
6633     },
6634     
6635     /**
6636      * Convenience function for boolean show/hide
6637      * @param {Boolean} visible True to show, false to hide
6638      */
6639     setVisible: function(visible){
6640         if(visible) {
6641             this.show();
6642         }else{
6643             this.hide();
6644         }
6645     },
6646     /**
6647          * Similar to toggle, but does not trigger event.
6648          * @param {Boolean} state [required] Force a particular state
6649          */
6650         setPressed : function(state)
6651         {
6652             if(state != this.pressed){
6653             if(state){
6654                 this.el.addClass("x-btn-pressed");
6655                 this.pressed = true;
6656             }else{
6657                 this.el.removeClass("x-btn-pressed");
6658                 this.pressed = false;
6659             }
6660         }
6661         },
6662         
6663     /**
6664      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6665      * @param {Boolean} state (optional) Force a particular state
6666      */
6667     toggle : function(state){
6668         state = state === undefined ? !this.pressed : state;
6669         if(state != this.pressed){
6670             if(state){
6671                 this.el.addClass("x-btn-pressed");
6672                 this.pressed = true;
6673                 this.fireEvent("toggle", this, true);
6674             }else{
6675                 this.el.removeClass("x-btn-pressed");
6676                 this.pressed = false;
6677                 this.fireEvent("toggle", this, false);
6678             }
6679             if(this.toggleHandler){
6680                 this.toggleHandler.call(this.scope || this, this, state);
6681             }
6682         }
6683     },
6684     
6685         
6686         
6687     /**
6688      * Focus the button
6689      */
6690     focus : function(){
6691         this.el.child('button:first').focus();
6692     },
6693     
6694     /**
6695      * Disable this button
6696      */
6697     disable : function(){
6698         if(this.el){
6699             this.el.addClass("x-btn-disabled");
6700         }
6701         this.disabled = true;
6702     },
6703     
6704     /**
6705      * Enable this button
6706      */
6707     enable : function(){
6708         if(this.el){
6709             this.el.removeClass("x-btn-disabled");
6710         }
6711         this.disabled = false;
6712     },
6713
6714     /**
6715      * Convenience function for boolean enable/disable
6716      * @param {Boolean} enabled True to enable, false to disable
6717      */
6718     setDisabled : function(v){
6719         this[v !== true ? "enable" : "disable"]();
6720     },
6721
6722     // private
6723     onClick : function(e)
6724     {
6725         if(e){
6726             e.preventDefault();
6727         }
6728         if(e.button != 0){
6729             return;
6730         }
6731         if(!this.disabled){
6732             if(this.enableToggle){
6733                 this.toggle();
6734             }
6735             if(this.menu && !this.menu.isVisible()){
6736                 this.menu.show(this.el, this.menuAlign);
6737             }
6738             this.fireEvent("click", this, e);
6739             if(this.handler){
6740                 this.el.removeClass("x-btn-over");
6741                 this.handler.call(this.scope || this, this, e);
6742             }
6743         }
6744     },
6745     // private
6746     onMouseOver : function(e){
6747         if(!this.disabled){
6748             this.el.addClass("x-btn-over");
6749             this.fireEvent('mouseover', this, e);
6750         }
6751     },
6752     // private
6753     onMouseOut : function(e){
6754         if(!e.within(this.el,  true)){
6755             this.el.removeClass("x-btn-over");
6756             this.fireEvent('mouseout', this, e);
6757         }
6758     },
6759     // private
6760     onFocus : function(e){
6761         if(!this.disabled){
6762             this.el.addClass("x-btn-focus");
6763         }
6764     },
6765     // private
6766     onBlur : function(e){
6767         this.el.removeClass("x-btn-focus");
6768     },
6769     // private
6770     onMouseDown : function(e){
6771         if(!this.disabled && e.button == 0){
6772             this.el.addClass("x-btn-click");
6773             Roo.get(document).on('mouseup', this.onMouseUp, this);
6774         }
6775     },
6776     // private
6777     onMouseUp : function(e){
6778         if(e.button == 0){
6779             this.el.removeClass("x-btn-click");
6780             Roo.get(document).un('mouseup', this.onMouseUp, this);
6781         }
6782     },
6783     // private
6784     onMenuShow : function(e){
6785         this.el.addClass("x-btn-menu-active");
6786     },
6787     // private
6788     onMenuHide : function(e){
6789         this.el.removeClass("x-btn-menu-active");
6790     }   
6791 });
6792
6793 // Private utility class used by Button
6794 Roo.ButtonToggleMgr = function(){
6795    var groups = {};
6796    
6797    function toggleGroup(btn, state){
6798        if(state){
6799            var g = groups[btn.toggleGroup];
6800            for(var i = 0, l = g.length; i < l; i++){
6801                if(g[i] != btn){
6802                    g[i].toggle(false);
6803                }
6804            }
6805        }
6806    }
6807    
6808    return {
6809        register : function(btn){
6810            if(!btn.toggleGroup){
6811                return;
6812            }
6813            var g = groups[btn.toggleGroup];
6814            if(!g){
6815                g = groups[btn.toggleGroup] = [];
6816            }
6817            g.push(btn);
6818            btn.on("toggle", toggleGroup);
6819        },
6820        
6821        unregister : function(btn){
6822            if(!btn.toggleGroup){
6823                return;
6824            }
6825            var g = groups[btn.toggleGroup];
6826            if(g){
6827                g.remove(btn);
6828                btn.un("toggle", toggleGroup);
6829            }
6830        }
6831    };
6832 }();/*
6833  * Based on:
6834  * Ext JS Library 1.1.1
6835  * Copyright(c) 2006-2007, Ext JS, LLC.
6836  *
6837  * Originally Released Under LGPL - original licence link has changed is not relivant.
6838  *
6839  * Fork - LGPL
6840  * <script type="text/javascript">
6841  */
6842  
6843 /**
6844  * @class Roo.SplitButton
6845  * @extends Roo.Button
6846  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6847  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6848  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6849  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6850  * @cfg {String} arrowTooltip The title attribute of the arrow
6851  * @constructor
6852  * Create a new menu button
6853  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6854  * @param {Object} config The config object
6855  */
6856 Roo.SplitButton = function(renderTo, config){
6857     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6858     /**
6859      * @event arrowclick
6860      * Fires when this button's arrow is clicked
6861      * @param {SplitButton} this
6862      * @param {EventObject} e The click event
6863      */
6864     this.addEvents({"arrowclick":true});
6865 };
6866
6867 Roo.extend(Roo.SplitButton, Roo.Button, {
6868     render : function(renderTo){
6869         // this is one sweet looking template!
6870         var tpl = new Roo.Template(
6871             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6872             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6873             '<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>',
6874             "</tbody></table></td><td>",
6875             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6876             '<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>',
6877             "</tbody></table></td></tr></table>"
6878         );
6879         var btn = tpl.append(renderTo, [this.text, this.type], true);
6880         var btnEl = btn.child("button");
6881         if(this.cls){
6882             btn.addClass(this.cls);
6883         }
6884         if(this.icon){
6885             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6886         }
6887         if(this.iconCls){
6888             btnEl.addClass(this.iconCls);
6889             if(!this.cls){
6890                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6891             }
6892         }
6893         this.el = btn;
6894         if(this.handleMouseEvents){
6895             btn.on("mouseover", this.onMouseOver, this);
6896             btn.on("mouseout", this.onMouseOut, this);
6897             btn.on("mousedown", this.onMouseDown, this);
6898             btn.on("mouseup", this.onMouseUp, this);
6899         }
6900         btn.on(this.clickEvent, this.onClick, this);
6901         if(this.tooltip){
6902             if(typeof this.tooltip == 'object'){
6903                 Roo.QuickTips.tips(Roo.apply({
6904                       target: btnEl.id
6905                 }, this.tooltip));
6906             } else {
6907                 btnEl.dom[this.tooltipType] = this.tooltip;
6908             }
6909         }
6910         if(this.arrowTooltip){
6911             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6912         }
6913         if(this.hidden){
6914             this.hide();
6915         }
6916         if(this.disabled){
6917             this.disable();
6918         }
6919         if(this.pressed){
6920             this.el.addClass("x-btn-pressed");
6921         }
6922         if(Roo.isIE && !Roo.isIE7){
6923             this.autoWidth.defer(1, this);
6924         }else{
6925             this.autoWidth();
6926         }
6927         if(this.menu){
6928             this.menu.on("show", this.onMenuShow, this);
6929             this.menu.on("hide", this.onMenuHide, this);
6930         }
6931         this.fireEvent('render', this);
6932     },
6933
6934     // private
6935     autoWidth : function(){
6936         if(this.el){
6937             var tbl = this.el.child("table:first");
6938             var tbl2 = this.el.child("table:last");
6939             this.el.setWidth("auto");
6940             tbl.setWidth("auto");
6941             if(Roo.isIE7 && Roo.isStrict){
6942                 var ib = this.el.child('button:first');
6943                 if(ib && ib.getWidth() > 20){
6944                     ib.clip();
6945                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6946                 }
6947             }
6948             if(this.minWidth){
6949                 if(this.hidden){
6950                     this.el.beginMeasure();
6951                 }
6952                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6953                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6954                 }
6955                 if(this.hidden){
6956                     this.el.endMeasure();
6957                 }
6958             }
6959             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6960         } 
6961     },
6962     /**
6963      * Sets this button's click handler
6964      * @param {Function} handler The function to call when the button is clicked
6965      * @param {Object} scope (optional) Scope for the function passed above
6966      */
6967     setHandler : function(handler, scope){
6968         this.handler = handler;
6969         this.scope = scope;  
6970     },
6971     
6972     /**
6973      * Sets this button's arrow click handler
6974      * @param {Function} handler The function to call when the arrow is clicked
6975      * @param {Object} scope (optional) Scope for the function passed above
6976      */
6977     setArrowHandler : function(handler, scope){
6978         this.arrowHandler = handler;
6979         this.scope = scope;  
6980     },
6981     
6982     /**
6983      * Focus the button
6984      */
6985     focus : function(){
6986         if(this.el){
6987             this.el.child("button:first").focus();
6988         }
6989     },
6990
6991     // private
6992     onClick : function(e){
6993         e.preventDefault();
6994         if(!this.disabled){
6995             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6996                 if(this.menu && !this.menu.isVisible()){
6997                     this.menu.show(this.el, this.menuAlign);
6998                 }
6999                 this.fireEvent("arrowclick", this, e);
7000                 if(this.arrowHandler){
7001                     this.arrowHandler.call(this.scope || this, this, e);
7002                 }
7003             }else{
7004                 this.fireEvent("click", this, e);
7005                 if(this.handler){
7006                     this.handler.call(this.scope || this, this, e);
7007                 }
7008             }
7009         }
7010     },
7011     // private
7012     onMouseDown : function(e){
7013         if(!this.disabled){
7014             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7015         }
7016     },
7017     // private
7018     onMouseUp : function(e){
7019         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7020     }   
7021 });
7022
7023
7024 // backwards compat
7025 Roo.MenuButton = Roo.SplitButton;/*
7026  * Based on:
7027  * Ext JS Library 1.1.1
7028  * Copyright(c) 2006-2007, Ext JS, LLC.
7029  *
7030  * Originally Released Under LGPL - original licence link has changed is not relivant.
7031  *
7032  * Fork - LGPL
7033  * <script type="text/javascript">
7034  */
7035
7036 /**
7037  * @class Roo.Toolbar
7038  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7039  * Basic Toolbar class.
7040  * @constructor
7041  * Creates a new Toolbar
7042  * @param {Object} container The config object
7043  */ 
7044 Roo.Toolbar = function(container, buttons, config)
7045 {
7046     /// old consturctor format still supported..
7047     if(container instanceof Array){ // omit the container for later rendering
7048         buttons = container;
7049         config = buttons;
7050         container = null;
7051     }
7052     if (typeof(container) == 'object' && container.xtype) {
7053         config = container;
7054         container = config.container;
7055         buttons = config.buttons || []; // not really - use items!!
7056     }
7057     var xitems = [];
7058     if (config && config.items) {
7059         xitems = config.items;
7060         delete config.items;
7061     }
7062     Roo.apply(this, config);
7063     this.buttons = buttons;
7064     
7065     if(container){
7066         this.render(container);
7067     }
7068     this.xitems = xitems;
7069     Roo.each(xitems, function(b) {
7070         this.add(b);
7071     }, this);
7072     
7073 };
7074
7075 Roo.Toolbar.prototype = {
7076     /**
7077      * @cfg {Array} items
7078      * array of button configs or elements to add (will be converted to a MixedCollection)
7079      */
7080     items: false,
7081     /**
7082      * @cfg {String/HTMLElement/Element} container
7083      * The id or element that will contain the toolbar
7084      */
7085     // private
7086     render : function(ct){
7087         this.el = Roo.get(ct);
7088         if(this.cls){
7089             this.el.addClass(this.cls);
7090         }
7091         // using a table allows for vertical alignment
7092         // 100% width is needed by Safari...
7093         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7094         this.tr = this.el.child("tr", true);
7095         var autoId = 0;
7096         this.items = new Roo.util.MixedCollection(false, function(o){
7097             return o.id || ("item" + (++autoId));
7098         });
7099         if(this.buttons){
7100             this.add.apply(this, this.buttons);
7101             delete this.buttons;
7102         }
7103     },
7104
7105     /**
7106      * Adds element(s) to the toolbar -- this function takes a variable number of 
7107      * arguments of mixed type and adds them to the toolbar.
7108      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7109      * <ul>
7110      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7111      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7112      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7113      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7114      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7115      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7116      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7117      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7118      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7119      * </ul>
7120      * @param {Mixed} arg2
7121      * @param {Mixed} etc.
7122      */
7123     add : function(){
7124         var a = arguments, l = a.length;
7125         for(var i = 0; i < l; i++){
7126             this._add(a[i]);
7127         }
7128     },
7129     // private..
7130     _add : function(el) {
7131         
7132         if (el.xtype) {
7133             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7134         }
7135         
7136         if (el.applyTo){ // some kind of form field
7137             return this.addField(el);
7138         } 
7139         if (el.render){ // some kind of Toolbar.Item
7140             return this.addItem(el);
7141         }
7142         if (typeof el == "string"){ // string
7143             if(el == "separator" || el == "-"){
7144                 return this.addSeparator();
7145             }
7146             if (el == " "){
7147                 return this.addSpacer();
7148             }
7149             if(el == "->"){
7150                 return this.addFill();
7151             }
7152             return this.addText(el);
7153             
7154         }
7155         if(el.tagName){ // element
7156             return this.addElement(el);
7157         }
7158         if(typeof el == "object"){ // must be button config?
7159             return this.addButton(el);
7160         }
7161         // and now what?!?!
7162         return false;
7163         
7164     },
7165     
7166     /**
7167      * Add an Xtype element
7168      * @param {Object} xtype Xtype Object
7169      * @return {Object} created Object
7170      */
7171     addxtype : function(e){
7172         return this.add(e);  
7173     },
7174     
7175     /**
7176      * Returns the Element for this toolbar.
7177      * @return {Roo.Element}
7178      */
7179     getEl : function(){
7180         return this.el;  
7181     },
7182     
7183     /**
7184      * Adds a separator
7185      * @return {Roo.Toolbar.Item} The separator item
7186      */
7187     addSeparator : function(){
7188         return this.addItem(new Roo.Toolbar.Separator());
7189     },
7190
7191     /**
7192      * Adds a spacer element
7193      * @return {Roo.Toolbar.Spacer} The spacer item
7194      */
7195     addSpacer : function(){
7196         return this.addItem(new Roo.Toolbar.Spacer());
7197     },
7198
7199     /**
7200      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7201      * @return {Roo.Toolbar.Fill} The fill item
7202      */
7203     addFill : function(){
7204         return this.addItem(new Roo.Toolbar.Fill());
7205     },
7206
7207     /**
7208      * Adds any standard HTML element to the toolbar
7209      * @param {String/HTMLElement/Element} el The element or id of the element to add
7210      * @return {Roo.Toolbar.Item} The element's item
7211      */
7212     addElement : function(el){
7213         return this.addItem(new Roo.Toolbar.Item(el));
7214     },
7215     /**
7216      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7217      * @type Roo.util.MixedCollection  
7218      */
7219     items : false,
7220      
7221     /**
7222      * Adds any Toolbar.Item or subclass
7223      * @param {Roo.Toolbar.Item} item
7224      * @return {Roo.Toolbar.Item} The item
7225      */
7226     addItem : function(item){
7227         var td = this.nextBlock();
7228         item.render(td);
7229         this.items.add(item);
7230         return item;
7231     },
7232     
7233     /**
7234      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7235      * @param {Object/Array} config A button config or array of configs
7236      * @return {Roo.Toolbar.Button/Array}
7237      */
7238     addButton : function(config){
7239         if(config instanceof Array){
7240             var buttons = [];
7241             for(var i = 0, len = config.length; i < len; i++) {
7242                 buttons.push(this.addButton(config[i]));
7243             }
7244             return buttons;
7245         }
7246         var b = config;
7247         if(!(config instanceof Roo.Toolbar.Button)){
7248             b = config.split ?
7249                 new Roo.Toolbar.SplitButton(config) :
7250                 new Roo.Toolbar.Button(config);
7251         }
7252         var td = this.nextBlock();
7253         b.render(td);
7254         this.items.add(b);
7255         return b;
7256     },
7257     
7258     /**
7259      * Adds text to the toolbar
7260      * @param {String} text The text to add
7261      * @return {Roo.Toolbar.Item} The element's item
7262      */
7263     addText : function(text){
7264         return this.addItem(new Roo.Toolbar.TextItem(text));
7265     },
7266     
7267     /**
7268      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7269      * @param {Number} index The index where the item is to be inserted
7270      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7271      * @return {Roo.Toolbar.Button/Item}
7272      */
7273     insertButton : function(index, item){
7274         if(item instanceof Array){
7275             var buttons = [];
7276             for(var i = 0, len = item.length; i < len; i++) {
7277                buttons.push(this.insertButton(index + i, item[i]));
7278             }
7279             return buttons;
7280         }
7281         if (!(item instanceof Roo.Toolbar.Button)){
7282            item = new Roo.Toolbar.Button(item);
7283         }
7284         var td = document.createElement("td");
7285         this.tr.insertBefore(td, this.tr.childNodes[index]);
7286         item.render(td);
7287         this.items.insert(index, item);
7288         return item;
7289     },
7290     
7291     /**
7292      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7293      * @param {Object} config
7294      * @return {Roo.Toolbar.Item} The element's item
7295      */
7296     addDom : function(config, returnEl){
7297         var td = this.nextBlock();
7298         Roo.DomHelper.overwrite(td, config);
7299         var ti = new Roo.Toolbar.Item(td.firstChild);
7300         ti.render(td);
7301         this.items.add(ti);
7302         return ti;
7303     },
7304
7305     /**
7306      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7307      * @type Roo.util.MixedCollection  
7308      */
7309     fields : false,
7310     
7311     /**
7312      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7313      * Note: the field should not have been rendered yet. For a field that has already been
7314      * rendered, use {@link #addElement}.
7315      * @param {Roo.form.Field} field
7316      * @return {Roo.ToolbarItem}
7317      */
7318      
7319       
7320     addField : function(field) {
7321         if (!this.fields) {
7322             var autoId = 0;
7323             this.fields = new Roo.util.MixedCollection(false, function(o){
7324                 return o.id || ("item" + (++autoId));
7325             });
7326
7327         }
7328         
7329         var td = this.nextBlock();
7330         field.render(td);
7331         var ti = new Roo.Toolbar.Item(td.firstChild);
7332         ti.render(td);
7333         this.items.add(ti);
7334         this.fields.add(field);
7335         return ti;
7336     },
7337     /**
7338      * Hide the toolbar
7339      * @method hide
7340      */
7341      
7342       
7343     hide : function()
7344     {
7345         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7346         this.el.child('div').hide();
7347     },
7348     /**
7349      * Show the toolbar
7350      * @method show
7351      */
7352     show : function()
7353     {
7354         this.el.child('div').show();
7355     },
7356       
7357     // private
7358     nextBlock : function(){
7359         var td = document.createElement("td");
7360         this.tr.appendChild(td);
7361         return td;
7362     },
7363
7364     // private
7365     destroy : function(){
7366         if(this.items){ // rendered?
7367             Roo.destroy.apply(Roo, this.items.items);
7368         }
7369         if(this.fields){ // rendered?
7370             Roo.destroy.apply(Roo, this.fields.items);
7371         }
7372         Roo.Element.uncache(this.el, this.tr);
7373     }
7374 };
7375
7376 /**
7377  * @class Roo.Toolbar.Item
7378  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7379  * @constructor
7380  * Creates a new Item
7381  * @param {HTMLElement} el 
7382  */
7383 Roo.Toolbar.Item = function(el){
7384     var cfg = {};
7385     if (typeof (el.xtype) != 'undefined') {
7386         cfg = el;
7387         el = cfg.el;
7388     }
7389     
7390     this.el = Roo.getDom(el);
7391     this.id = Roo.id(this.el);
7392     this.hidden = false;
7393     
7394     this.addEvents({
7395          /**
7396              * @event render
7397              * Fires when the button is rendered
7398              * @param {Button} this
7399              */
7400         'render': true
7401     });
7402     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7403 };
7404 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7405 //Roo.Toolbar.Item.prototype = {
7406     
7407     /**
7408      * Get this item's HTML Element
7409      * @return {HTMLElement}
7410      */
7411     getEl : function(){
7412        return this.el;  
7413     },
7414
7415     // private
7416     render : function(td){
7417         
7418          this.td = td;
7419         td.appendChild(this.el);
7420         
7421         this.fireEvent('render', this);
7422     },
7423     
7424     /**
7425      * Removes and destroys this item.
7426      */
7427     destroy : function(){
7428         this.td.parentNode.removeChild(this.td);
7429     },
7430     
7431     /**
7432      * Shows this item.
7433      */
7434     show: function(){
7435         this.hidden = false;
7436         this.td.style.display = "";
7437     },
7438     
7439     /**
7440      * Hides this item.
7441      */
7442     hide: function(){
7443         this.hidden = true;
7444         this.td.style.display = "none";
7445     },
7446     
7447     /**
7448      * Convenience function for boolean show/hide.
7449      * @param {Boolean} visible true to show/false to hide
7450      */
7451     setVisible: function(visible){
7452         if(visible) {
7453             this.show();
7454         }else{
7455             this.hide();
7456         }
7457     },
7458     
7459     /**
7460      * Try to focus this item.
7461      */
7462     focus : function(){
7463         Roo.fly(this.el).focus();
7464     },
7465     
7466     /**
7467      * Disables this item.
7468      */
7469     disable : function(){
7470         Roo.fly(this.td).addClass("x-item-disabled");
7471         this.disabled = true;
7472         this.el.disabled = true;
7473     },
7474     
7475     /**
7476      * Enables this item.
7477      */
7478     enable : function(){
7479         Roo.fly(this.td).removeClass("x-item-disabled");
7480         this.disabled = false;
7481         this.el.disabled = false;
7482     }
7483 });
7484
7485
7486 /**
7487  * @class Roo.Toolbar.Separator
7488  * @extends Roo.Toolbar.Item
7489  * A simple toolbar separator class
7490  * @constructor
7491  * Creates a new Separator
7492  */
7493 Roo.Toolbar.Separator = function(cfg){
7494     
7495     var s = document.createElement("span");
7496     s.className = "ytb-sep";
7497     if (cfg) {
7498         cfg.el = s;
7499     }
7500     
7501     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7502 };
7503 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7504     enable:Roo.emptyFn,
7505     disable:Roo.emptyFn,
7506     focus:Roo.emptyFn
7507 });
7508
7509 /**
7510  * @class Roo.Toolbar.Spacer
7511  * @extends Roo.Toolbar.Item
7512  * A simple element that adds extra horizontal space to a toolbar.
7513  * @constructor
7514  * Creates a new Spacer
7515  */
7516 Roo.Toolbar.Spacer = function(cfg){
7517     var s = document.createElement("div");
7518     s.className = "ytb-spacer";
7519     if (cfg) {
7520         cfg.el = s;
7521     }
7522     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7523 };
7524 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7525     enable:Roo.emptyFn,
7526     disable:Roo.emptyFn,
7527     focus:Roo.emptyFn
7528 });
7529
7530 /**
7531  * @class Roo.Toolbar.Fill
7532  * @extends Roo.Toolbar.Spacer
7533  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7534  * @constructor
7535  * Creates a new Spacer
7536  */
7537 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7538     // private
7539     render : function(td){
7540         td.style.width = '100%';
7541         Roo.Toolbar.Fill.superclass.render.call(this, td);
7542     }
7543 });
7544
7545 /**
7546  * @class Roo.Toolbar.TextItem
7547  * @extends Roo.Toolbar.Item
7548  * A simple class that renders text directly into a toolbar.
7549  * @constructor
7550  * Creates a new TextItem
7551  * @cfg {string} text 
7552  */
7553 Roo.Toolbar.TextItem = function(cfg){
7554     var  text = cfg || "";
7555     if (typeof(cfg) == 'object') {
7556         text = cfg.text || "";
7557     }  else {
7558         cfg = null;
7559     }
7560     var s = document.createElement("span");
7561     s.className = "ytb-text";
7562     s.innerHTML = text;
7563     if (cfg) {
7564         cfg.el  = s;
7565     }
7566     
7567     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7568 };
7569 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7570     
7571      
7572     enable:Roo.emptyFn,
7573     disable:Roo.emptyFn,
7574     focus:Roo.emptyFn,
7575      /**
7576      * Shows this button
7577      */
7578     show: function(){
7579         this.hidden = false;
7580         this.el.style.display = "";
7581     },
7582     
7583     /**
7584      * Hides this button
7585      */
7586     hide: function(){
7587         this.hidden = true;
7588         this.el.style.display = "none";
7589     }
7590     
7591 });
7592
7593 /**
7594  * @class Roo.Toolbar.Button
7595  * @extends Roo.Button
7596  * A button that renders into a toolbar.
7597  * @constructor
7598  * Creates a new Button
7599  * @param {Object} config A standard {@link Roo.Button} config object
7600  */
7601 Roo.Toolbar.Button = function(config){
7602     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7603 };
7604 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7605 {
7606     
7607     
7608     render : function(td){
7609         this.td = td;
7610         Roo.Toolbar.Button.superclass.render.call(this, td);
7611     },
7612     
7613     /**
7614      * Removes and destroys this button
7615      */
7616     destroy : function(){
7617         Roo.Toolbar.Button.superclass.destroy.call(this);
7618         this.td.parentNode.removeChild(this.td);
7619     },
7620     
7621     /**
7622      * Shows this button
7623      */
7624     show: function(){
7625         this.hidden = false;
7626         this.td.style.display = "";
7627     },
7628     
7629     /**
7630      * Hides this button
7631      */
7632     hide: function(){
7633         this.hidden = true;
7634         this.td.style.display = "none";
7635     },
7636
7637     /**
7638      * Disables this item
7639      */
7640     disable : function(){
7641         Roo.fly(this.td).addClass("x-item-disabled");
7642         this.disabled = true;
7643     },
7644
7645     /**
7646      * Enables this item
7647      */
7648     enable : function(){
7649         Roo.fly(this.td).removeClass("x-item-disabled");
7650         this.disabled = false;
7651     }
7652 });
7653 // backwards compat
7654 Roo.ToolbarButton = Roo.Toolbar.Button;
7655
7656 /**
7657  * @class Roo.Toolbar.SplitButton
7658  * @extends Roo.SplitButton
7659  * A menu button that renders into a toolbar.
7660  * @constructor
7661  * Creates a new SplitButton
7662  * @param {Object} config A standard {@link Roo.SplitButton} config object
7663  */
7664 Roo.Toolbar.SplitButton = function(config){
7665     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7666 };
7667 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7668     render : function(td){
7669         this.td = td;
7670         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7671     },
7672     
7673     /**
7674      * Removes and destroys this button
7675      */
7676     destroy : function(){
7677         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7678         this.td.parentNode.removeChild(this.td);
7679     },
7680     
7681     /**
7682      * Shows this button
7683      */
7684     show: function(){
7685         this.hidden = false;
7686         this.td.style.display = "";
7687     },
7688     
7689     /**
7690      * Hides this button
7691      */
7692     hide: function(){
7693         this.hidden = true;
7694         this.td.style.display = "none";
7695     }
7696 });
7697
7698 // backwards compat
7699 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7700  * Based on:
7701  * Ext JS Library 1.1.1
7702  * Copyright(c) 2006-2007, Ext JS, LLC.
7703  *
7704  * Originally Released Under LGPL - original licence link has changed is not relivant.
7705  *
7706  * Fork - LGPL
7707  * <script type="text/javascript">
7708  */
7709  
7710 /**
7711  * @class Roo.PagingToolbar
7712  * @extends Roo.Toolbar
7713  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7714  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7715  * @constructor
7716  * Create a new PagingToolbar
7717  * @param {Object} config The config object
7718  */
7719 Roo.PagingToolbar = function(el, ds, config)
7720 {
7721     // old args format still supported... - xtype is prefered..
7722     if (typeof(el) == 'object' && el.xtype) {
7723         // created from xtype...
7724         config = el;
7725         ds = el.dataSource;
7726         el = config.container;
7727     }
7728     var items = [];
7729     if (config.items) {
7730         items = config.items;
7731         config.items = [];
7732     }
7733     
7734     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7735     this.ds = ds;
7736     this.cursor = 0;
7737     this.renderButtons(this.el);
7738     this.bind(ds);
7739     
7740     // supprot items array.
7741    
7742     Roo.each(items, function(e) {
7743         this.add(Roo.factory(e));
7744     },this);
7745     
7746 };
7747
7748 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7749    
7750     /**
7751      * @cfg {String/HTMLElement/Element} container
7752      * container The id or element that will contain the toolbar
7753      */
7754     /**
7755      * @cfg {Boolean} displayInfo
7756      * True to display the displayMsg (defaults to false)
7757      */
7758     
7759     
7760     /**
7761      * @cfg {Number} pageSize
7762      * The number of records to display per page (defaults to 20)
7763      */
7764     pageSize: 20,
7765     /**
7766      * @cfg {String} displayMsg
7767      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7768      */
7769     displayMsg : 'Displaying {0} - {1} of {2}',
7770     /**
7771      * @cfg {String} emptyMsg
7772      * The message to display when no records are found (defaults to "No data to display")
7773      */
7774     emptyMsg : 'No data to display',
7775     /**
7776      * Customizable piece of the default paging text (defaults to "Page")
7777      * @type String
7778      */
7779     beforePageText : "Page",
7780     /**
7781      * Customizable piece of the default paging text (defaults to "of %0")
7782      * @type String
7783      */
7784     afterPageText : "of {0}",
7785     /**
7786      * Customizable piece of the default paging text (defaults to "First Page")
7787      * @type String
7788      */
7789     firstText : "First Page",
7790     /**
7791      * Customizable piece of the default paging text (defaults to "Previous Page")
7792      * @type String
7793      */
7794     prevText : "Previous Page",
7795     /**
7796      * Customizable piece of the default paging text (defaults to "Next Page")
7797      * @type String
7798      */
7799     nextText : "Next Page",
7800     /**
7801      * Customizable piece of the default paging text (defaults to "Last Page")
7802      * @type String
7803      */
7804     lastText : "Last Page",
7805     /**
7806      * Customizable piece of the default paging text (defaults to "Refresh")
7807      * @type String
7808      */
7809     refreshText : "Refresh",
7810
7811     // private
7812     renderButtons : function(el){
7813         Roo.PagingToolbar.superclass.render.call(this, el);
7814         this.first = this.addButton({
7815             tooltip: this.firstText,
7816             cls: "x-btn-icon x-grid-page-first",
7817             disabled: true,
7818             handler: this.onClick.createDelegate(this, ["first"])
7819         });
7820         this.prev = this.addButton({
7821             tooltip: this.prevText,
7822             cls: "x-btn-icon x-grid-page-prev",
7823             disabled: true,
7824             handler: this.onClick.createDelegate(this, ["prev"])
7825         });
7826         //this.addSeparator();
7827         this.add(this.beforePageText);
7828         this.field = Roo.get(this.addDom({
7829            tag: "input",
7830            type: "text",
7831            size: "3",
7832            value: "1",
7833            cls: "x-grid-page-number"
7834         }).el);
7835         this.field.on("keydown", this.onPagingKeydown, this);
7836         this.field.on("focus", function(){this.dom.select();});
7837         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7838         this.field.setHeight(18);
7839         //this.addSeparator();
7840         this.next = this.addButton({
7841             tooltip: this.nextText,
7842             cls: "x-btn-icon x-grid-page-next",
7843             disabled: true,
7844             handler: this.onClick.createDelegate(this, ["next"])
7845         });
7846         this.last = this.addButton({
7847             tooltip: this.lastText,
7848             cls: "x-btn-icon x-grid-page-last",
7849             disabled: true,
7850             handler: this.onClick.createDelegate(this, ["last"])
7851         });
7852         //this.addSeparator();
7853         this.loading = this.addButton({
7854             tooltip: this.refreshText,
7855             cls: "x-btn-icon x-grid-loading",
7856             handler: this.onClick.createDelegate(this, ["refresh"])
7857         });
7858
7859         if(this.displayInfo){
7860             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7861         }
7862     },
7863
7864     // private
7865     updateInfo : function(){
7866         if(this.displayEl){
7867             var count = this.ds.getCount();
7868             var msg = count == 0 ?
7869                 this.emptyMsg :
7870                 String.format(
7871                     this.displayMsg,
7872                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7873                 );
7874             this.displayEl.update(msg);
7875         }
7876     },
7877
7878     // private
7879     onLoad : function(ds, r, o){
7880        this.cursor = o.params ? o.params.start : 0;
7881        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7882
7883        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7884        this.field.dom.value = ap;
7885        this.first.setDisabled(ap == 1);
7886        this.prev.setDisabled(ap == 1);
7887        this.next.setDisabled(ap == ps);
7888        this.last.setDisabled(ap == ps);
7889        this.loading.enable();
7890        this.updateInfo();
7891     },
7892
7893     // private
7894     getPageData : function(){
7895         var total = this.ds.getTotalCount();
7896         return {
7897             total : total,
7898             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7899             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7900         };
7901     },
7902
7903     // private
7904     onLoadError : function(){
7905         this.loading.enable();
7906     },
7907
7908     // private
7909     onPagingKeydown : function(e){
7910         var k = e.getKey();
7911         var d = this.getPageData();
7912         if(k == e.RETURN){
7913             var v = this.field.dom.value, pageNum;
7914             if(!v || isNaN(pageNum = parseInt(v, 10))){
7915                 this.field.dom.value = d.activePage;
7916                 return;
7917             }
7918             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7919             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7920             e.stopEvent();
7921         }
7922         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))
7923         {
7924           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7925           this.field.dom.value = pageNum;
7926           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7927           e.stopEvent();
7928         }
7929         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7930         {
7931           var v = this.field.dom.value, pageNum; 
7932           var increment = (e.shiftKey) ? 10 : 1;
7933           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7934             increment *= -1;
7935           }
7936           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7937             this.field.dom.value = d.activePage;
7938             return;
7939           }
7940           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7941           {
7942             this.field.dom.value = parseInt(v, 10) + increment;
7943             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7944             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7945           }
7946           e.stopEvent();
7947         }
7948     },
7949
7950     // private
7951     beforeLoad : function(){
7952         if(this.loading){
7953             this.loading.disable();
7954         }
7955     },
7956     /**
7957      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7958      * @param {String} which (first|prev|next|last|refresh)  which button to press.
7959      *
7960      */
7961     // private
7962     onClick : function(which){
7963         var ds = this.ds;
7964         switch(which){
7965             case "first":
7966                 ds.load({params:{start: 0, limit: this.pageSize}});
7967             break;
7968             case "prev":
7969                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7970             break;
7971             case "next":
7972                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7973             break;
7974             case "last":
7975                 var total = ds.getTotalCount();
7976                 var extra = total % this.pageSize;
7977                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7978                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7979             break;
7980             case "refresh":
7981                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7982             break;
7983         }
7984     },
7985
7986     /**
7987      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7988      * @param {Roo.data.Store} store The data store to unbind
7989      */
7990     unbind : function(ds){
7991         ds.un("beforeload", this.beforeLoad, this);
7992         ds.un("load", this.onLoad, this);
7993         ds.un("loadexception", this.onLoadError, this);
7994         ds.un("remove", this.updateInfo, this);
7995         ds.un("add", this.updateInfo, this);
7996         this.ds = undefined;
7997     },
7998
7999     /**
8000      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8001      * @param {Roo.data.Store} store The data store to bind
8002      */
8003     bind : function(ds){
8004         ds.on("beforeload", this.beforeLoad, this);
8005         ds.on("load", this.onLoad, this);
8006         ds.on("loadexception", this.onLoadError, this);
8007         ds.on("remove", this.updateInfo, this);
8008         ds.on("add", this.updateInfo, this);
8009         this.ds = ds;
8010     }
8011 });/*
8012  * Based on:
8013  * Ext JS Library 1.1.1
8014  * Copyright(c) 2006-2007, Ext JS, LLC.
8015  *
8016  * Originally Released Under LGPL - original licence link has changed is not relivant.
8017  *
8018  * Fork - LGPL
8019  * <script type="text/javascript">
8020  */
8021
8022 /**
8023  * @class Roo.Resizable
8024  * @extends Roo.util.Observable
8025  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8026  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8027  * 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
8028  * the element will be wrapped for you automatically.</p>
8029  * <p>Here is the list of valid resize handles:</p>
8030  * <pre>
8031 Value   Description
8032 ------  -------------------
8033  'n'     north
8034  's'     south
8035  'e'     east
8036  'w'     west
8037  'nw'    northwest
8038  'sw'    southwest
8039  'se'    southeast
8040  'ne'    northeast
8041  'hd'    horizontal drag
8042  'all'   all
8043 </pre>
8044  * <p>Here's an example showing the creation of a typical Resizable:</p>
8045  * <pre><code>
8046 var resizer = new Roo.Resizable("element-id", {
8047     handles: 'all',
8048     minWidth: 200,
8049     minHeight: 100,
8050     maxWidth: 500,
8051     maxHeight: 400,
8052     pinned: true
8053 });
8054 resizer.on("resize", myHandler);
8055 </code></pre>
8056  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8057  * resizer.east.setDisplayed(false);</p>
8058  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8059  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8060  * resize operation's new size (defaults to [0, 0])
8061  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8062  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8063  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8064  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8065  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8066  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8067  * @cfg {Number} width The width of the element in pixels (defaults to null)
8068  * @cfg {Number} height The height of the element in pixels (defaults to null)
8069  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8070  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8071  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8072  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8073  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8074  * in favor of the handles config option (defaults to false)
8075  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8076  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8077  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8078  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8079  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8080  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8081  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8082  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8083  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8084  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8085  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8086  * @constructor
8087  * Create a new resizable component
8088  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8089  * @param {Object} config configuration options
8090   */
8091 Roo.Resizable = function(el, config)
8092 {
8093     this.el = Roo.get(el);
8094
8095     if(config && config.wrap){
8096         config.resizeChild = this.el;
8097         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8098         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8099         this.el.setStyle("overflow", "hidden");
8100         this.el.setPositioning(config.resizeChild.getPositioning());
8101         config.resizeChild.clearPositioning();
8102         if(!config.width || !config.height){
8103             var csize = config.resizeChild.getSize();
8104             this.el.setSize(csize.width, csize.height);
8105         }
8106         if(config.pinned && !config.adjustments){
8107             config.adjustments = "auto";
8108         }
8109     }
8110
8111     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8112     this.proxy.unselectable();
8113     this.proxy.enableDisplayMode('block');
8114
8115     Roo.apply(this, config);
8116
8117     if(this.pinned){
8118         this.disableTrackOver = true;
8119         this.el.addClass("x-resizable-pinned");
8120     }
8121     // if the element isn't positioned, make it relative
8122     var position = this.el.getStyle("position");
8123     if(position != "absolute" && position != "fixed"){
8124         this.el.setStyle("position", "relative");
8125     }
8126     if(!this.handles){ // no handles passed, must be legacy style
8127         this.handles = 's,e,se';
8128         if(this.multiDirectional){
8129             this.handles += ',n,w';
8130         }
8131     }
8132     if(this.handles == "all"){
8133         this.handles = "n s e w ne nw se sw";
8134     }
8135     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8136     var ps = Roo.Resizable.positions;
8137     for(var i = 0, len = hs.length; i < len; i++){
8138         if(hs[i] && ps[hs[i]]){
8139             var pos = ps[hs[i]];
8140             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8141         }
8142     }
8143     // legacy
8144     this.corner = this.southeast;
8145     
8146     // updateBox = the box can move..
8147     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8148         this.updateBox = true;
8149     }
8150
8151     this.activeHandle = null;
8152
8153     if(this.resizeChild){
8154         if(typeof this.resizeChild == "boolean"){
8155             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8156         }else{
8157             this.resizeChild = Roo.get(this.resizeChild, true);
8158         }
8159     }
8160     
8161     if(this.adjustments == "auto"){
8162         var rc = this.resizeChild;
8163         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8164         if(rc && (hw || hn)){
8165             rc.position("relative");
8166             rc.setLeft(hw ? hw.el.getWidth() : 0);
8167             rc.setTop(hn ? hn.el.getHeight() : 0);
8168         }
8169         this.adjustments = [
8170             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8171             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8172         ];
8173     }
8174
8175     if(this.draggable){
8176         this.dd = this.dynamic ?
8177             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8178         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8179     }
8180
8181     // public events
8182     this.addEvents({
8183         /**
8184          * @event beforeresize
8185          * Fired before resize is allowed. Set enabled to false to cancel resize.
8186          * @param {Roo.Resizable} this
8187          * @param {Roo.EventObject} e The mousedown event
8188          */
8189         "beforeresize" : true,
8190         /**
8191          * @event resizing
8192          * Fired a resizing.
8193          * @param {Roo.Resizable} this
8194          * @param {Number} x The new x position
8195          * @param {Number} y The new y position
8196          * @param {Number} w The new w width
8197          * @param {Number} h The new h hight
8198          * @param {Roo.EventObject} e The mouseup event
8199          */
8200         "resizing" : true,
8201         /**
8202          * @event resize
8203          * Fired after a resize.
8204          * @param {Roo.Resizable} this
8205          * @param {Number} width The new width
8206          * @param {Number} height The new height
8207          * @param {Roo.EventObject} e The mouseup event
8208          */
8209         "resize" : true
8210     });
8211
8212     if(this.width !== null && this.height !== null){
8213         this.resizeTo(this.width, this.height);
8214     }else{
8215         this.updateChildSize();
8216     }
8217     if(Roo.isIE){
8218         this.el.dom.style.zoom = 1;
8219     }
8220     Roo.Resizable.superclass.constructor.call(this);
8221 };
8222
8223 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8224         resizeChild : false,
8225         adjustments : [0, 0],
8226         minWidth : 5,
8227         minHeight : 5,
8228         maxWidth : 10000,
8229         maxHeight : 10000,
8230         enabled : true,
8231         animate : false,
8232         duration : .35,
8233         dynamic : false,
8234         handles : false,
8235         multiDirectional : false,
8236         disableTrackOver : false,
8237         easing : 'easeOutStrong',
8238         widthIncrement : 0,
8239         heightIncrement : 0,
8240         pinned : false,
8241         width : null,
8242         height : null,
8243         preserveRatio : false,
8244         transparent: false,
8245         minX: 0,
8246         minY: 0,
8247         draggable: false,
8248
8249         /**
8250          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8251          */
8252         constrainTo: undefined,
8253         /**
8254          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8255          */
8256         resizeRegion: undefined,
8257
8258
8259     /**
8260      * Perform a manual resize
8261      * @param {Number} width
8262      * @param {Number} height
8263      */
8264     resizeTo : function(width, height){
8265         this.el.setSize(width, height);
8266         this.updateChildSize();
8267         this.fireEvent("resize", this, width, height, null);
8268     },
8269
8270     // private
8271     startSizing : function(e, handle){
8272         this.fireEvent("beforeresize", this, e);
8273         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8274
8275             if(!this.overlay){
8276                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8277                 this.overlay.unselectable();
8278                 this.overlay.enableDisplayMode("block");
8279                 this.overlay.on("mousemove", this.onMouseMove, this);
8280                 this.overlay.on("mouseup", this.onMouseUp, this);
8281             }
8282             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8283
8284             this.resizing = true;
8285             this.startBox = this.el.getBox();
8286             this.startPoint = e.getXY();
8287             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8288                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8289
8290             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8291             this.overlay.show();
8292
8293             if(this.constrainTo) {
8294                 var ct = Roo.get(this.constrainTo);
8295                 this.resizeRegion = ct.getRegion().adjust(
8296                     ct.getFrameWidth('t'),
8297                     ct.getFrameWidth('l'),
8298                     -ct.getFrameWidth('b'),
8299                     -ct.getFrameWidth('r')
8300                 );
8301             }
8302
8303             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8304             this.proxy.show();
8305             this.proxy.setBox(this.startBox);
8306             if(!this.dynamic){
8307                 this.proxy.setStyle('visibility', 'visible');
8308             }
8309         }
8310     },
8311
8312     // private
8313     onMouseDown : function(handle, e){
8314         if(this.enabled){
8315             e.stopEvent();
8316             this.activeHandle = handle;
8317             this.startSizing(e, handle);
8318         }
8319     },
8320
8321     // private
8322     onMouseUp : function(e){
8323         var size = this.resizeElement();
8324         this.resizing = false;
8325         this.handleOut();
8326         this.overlay.hide();
8327         this.proxy.hide();
8328         this.fireEvent("resize", this, size.width, size.height, e);
8329     },
8330
8331     // private
8332     updateChildSize : function(){
8333         
8334         if(this.resizeChild){
8335             var el = this.el;
8336             var child = this.resizeChild;
8337             var adj = this.adjustments;
8338             if(el.dom.offsetWidth){
8339                 var b = el.getSize(true);
8340                 child.setSize(b.width+adj[0], b.height+adj[1]);
8341             }
8342             // Second call here for IE
8343             // The first call enables instant resizing and
8344             // the second call corrects scroll bars if they
8345             // exist
8346             if(Roo.isIE){
8347                 setTimeout(function(){
8348                     if(el.dom.offsetWidth){
8349                         var b = el.getSize(true);
8350                         child.setSize(b.width+adj[0], b.height+adj[1]);
8351                     }
8352                 }, 10);
8353             }
8354         }
8355     },
8356
8357     // private
8358     snap : function(value, inc, min){
8359         if(!inc || !value) {
8360             return value;
8361         }
8362         var newValue = value;
8363         var m = value % inc;
8364         if(m > 0){
8365             if(m > (inc/2)){
8366                 newValue = value + (inc-m);
8367             }else{
8368                 newValue = value - m;
8369             }
8370         }
8371         return Math.max(min, newValue);
8372     },
8373
8374     // private
8375     resizeElement : function(){
8376         var box = this.proxy.getBox();
8377         if(this.updateBox){
8378             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8379         }else{
8380             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8381         }
8382         this.updateChildSize();
8383         if(!this.dynamic){
8384             this.proxy.hide();
8385         }
8386         return box;
8387     },
8388
8389     // private
8390     constrain : function(v, diff, m, mx){
8391         if(v - diff < m){
8392             diff = v - m;
8393         }else if(v - diff > mx){
8394             diff = mx - v;
8395         }
8396         return diff;
8397     },
8398
8399     // private
8400     onMouseMove : function(e){
8401         
8402         if(this.enabled){
8403             try{// try catch so if something goes wrong the user doesn't get hung
8404
8405             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8406                 return;
8407             }
8408
8409             //var curXY = this.startPoint;
8410             var curSize = this.curSize || this.startBox;
8411             var x = this.startBox.x, y = this.startBox.y;
8412             var ox = x, oy = y;
8413             var w = curSize.width, h = curSize.height;
8414             var ow = w, oh = h;
8415             var mw = this.minWidth, mh = this.minHeight;
8416             var mxw = this.maxWidth, mxh = this.maxHeight;
8417             var wi = this.widthIncrement;
8418             var hi = this.heightIncrement;
8419
8420             var eventXY = e.getXY();
8421             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8422             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8423
8424             var pos = this.activeHandle.position;
8425
8426             switch(pos){
8427                 case "east":
8428                     w += diffX;
8429                     w = Math.min(Math.max(mw, w), mxw);
8430                     break;
8431              
8432                 case "south":
8433                     h += diffY;
8434                     h = Math.min(Math.max(mh, h), mxh);
8435                     break;
8436                 case "southeast":
8437                     w += diffX;
8438                     h += diffY;
8439                     w = Math.min(Math.max(mw, w), mxw);
8440                     h = Math.min(Math.max(mh, h), mxh);
8441                     break;
8442                 case "north":
8443                     diffY = this.constrain(h, diffY, mh, mxh);
8444                     y += diffY;
8445                     h -= diffY;
8446                     break;
8447                 case "hdrag":
8448                     
8449                     if (wi) {
8450                         var adiffX = Math.abs(diffX);
8451                         var sub = (adiffX % wi); // how much 
8452                         if (sub > (wi/2)) { // far enough to snap
8453                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8454                         } else {
8455                             // remove difference.. 
8456                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8457                         }
8458                     }
8459                     x += diffX;
8460                     x = Math.max(this.minX, x);
8461                     break;
8462                 case "west":
8463                     diffX = this.constrain(w, diffX, mw, mxw);
8464                     x += diffX;
8465                     w -= diffX;
8466                     break;
8467                 case "northeast":
8468                     w += diffX;
8469                     w = Math.min(Math.max(mw, w), mxw);
8470                     diffY = this.constrain(h, diffY, mh, mxh);
8471                     y += diffY;
8472                     h -= diffY;
8473                     break;
8474                 case "northwest":
8475                     diffX = this.constrain(w, diffX, mw, mxw);
8476                     diffY = this.constrain(h, diffY, mh, mxh);
8477                     y += diffY;
8478                     h -= diffY;
8479                     x += diffX;
8480                     w -= diffX;
8481                     break;
8482                case "southwest":
8483                     diffX = this.constrain(w, diffX, mw, mxw);
8484                     h += diffY;
8485                     h = Math.min(Math.max(mh, h), mxh);
8486                     x += diffX;
8487                     w -= diffX;
8488                     break;
8489             }
8490
8491             var sw = this.snap(w, wi, mw);
8492             var sh = this.snap(h, hi, mh);
8493             if(sw != w || sh != h){
8494                 switch(pos){
8495                     case "northeast":
8496                         y -= sh - h;
8497                     break;
8498                     case "north":
8499                         y -= sh - h;
8500                         break;
8501                     case "southwest":
8502                         x -= sw - w;
8503                     break;
8504                     case "west":
8505                         x -= sw - w;
8506                         break;
8507                     case "northwest":
8508                         x -= sw - w;
8509                         y -= sh - h;
8510                     break;
8511                 }
8512                 w = sw;
8513                 h = sh;
8514             }
8515
8516             if(this.preserveRatio){
8517                 switch(pos){
8518                     case "southeast":
8519                     case "east":
8520                         h = oh * (w/ow);
8521                         h = Math.min(Math.max(mh, h), mxh);
8522                         w = ow * (h/oh);
8523                        break;
8524                     case "south":
8525                         w = ow * (h/oh);
8526                         w = Math.min(Math.max(mw, w), mxw);
8527                         h = oh * (w/ow);
8528                         break;
8529                     case "northeast":
8530                         w = ow * (h/oh);
8531                         w = Math.min(Math.max(mw, w), mxw);
8532                         h = oh * (w/ow);
8533                     break;
8534                     case "north":
8535                         var tw = w;
8536                         w = ow * (h/oh);
8537                         w = Math.min(Math.max(mw, w), mxw);
8538                         h = oh * (w/ow);
8539                         x += (tw - w) / 2;
8540                         break;
8541                     case "southwest":
8542                         h = oh * (w/ow);
8543                         h = Math.min(Math.max(mh, h), mxh);
8544                         var tw = w;
8545                         w = ow * (h/oh);
8546                         x += tw - w;
8547                         break;
8548                     case "west":
8549                         var th = h;
8550                         h = oh * (w/ow);
8551                         h = Math.min(Math.max(mh, h), mxh);
8552                         y += (th - h) / 2;
8553                         var tw = w;
8554                         w = ow * (h/oh);
8555                         x += tw - w;
8556                        break;
8557                     case "northwest":
8558                         var tw = w;
8559                         var th = h;
8560                         h = oh * (w/ow);
8561                         h = Math.min(Math.max(mh, h), mxh);
8562                         w = ow * (h/oh);
8563                         y += th - h;
8564                         x += tw - w;
8565                        break;
8566
8567                 }
8568             }
8569             if (pos == 'hdrag') {
8570                 w = ow;
8571             }
8572             this.proxy.setBounds(x, y, w, h);
8573             if(this.dynamic){
8574                 this.resizeElement();
8575             }
8576             }catch(e){}
8577         }
8578         this.fireEvent("resizing", this, x, y, w, h, e);
8579     },
8580
8581     // private
8582     handleOver : function(){
8583         if(this.enabled){
8584             this.el.addClass("x-resizable-over");
8585         }
8586     },
8587
8588     // private
8589     handleOut : function(){
8590         if(!this.resizing){
8591             this.el.removeClass("x-resizable-over");
8592         }
8593     },
8594
8595     /**
8596      * Returns the element this component is bound to.
8597      * @return {Roo.Element}
8598      */
8599     getEl : function(){
8600         return this.el;
8601     },
8602
8603     /**
8604      * Returns the resizeChild element (or null).
8605      * @return {Roo.Element}
8606      */
8607     getResizeChild : function(){
8608         return this.resizeChild;
8609     },
8610     groupHandler : function()
8611     {
8612         
8613     },
8614     /**
8615      * Destroys this resizable. If the element was wrapped and
8616      * removeEl is not true then the element remains.
8617      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8618      */
8619     destroy : function(removeEl){
8620         this.proxy.remove();
8621         if(this.overlay){
8622             this.overlay.removeAllListeners();
8623             this.overlay.remove();
8624         }
8625         var ps = Roo.Resizable.positions;
8626         for(var k in ps){
8627             if(typeof ps[k] != "function" && this[ps[k]]){
8628                 var h = this[ps[k]];
8629                 h.el.removeAllListeners();
8630                 h.el.remove();
8631             }
8632         }
8633         if(removeEl){
8634             this.el.update("");
8635             this.el.remove();
8636         }
8637     }
8638 });
8639
8640 // private
8641 // hash to map config positions to true positions
8642 Roo.Resizable.positions = {
8643     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8644     hd: "hdrag"
8645 };
8646
8647 // private
8648 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8649     if(!this.tpl){
8650         // only initialize the template if resizable is used
8651         var tpl = Roo.DomHelper.createTemplate(
8652             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8653         );
8654         tpl.compile();
8655         Roo.Resizable.Handle.prototype.tpl = tpl;
8656     }
8657     this.position = pos;
8658     this.rz = rz;
8659     // show north drag fro topdra
8660     var handlepos = pos == 'hdrag' ? 'north' : pos;
8661     
8662     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8663     if (pos == 'hdrag') {
8664         this.el.setStyle('cursor', 'pointer');
8665     }
8666     this.el.unselectable();
8667     if(transparent){
8668         this.el.setOpacity(0);
8669     }
8670     this.el.on("mousedown", this.onMouseDown, this);
8671     if(!disableTrackOver){
8672         this.el.on("mouseover", this.onMouseOver, this);
8673         this.el.on("mouseout", this.onMouseOut, this);
8674     }
8675 };
8676
8677 // private
8678 Roo.Resizable.Handle.prototype = {
8679     afterResize : function(rz){
8680         Roo.log('after?');
8681         // do nothing
8682     },
8683     // private
8684     onMouseDown : function(e){
8685         this.rz.onMouseDown(this, e);
8686     },
8687     // private
8688     onMouseOver : function(e){
8689         this.rz.handleOver(this, e);
8690     },
8691     // private
8692     onMouseOut : function(e){
8693         this.rz.handleOut(this, e);
8694     }
8695 };/*
8696  * Based on:
8697  * Ext JS Library 1.1.1
8698  * Copyright(c) 2006-2007, Ext JS, LLC.
8699  *
8700  * Originally Released Under LGPL - original licence link has changed is not relivant.
8701  *
8702  * Fork - LGPL
8703  * <script type="text/javascript">
8704  */
8705
8706 /**
8707  * @class Roo.Editor
8708  * @extends Roo.Component
8709  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8710  * @constructor
8711  * Create a new Editor
8712  * @param {Roo.form.Field} field The Field object (or descendant)
8713  * @param {Object} config The config object
8714  */
8715 Roo.Editor = function(field, config){
8716     Roo.Editor.superclass.constructor.call(this, config);
8717     this.field = field;
8718     this.addEvents({
8719         /**
8720              * @event beforestartedit
8721              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8722              * false from the handler of this event.
8723              * @param {Editor} this
8724              * @param {Roo.Element} boundEl The underlying element bound to this editor
8725              * @param {Mixed} value The field value being set
8726              */
8727         "beforestartedit" : true,
8728         /**
8729              * @event startedit
8730              * Fires when this editor is displayed
8731              * @param {Roo.Element} boundEl The underlying element bound to this editor
8732              * @param {Mixed} value The starting field value
8733              */
8734         "startedit" : true,
8735         /**
8736              * @event beforecomplete
8737              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8738              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8739              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8740              * event will not fire since no edit actually occurred.
8741              * @param {Editor} this
8742              * @param {Mixed} value The current field value
8743              * @param {Mixed} startValue The original field value
8744              */
8745         "beforecomplete" : true,
8746         /**
8747              * @event complete
8748              * Fires after editing is complete and any changed value has been written to the underlying field.
8749              * @param {Editor} this
8750              * @param {Mixed} value The current field value
8751              * @param {Mixed} startValue The original field value
8752              */
8753         "complete" : true,
8754         /**
8755          * @event specialkey
8756          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8757          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8758          * @param {Roo.form.Field} this
8759          * @param {Roo.EventObject} e The event object
8760          */
8761         "specialkey" : true
8762     });
8763 };
8764
8765 Roo.extend(Roo.Editor, Roo.Component, {
8766     /**
8767      * @cfg {Boolean/String} autosize
8768      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8769      * or "height" to adopt the height only (defaults to false)
8770      */
8771     /**
8772      * @cfg {Boolean} revertInvalid
8773      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8774      * validation fails (defaults to true)
8775      */
8776     /**
8777      * @cfg {Boolean} ignoreNoChange
8778      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8779      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8780      * will never be ignored.
8781      */
8782     /**
8783      * @cfg {Boolean} hideEl
8784      * False to keep the bound element visible while the editor is displayed (defaults to true)
8785      */
8786     /**
8787      * @cfg {Mixed} value
8788      * The data value of the underlying field (defaults to "")
8789      */
8790     value : "",
8791     /**
8792      * @cfg {String} alignment
8793      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8794      */
8795     alignment: "c-c?",
8796     /**
8797      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8798      * for bottom-right shadow (defaults to "frame")
8799      */
8800     shadow : "frame",
8801     /**
8802      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8803      */
8804     constrain : false,
8805     /**
8806      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8807      */
8808     completeOnEnter : false,
8809     /**
8810      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8811      */
8812     cancelOnEsc : false,
8813     /**
8814      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8815      */
8816     updateEl : false,
8817
8818     // private
8819     onRender : function(ct, position){
8820         this.el = new Roo.Layer({
8821             shadow: this.shadow,
8822             cls: "x-editor",
8823             parentEl : ct,
8824             shim : this.shim,
8825             shadowOffset:4,
8826             id: this.id,
8827             constrain: this.constrain
8828         });
8829         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8830         if(this.field.msgTarget != 'title'){
8831             this.field.msgTarget = 'qtip';
8832         }
8833         this.field.render(this.el);
8834         if(Roo.isGecko){
8835             this.field.el.dom.setAttribute('autocomplete', 'off');
8836         }
8837         this.field.on("specialkey", this.onSpecialKey, this);
8838         if(this.swallowKeys){
8839             this.field.el.swallowEvent(['keydown','keypress']);
8840         }
8841         this.field.show();
8842         this.field.on("blur", this.onBlur, this);
8843         if(this.field.grow){
8844             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8845         }
8846     },
8847
8848     onSpecialKey : function(field, e)
8849     {
8850         //Roo.log('editor onSpecialKey');
8851         if(this.completeOnEnter && e.getKey() == e.ENTER){
8852             e.stopEvent();
8853             this.completeEdit();
8854             return;
8855         }
8856         // do not fire special key otherwise it might hide close the editor...
8857         if(e.getKey() == e.ENTER){    
8858             return;
8859         }
8860         if(this.cancelOnEsc && e.getKey() == e.ESC){
8861             this.cancelEdit();
8862             return;
8863         } 
8864         this.fireEvent('specialkey', field, e);
8865     
8866     },
8867
8868     /**
8869      * Starts the editing process and shows the editor.
8870      * @param {String/HTMLElement/Element} el The element to edit
8871      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8872       * to the innerHTML of el.
8873      */
8874     startEdit : function(el, value){
8875         if(this.editing){
8876             this.completeEdit();
8877         }
8878         this.boundEl = Roo.get(el);
8879         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8880         if(!this.rendered){
8881             this.render(this.parentEl || document.body);
8882         }
8883         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8884             return;
8885         }
8886         this.startValue = v;
8887         this.field.setValue(v);
8888         if(this.autoSize){
8889             var sz = this.boundEl.getSize();
8890             switch(this.autoSize){
8891                 case "width":
8892                 this.setSize(sz.width,  "");
8893                 break;
8894                 case "height":
8895                 this.setSize("",  sz.height);
8896                 break;
8897                 default:
8898                 this.setSize(sz.width,  sz.height);
8899             }
8900         }
8901         this.el.alignTo(this.boundEl, this.alignment);
8902         this.editing = true;
8903         if(Roo.QuickTips){
8904             Roo.QuickTips.disable();
8905         }
8906         this.show();
8907     },
8908
8909     /**
8910      * Sets the height and width of this editor.
8911      * @param {Number} width The new width
8912      * @param {Number} height The new height
8913      */
8914     setSize : function(w, h){
8915         this.field.setSize(w, h);
8916         if(this.el){
8917             this.el.sync();
8918         }
8919     },
8920
8921     /**
8922      * Realigns the editor to the bound field based on the current alignment config value.
8923      */
8924     realign : function(){
8925         this.el.alignTo(this.boundEl, this.alignment);
8926     },
8927
8928     /**
8929      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8930      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8931      */
8932     completeEdit : function(remainVisible){
8933         if(!this.editing){
8934             return;
8935         }
8936         var v = this.getValue();
8937         if(this.revertInvalid !== false && !this.field.isValid()){
8938             v = this.startValue;
8939             this.cancelEdit(true);
8940         }
8941         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8942             this.editing = false;
8943             this.hide();
8944             return;
8945         }
8946         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8947             this.editing = false;
8948             if(this.updateEl && this.boundEl){
8949                 this.boundEl.update(v);
8950             }
8951             if(remainVisible !== true){
8952                 this.hide();
8953             }
8954             this.fireEvent("complete", this, v, this.startValue);
8955         }
8956     },
8957
8958     // private
8959     onShow : function(){
8960         this.el.show();
8961         if(this.hideEl !== false){
8962             this.boundEl.hide();
8963         }
8964         this.field.show();
8965         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8966             this.fixIEFocus = true;
8967             this.deferredFocus.defer(50, this);
8968         }else{
8969             this.field.focus();
8970         }
8971         this.fireEvent("startedit", this.boundEl, this.startValue);
8972     },
8973
8974     deferredFocus : function(){
8975         if(this.editing){
8976             this.field.focus();
8977         }
8978     },
8979
8980     /**
8981      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8982      * reverted to the original starting value.
8983      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8984      * cancel (defaults to false)
8985      */
8986     cancelEdit : function(remainVisible){
8987         if(this.editing){
8988             this.setValue(this.startValue);
8989             if(remainVisible !== true){
8990                 this.hide();
8991             }
8992         }
8993     },
8994
8995     // private
8996     onBlur : function(){
8997         if(this.allowBlur !== true && this.editing){
8998             this.completeEdit();
8999         }
9000     },
9001
9002     // private
9003     onHide : function(){
9004         if(this.editing){
9005             this.completeEdit();
9006             return;
9007         }
9008         this.field.blur();
9009         if(this.field.collapse){
9010             this.field.collapse();
9011         }
9012         this.el.hide();
9013         if(this.hideEl !== false){
9014             this.boundEl.show();
9015         }
9016         if(Roo.QuickTips){
9017             Roo.QuickTips.enable();
9018         }
9019     },
9020
9021     /**
9022      * Sets the data value of the editor
9023      * @param {Mixed} value Any valid value supported by the underlying field
9024      */
9025     setValue : function(v){
9026         this.field.setValue(v);
9027     },
9028
9029     /**
9030      * Gets the data value of the editor
9031      * @return {Mixed} The data value
9032      */
9033     getValue : function(){
9034         return this.field.getValue();
9035     }
9036 });/*
9037  * Based on:
9038  * Ext JS Library 1.1.1
9039  * Copyright(c) 2006-2007, Ext JS, LLC.
9040  *
9041  * Originally Released Under LGPL - original licence link has changed is not relivant.
9042  *
9043  * Fork - LGPL
9044  * <script type="text/javascript">
9045  */
9046  
9047 /**
9048  * @class Roo.BasicDialog
9049  * @extends Roo.util.Observable
9050  * @parent none builder
9051  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9052  * <pre><code>
9053 var dlg = new Roo.BasicDialog("my-dlg", {
9054     height: 200,
9055     width: 300,
9056     minHeight: 100,
9057     minWidth: 150,
9058     modal: true,
9059     proxyDrag: true,
9060     shadow: true
9061 });
9062 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9063 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9064 dlg.addButton('Cancel', dlg.hide, dlg);
9065 dlg.show();
9066 </code></pre>
9067   <b>A Dialog should always be a direct child of the body element.</b>
9068  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9069  * @cfg {String} title Default text to display in the title bar (defaults to null)
9070  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9071  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9072  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9073  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9074  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9075  * (defaults to null with no animation)
9076  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9077  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9078  * property for valid values (defaults to 'all')
9079  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9080  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9081  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9082  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9083  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9084  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9085  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9086  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9087  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9088  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9089  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9090  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9091  * draggable = true (defaults to false)
9092  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9093  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9094  * shadow (defaults to false)
9095  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9096  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9097  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9098  * @cfg {Array} buttons Array of buttons
9099  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9100  * @constructor
9101  * Create a new BasicDialog.
9102  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9103  * @param {Object} config Configuration options
9104  */
9105 Roo.BasicDialog = function(el, config){
9106     this.el = Roo.get(el);
9107     var dh = Roo.DomHelper;
9108     if(!this.el && config && config.autoCreate){
9109         if(typeof config.autoCreate == "object"){
9110             if(!config.autoCreate.id){
9111                 config.autoCreate.id = el;
9112             }
9113             this.el = dh.append(document.body,
9114                         config.autoCreate, true);
9115         }else{
9116             this.el = dh.append(document.body,
9117                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9118         }
9119     }
9120     el = this.el;
9121     el.setDisplayed(true);
9122     el.hide = this.hideAction;
9123     this.id = el.id;
9124     el.addClass("x-dlg");
9125
9126     Roo.apply(this, config);
9127
9128     this.proxy = el.createProxy("x-dlg-proxy");
9129     this.proxy.hide = this.hideAction;
9130     this.proxy.setOpacity(.5);
9131     this.proxy.hide();
9132
9133     if(config.width){
9134         el.setWidth(config.width);
9135     }
9136     if(config.height){
9137         el.setHeight(config.height);
9138     }
9139     this.size = el.getSize();
9140     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9141         this.xy = [config.x,config.y];
9142     }else{
9143         this.xy = el.getCenterXY(true);
9144     }
9145     /** The header element @type Roo.Element */
9146     this.header = el.child("> .x-dlg-hd");
9147     /** The body element @type Roo.Element */
9148     this.body = el.child("> .x-dlg-bd");
9149     /** The footer element @type Roo.Element */
9150     this.footer = el.child("> .x-dlg-ft");
9151
9152     if(!this.header){
9153         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9154     }
9155     if(!this.body){
9156         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9157     }
9158
9159     this.header.unselectable();
9160     if(this.title){
9161         this.header.update(this.title);
9162     }
9163     // this element allows the dialog to be focused for keyboard event
9164     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9165     this.focusEl.swallowEvent("click", true);
9166
9167     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9168
9169     // wrap the body and footer for special rendering
9170     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9171     if(this.footer){
9172         this.bwrap.dom.appendChild(this.footer.dom);
9173     }
9174
9175     this.bg = this.el.createChild({
9176         tag: "div", cls:"x-dlg-bg",
9177         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9178     });
9179     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9180
9181
9182     if(this.autoScroll !== false && !this.autoTabs){
9183         this.body.setStyle("overflow", "auto");
9184     }
9185
9186     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9187
9188     if(this.closable !== false){
9189         this.el.addClass("x-dlg-closable");
9190         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9191         this.close.on("click", this.closeClick, this);
9192         this.close.addClassOnOver("x-dlg-close-over");
9193     }
9194     if(this.collapsible !== false){
9195         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9196         this.collapseBtn.on("click", this.collapseClick, this);
9197         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9198         this.header.on("dblclick", this.collapseClick, this);
9199     }
9200     if(this.resizable !== false){
9201         this.el.addClass("x-dlg-resizable");
9202         this.resizer = new Roo.Resizable(el, {
9203             minWidth: this.minWidth || 80,
9204             minHeight:this.minHeight || 80,
9205             handles: this.resizeHandles || "all",
9206             pinned: true
9207         });
9208         this.resizer.on("beforeresize", this.beforeResize, this);
9209         this.resizer.on("resize", this.onResize, this);
9210     }
9211     if(this.draggable !== false){
9212         el.addClass("x-dlg-draggable");
9213         if (!this.proxyDrag) {
9214             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9215         }
9216         else {
9217             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9218         }
9219         dd.setHandleElId(this.header.id);
9220         dd.endDrag = this.endMove.createDelegate(this);
9221         dd.startDrag = this.startMove.createDelegate(this);
9222         dd.onDrag = this.onDrag.createDelegate(this);
9223         dd.scroll = false;
9224         this.dd = dd;
9225     }
9226     if(this.modal){
9227         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9228         this.mask.enableDisplayMode("block");
9229         this.mask.hide();
9230         this.el.addClass("x-dlg-modal");
9231     }
9232     if(this.shadow){
9233         this.shadow = new Roo.Shadow({
9234             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9235             offset : this.shadowOffset
9236         });
9237     }else{
9238         this.shadowOffset = 0;
9239     }
9240     if(Roo.useShims && this.shim !== false){
9241         this.shim = this.el.createShim();
9242         this.shim.hide = this.hideAction;
9243         this.shim.hide();
9244     }else{
9245         this.shim = false;
9246     }
9247     if(this.autoTabs){
9248         this.initTabs();
9249     }
9250     if (this.buttons) { 
9251         var bts= this.buttons;
9252         this.buttons = [];
9253         Roo.each(bts, function(b) {
9254             this.addButton(b);
9255         }, this);
9256     }
9257     
9258     
9259     this.addEvents({
9260         /**
9261          * @event keydown
9262          * Fires when a key is pressed
9263          * @param {Roo.BasicDialog} this
9264          * @param {Roo.EventObject} e
9265          */
9266         "keydown" : true,
9267         /**
9268          * @event move
9269          * Fires when this dialog is moved by the user.
9270          * @param {Roo.BasicDialog} this
9271          * @param {Number} x The new page X
9272          * @param {Number} y The new page Y
9273          */
9274         "move" : true,
9275         /**
9276          * @event resize
9277          * Fires when this dialog is resized by the user.
9278          * @param {Roo.BasicDialog} this
9279          * @param {Number} width The new width
9280          * @param {Number} height The new height
9281          */
9282         "resize" : true,
9283         /**
9284          * @event beforehide
9285          * Fires before this dialog is hidden.
9286          * @param {Roo.BasicDialog} this
9287          */
9288         "beforehide" : true,
9289         /**
9290          * @event hide
9291          * Fires when this dialog is hidden.
9292          * @param {Roo.BasicDialog} this
9293          */
9294         "hide" : true,
9295         /**
9296          * @event beforeshow
9297          * Fires before this dialog is shown.
9298          * @param {Roo.BasicDialog} this
9299          */
9300         "beforeshow" : true,
9301         /**
9302          * @event show
9303          * Fires when this dialog is shown.
9304          * @param {Roo.BasicDialog} this
9305          */
9306         "show" : true
9307     });
9308     el.on("keydown", this.onKeyDown, this);
9309     el.on("mousedown", this.toFront, this);
9310     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9311     this.el.hide();
9312     Roo.DialogManager.register(this);
9313     Roo.BasicDialog.superclass.constructor.call(this);
9314 };
9315
9316 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9317     shadowOffset: Roo.isIE ? 6 : 5,
9318     minHeight: 80,
9319     minWidth: 200,
9320     minButtonWidth: 75,
9321     defaultButton: null,
9322     buttonAlign: "right",
9323     tabTag: 'div',
9324     firstShow: true,
9325
9326     /**
9327      * Sets the dialog title text
9328      * @param {String} text The title text to display
9329      * @return {Roo.BasicDialog} this
9330      */
9331     setTitle : function(text){
9332         this.header.update(text);
9333         return this;
9334     },
9335
9336     // private
9337     closeClick : function(){
9338         this.hide();
9339     },
9340
9341     // private
9342     collapseClick : function(){
9343         this[this.collapsed ? "expand" : "collapse"]();
9344     },
9345
9346     /**
9347      * Collapses the dialog to its minimized state (only the title bar is visible).
9348      * Equivalent to the user clicking the collapse dialog button.
9349      */
9350     collapse : function(){
9351         if(!this.collapsed){
9352             this.collapsed = true;
9353             this.el.addClass("x-dlg-collapsed");
9354             this.restoreHeight = this.el.getHeight();
9355             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9356         }
9357     },
9358
9359     /**
9360      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9361      * clicking the expand dialog button.
9362      */
9363     expand : function(){
9364         if(this.collapsed){
9365             this.collapsed = false;
9366             this.el.removeClass("x-dlg-collapsed");
9367             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9368         }
9369     },
9370
9371     /**
9372      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9373      * @return {Roo.TabPanel} The tabs component
9374      */
9375     initTabs : function(){
9376         var tabs = this.getTabs();
9377         while(tabs.getTab(0)){
9378             tabs.removeTab(0);
9379         }
9380         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9381             var dom = el.dom;
9382             tabs.addTab(Roo.id(dom), dom.title);
9383             dom.title = "";
9384         });
9385         tabs.activate(0);
9386         return tabs;
9387     },
9388
9389     // private
9390     beforeResize : function(){
9391         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9392     },
9393
9394     // private
9395     onResize : function(){
9396         this.refreshSize();
9397         this.syncBodyHeight();
9398         this.adjustAssets();
9399         this.focus();
9400         this.fireEvent("resize", this, this.size.width, this.size.height);
9401     },
9402
9403     // private
9404     onKeyDown : function(e){
9405         if(this.isVisible()){
9406             this.fireEvent("keydown", this, e);
9407         }
9408     },
9409
9410     /**
9411      * Resizes the dialog.
9412      * @param {Number} width
9413      * @param {Number} height
9414      * @return {Roo.BasicDialog} this
9415      */
9416     resizeTo : function(width, height){
9417         this.el.setSize(width, height);
9418         this.size = {width: width, height: height};
9419         this.syncBodyHeight();
9420         if(this.fixedcenter){
9421             this.center();
9422         }
9423         if(this.isVisible()){
9424             this.constrainXY();
9425             this.adjustAssets();
9426         }
9427         this.fireEvent("resize", this, width, height);
9428         return this;
9429     },
9430
9431
9432     /**
9433      * Resizes the dialog to fit the specified content size.
9434      * @param {Number} width
9435      * @param {Number} height
9436      * @return {Roo.BasicDialog} this
9437      */
9438     setContentSize : function(w, h){
9439         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9440         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9441         //if(!this.el.isBorderBox()){
9442             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9443             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9444         //}
9445         if(this.tabs){
9446             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9447             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9448         }
9449         this.resizeTo(w, h);
9450         return this;
9451     },
9452
9453     /**
9454      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9455      * executed in response to a particular key being pressed while the dialog is active.
9456      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9457      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9458      * @param {Function} fn The function to call
9459      * @param {Object} scope (optional) The scope of the function
9460      * @return {Roo.BasicDialog} this
9461      */
9462     addKeyListener : function(key, fn, scope){
9463         var keyCode, shift, ctrl, alt;
9464         if(typeof key == "object" && !(key instanceof Array)){
9465             keyCode = key["key"];
9466             shift = key["shift"];
9467             ctrl = key["ctrl"];
9468             alt = key["alt"];
9469         }else{
9470             keyCode = key;
9471         }
9472         var handler = function(dlg, e){
9473             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9474                 var k = e.getKey();
9475                 if(keyCode instanceof Array){
9476                     for(var i = 0, len = keyCode.length; i < len; i++){
9477                         if(keyCode[i] == k){
9478                           fn.call(scope || window, dlg, k, e);
9479                           return;
9480                         }
9481                     }
9482                 }else{
9483                     if(k == keyCode){
9484                         fn.call(scope || window, dlg, k, e);
9485                     }
9486                 }
9487             }
9488         };
9489         this.on("keydown", handler);
9490         return this;
9491     },
9492
9493     /**
9494      * Returns the TabPanel component (creates it if it doesn't exist).
9495      * Note: If you wish to simply check for the existence of tabs without creating them,
9496      * check for a null 'tabs' property.
9497      * @return {Roo.TabPanel} The tabs component
9498      */
9499     getTabs : function(){
9500         if(!this.tabs){
9501             this.el.addClass("x-dlg-auto-tabs");
9502             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9503             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9504         }
9505         return this.tabs;
9506     },
9507
9508     /**
9509      * Adds a button to the footer section of the dialog.
9510      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9511      * object or a valid Roo.DomHelper element config
9512      * @param {Function} handler The function called when the button is clicked
9513      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9514      * @return {Roo.Button} The new button
9515      */
9516     addButton : function(config, handler, scope){
9517         var dh = Roo.DomHelper;
9518         if(!this.footer){
9519             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9520         }
9521         if(!this.btnContainer){
9522             var tb = this.footer.createChild({
9523
9524                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9525                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9526             }, null, true);
9527             this.btnContainer = tb.firstChild.firstChild.firstChild;
9528         }
9529         var bconfig = {
9530             handler: handler,
9531             scope: scope,
9532             minWidth: this.minButtonWidth,
9533             hideParent:true
9534         };
9535         if(typeof config == "string"){
9536             bconfig.text = config;
9537         }else{
9538             if(config.tag){
9539                 bconfig.dhconfig = config;
9540             }else{
9541                 Roo.apply(bconfig, config);
9542             }
9543         }
9544         var fc = false;
9545         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9546             bconfig.position = Math.max(0, bconfig.position);
9547             fc = this.btnContainer.childNodes[bconfig.position];
9548         }
9549          
9550         var btn = new Roo.Button(
9551             fc ? 
9552                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9553                 : this.btnContainer.appendChild(document.createElement("td")),
9554             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9555             bconfig
9556         );
9557         this.syncBodyHeight();
9558         if(!this.buttons){
9559             /**
9560              * Array of all the buttons that have been added to this dialog via addButton
9561              * @type Array
9562              */
9563             this.buttons = [];
9564         }
9565         this.buttons.push(btn);
9566         return btn;
9567     },
9568
9569     /**
9570      * Sets the default button to be focused when the dialog is displayed.
9571      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9572      * @return {Roo.BasicDialog} this
9573      */
9574     setDefaultButton : function(btn){
9575         this.defaultButton = btn;
9576         return this;
9577     },
9578
9579     // private
9580     getHeaderFooterHeight : function(safe){
9581         var height = 0;
9582         if(this.header){
9583            height += this.header.getHeight();
9584         }
9585         if(this.footer){
9586            var fm = this.footer.getMargins();
9587             height += (this.footer.getHeight()+fm.top+fm.bottom);
9588         }
9589         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9590         height += this.centerBg.getPadding("tb");
9591         return height;
9592     },
9593
9594     // private
9595     syncBodyHeight : function()
9596     {
9597         var bd = this.body, // the text
9598             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9599             bw = this.bwrap;
9600         var height = this.size.height - this.getHeaderFooterHeight(false);
9601         bd.setHeight(height-bd.getMargins("tb"));
9602         var hh = this.header.getHeight();
9603         var h = this.size.height-hh;
9604         cb.setHeight(h);
9605         
9606         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9607         bw.setHeight(h-cb.getPadding("tb"));
9608         
9609         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9610         bd.setWidth(bw.getWidth(true));
9611         if(this.tabs){
9612             this.tabs.syncHeight();
9613             if(Roo.isIE){
9614                 this.tabs.el.repaint();
9615             }
9616         }
9617     },
9618
9619     /**
9620      * Restores the previous state of the dialog if Roo.state is configured.
9621      * @return {Roo.BasicDialog} this
9622      */
9623     restoreState : function(){
9624         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9625         if(box && box.width){
9626             this.xy = [box.x, box.y];
9627             this.resizeTo(box.width, box.height);
9628         }
9629         return this;
9630     },
9631
9632     // private
9633     beforeShow : function(){
9634         this.expand();
9635         if(this.fixedcenter){
9636             this.xy = this.el.getCenterXY(true);
9637         }
9638         if(this.modal){
9639             Roo.get(document.body).addClass("x-body-masked");
9640             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9641             this.mask.show();
9642         }
9643         this.constrainXY();
9644     },
9645
9646     // private
9647     animShow : function(){
9648         var b = Roo.get(this.animateTarget).getBox();
9649         this.proxy.setSize(b.width, b.height);
9650         this.proxy.setLocation(b.x, b.y);
9651         this.proxy.show();
9652         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9653                     true, .35, this.showEl.createDelegate(this));
9654     },
9655
9656     /**
9657      * Shows the dialog.
9658      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9659      * @return {Roo.BasicDialog} this
9660      */
9661     show : function(animateTarget){
9662         if (this.fireEvent("beforeshow", this) === false){
9663             return;
9664         }
9665         if(this.syncHeightBeforeShow){
9666             this.syncBodyHeight();
9667         }else if(this.firstShow){
9668             this.firstShow = false;
9669             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9670         }
9671         this.animateTarget = animateTarget || this.animateTarget;
9672         if(!this.el.isVisible()){
9673             this.beforeShow();
9674             if(this.animateTarget && Roo.get(this.animateTarget)){
9675                 this.animShow();
9676             }else{
9677                 this.showEl();
9678             }
9679         }
9680         return this;
9681     },
9682
9683     // private
9684     showEl : function(){
9685         this.proxy.hide();
9686         this.el.setXY(this.xy);
9687         this.el.show();
9688         this.adjustAssets(true);
9689         this.toFront();
9690         this.focus();
9691         // IE peekaboo bug - fix found by Dave Fenwick
9692         if(Roo.isIE){
9693             this.el.repaint();
9694         }
9695         this.fireEvent("show", this);
9696     },
9697
9698     /**
9699      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9700      * dialog itself will receive focus.
9701      */
9702     focus : function(){
9703         if(this.defaultButton){
9704             this.defaultButton.focus();
9705         }else{
9706             this.focusEl.focus();
9707         }
9708     },
9709
9710     // private
9711     constrainXY : function(){
9712         if(this.constraintoviewport !== false){
9713             if(!this.viewSize){
9714                 if(this.container){
9715                     var s = this.container.getSize();
9716                     this.viewSize = [s.width, s.height];
9717                 }else{
9718                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9719                 }
9720             }
9721             var s = Roo.get(this.container||document).getScroll();
9722
9723             var x = this.xy[0], y = this.xy[1];
9724             var w = this.size.width, h = this.size.height;
9725             var vw = this.viewSize[0], vh = this.viewSize[1];
9726             // only move it if it needs it
9727             var moved = false;
9728             // first validate right/bottom
9729             if(x + w > vw+s.left){
9730                 x = vw - w;
9731                 moved = true;
9732             }
9733             if(y + h > vh+s.top){
9734                 y = vh - h;
9735                 moved = true;
9736             }
9737             // then make sure top/left isn't negative
9738             if(x < s.left){
9739                 x = s.left;
9740                 moved = true;
9741             }
9742             if(y < s.top){
9743                 y = s.top;
9744                 moved = true;
9745             }
9746             if(moved){
9747                 // cache xy
9748                 this.xy = [x, y];
9749                 if(this.isVisible()){
9750                     this.el.setLocation(x, y);
9751                     this.adjustAssets();
9752                 }
9753             }
9754         }
9755     },
9756
9757     // private
9758     onDrag : function(){
9759         if(!this.proxyDrag){
9760             this.xy = this.el.getXY();
9761             this.adjustAssets();
9762         }
9763     },
9764
9765     // private
9766     adjustAssets : function(doShow){
9767         var x = this.xy[0], y = this.xy[1];
9768         var w = this.size.width, h = this.size.height;
9769         if(doShow === true){
9770             if(this.shadow){
9771                 this.shadow.show(this.el);
9772             }
9773             if(this.shim){
9774                 this.shim.show();
9775             }
9776         }
9777         if(this.shadow && this.shadow.isVisible()){
9778             this.shadow.show(this.el);
9779         }
9780         if(this.shim && this.shim.isVisible()){
9781             this.shim.setBounds(x, y, w, h);
9782         }
9783     },
9784
9785     // private
9786     adjustViewport : function(w, h){
9787         if(!w || !h){
9788             w = Roo.lib.Dom.getViewWidth();
9789             h = Roo.lib.Dom.getViewHeight();
9790         }
9791         // cache the size
9792         this.viewSize = [w, h];
9793         if(this.modal && this.mask.isVisible()){
9794             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9795             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9796         }
9797         if(this.isVisible()){
9798             this.constrainXY();
9799         }
9800     },
9801
9802     /**
9803      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9804      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9805      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9806      */
9807     destroy : function(removeEl){
9808         if(this.isVisible()){
9809             this.animateTarget = null;
9810             this.hide();
9811         }
9812         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9813         if(this.tabs){
9814             this.tabs.destroy(removeEl);
9815         }
9816         Roo.destroy(
9817              this.shim,
9818              this.proxy,
9819              this.resizer,
9820              this.close,
9821              this.mask
9822         );
9823         if(this.dd){
9824             this.dd.unreg();
9825         }
9826         if(this.buttons){
9827            for(var i = 0, len = this.buttons.length; i < len; i++){
9828                this.buttons[i].destroy();
9829            }
9830         }
9831         this.el.removeAllListeners();
9832         if(removeEl === true){
9833             this.el.update("");
9834             this.el.remove();
9835         }
9836         Roo.DialogManager.unregister(this);
9837     },
9838
9839     // private
9840     startMove : function(){
9841         if(this.proxyDrag){
9842             this.proxy.show();
9843         }
9844         if(this.constraintoviewport !== false){
9845             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9846         }
9847     },
9848
9849     // private
9850     endMove : function(){
9851         if(!this.proxyDrag){
9852             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9853         }else{
9854             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9855             this.proxy.hide();
9856         }
9857         this.refreshSize();
9858         this.adjustAssets();
9859         this.focus();
9860         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9861     },
9862
9863     /**
9864      * Brings this dialog to the front of any other visible dialogs
9865      * @return {Roo.BasicDialog} this
9866      */
9867     toFront : function(){
9868         Roo.DialogManager.bringToFront(this);
9869         return this;
9870     },
9871
9872     /**
9873      * Sends this dialog to the back (under) of any other visible dialogs
9874      * @return {Roo.BasicDialog} this
9875      */
9876     toBack : function(){
9877         Roo.DialogManager.sendToBack(this);
9878         return this;
9879     },
9880
9881     /**
9882      * Centers this dialog in the viewport
9883      * @return {Roo.BasicDialog} this
9884      */
9885     center : function(){
9886         var xy = this.el.getCenterXY(true);
9887         this.moveTo(xy[0], xy[1]);
9888         return this;
9889     },
9890
9891     /**
9892      * Moves the dialog's top-left corner to the specified point
9893      * @param {Number} x
9894      * @param {Number} y
9895      * @return {Roo.BasicDialog} this
9896      */
9897     moveTo : function(x, y){
9898         this.xy = [x,y];
9899         if(this.isVisible()){
9900             this.el.setXY(this.xy);
9901             this.adjustAssets();
9902         }
9903         return this;
9904     },
9905
9906     /**
9907      * Aligns the dialog to the specified element
9908      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9909      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9910      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9911      * @return {Roo.BasicDialog} this
9912      */
9913     alignTo : function(element, position, offsets){
9914         this.xy = this.el.getAlignToXY(element, position, offsets);
9915         if(this.isVisible()){
9916             this.el.setXY(this.xy);
9917             this.adjustAssets();
9918         }
9919         return this;
9920     },
9921
9922     /**
9923      * Anchors an element to another element and realigns it when the window is resized.
9924      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9925      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9926      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9927      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9928      * is a number, it is used as the buffer delay (defaults to 50ms).
9929      * @return {Roo.BasicDialog} this
9930      */
9931     anchorTo : function(el, alignment, offsets, monitorScroll){
9932         var action = function(){
9933             this.alignTo(el, alignment, offsets);
9934         };
9935         Roo.EventManager.onWindowResize(action, this);
9936         var tm = typeof monitorScroll;
9937         if(tm != 'undefined'){
9938             Roo.EventManager.on(window, 'scroll', action, this,
9939                 {buffer: tm == 'number' ? monitorScroll : 50});
9940         }
9941         action.call(this);
9942         return this;
9943     },
9944
9945     /**
9946      * Returns true if the dialog is visible
9947      * @return {Boolean}
9948      */
9949     isVisible : function(){
9950         return this.el.isVisible();
9951     },
9952
9953     // private
9954     animHide : function(callback){
9955         var b = Roo.get(this.animateTarget).getBox();
9956         this.proxy.show();
9957         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9958         this.el.hide();
9959         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9960                     this.hideEl.createDelegate(this, [callback]));
9961     },
9962
9963     /**
9964      * Hides the dialog.
9965      * @param {Function} callback (optional) Function to call when the dialog is hidden
9966      * @return {Roo.BasicDialog} this
9967      */
9968     hide : function(callback){
9969         if (this.fireEvent("beforehide", this) === false){
9970             return;
9971         }
9972         if(this.shadow){
9973             this.shadow.hide();
9974         }
9975         if(this.shim) {
9976           this.shim.hide();
9977         }
9978         // sometimes animateTarget seems to get set.. causing problems...
9979         // this just double checks..
9980         if(this.animateTarget && Roo.get(this.animateTarget)) {
9981            this.animHide(callback);
9982         }else{
9983             this.el.hide();
9984             this.hideEl(callback);
9985         }
9986         return this;
9987     },
9988
9989     // private
9990     hideEl : function(callback){
9991         this.proxy.hide();
9992         if(this.modal){
9993             this.mask.hide();
9994             Roo.get(document.body).removeClass("x-body-masked");
9995         }
9996         this.fireEvent("hide", this);
9997         if(typeof callback == "function"){
9998             callback();
9999         }
10000     },
10001
10002     // private
10003     hideAction : function(){
10004         this.setLeft("-10000px");
10005         this.setTop("-10000px");
10006         this.setStyle("visibility", "hidden");
10007     },
10008
10009     // private
10010     refreshSize : function(){
10011         this.size = this.el.getSize();
10012         this.xy = this.el.getXY();
10013         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10014     },
10015
10016     // private
10017     // z-index is managed by the DialogManager and may be overwritten at any time
10018     setZIndex : function(index){
10019         if(this.modal){
10020             this.mask.setStyle("z-index", index);
10021         }
10022         if(this.shim){
10023             this.shim.setStyle("z-index", ++index);
10024         }
10025         if(this.shadow){
10026             this.shadow.setZIndex(++index);
10027         }
10028         this.el.setStyle("z-index", ++index);
10029         if(this.proxy){
10030             this.proxy.setStyle("z-index", ++index);
10031         }
10032         if(this.resizer){
10033             this.resizer.proxy.setStyle("z-index", ++index);
10034         }
10035
10036         this.lastZIndex = index;
10037     },
10038
10039     /**
10040      * Returns the element for this dialog
10041      * @return {Roo.Element} The underlying dialog Element
10042      */
10043     getEl : function(){
10044         return this.el;
10045     }
10046 });
10047
10048 /**
10049  * @class Roo.DialogManager
10050  * Provides global access to BasicDialogs that have been created and
10051  * support for z-indexing (layering) multiple open dialogs.
10052  */
10053 Roo.DialogManager = function(){
10054     var list = {};
10055     var accessList = [];
10056     var front = null;
10057
10058     // private
10059     var sortDialogs = function(d1, d2){
10060         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10061     };
10062
10063     // private
10064     var orderDialogs = function(){
10065         accessList.sort(sortDialogs);
10066         var seed = Roo.DialogManager.zseed;
10067         for(var i = 0, len = accessList.length; i < len; i++){
10068             var dlg = accessList[i];
10069             if(dlg){
10070                 dlg.setZIndex(seed + (i*10));
10071             }
10072         }
10073     };
10074
10075     return {
10076         /**
10077          * The starting z-index for BasicDialogs (defaults to 9000)
10078          * @type Number The z-index value
10079          */
10080         zseed : 9000,
10081
10082         // private
10083         register : function(dlg){
10084             list[dlg.id] = dlg;
10085             accessList.push(dlg);
10086         },
10087
10088         // private
10089         unregister : function(dlg){
10090             delete list[dlg.id];
10091             var i=0;
10092             var len=0;
10093             if(!accessList.indexOf){
10094                 for(  i = 0, len = accessList.length; i < len; i++){
10095                     if(accessList[i] == dlg){
10096                         accessList.splice(i, 1);
10097                         return;
10098                     }
10099                 }
10100             }else{
10101                  i = accessList.indexOf(dlg);
10102                 if(i != -1){
10103                     accessList.splice(i, 1);
10104                 }
10105             }
10106         },
10107
10108         /**
10109          * Gets a registered dialog by id
10110          * @param {String/Object} id The id of the dialog or a dialog
10111          * @return {Roo.BasicDialog} this
10112          */
10113         get : function(id){
10114             return typeof id == "object" ? id : list[id];
10115         },
10116
10117         /**
10118          * Brings the specified dialog to the front
10119          * @param {String/Object} dlg The id of the dialog or a dialog
10120          * @return {Roo.BasicDialog} this
10121          */
10122         bringToFront : function(dlg){
10123             dlg = this.get(dlg);
10124             if(dlg != front){
10125                 front = dlg;
10126                 dlg._lastAccess = new Date().getTime();
10127                 orderDialogs();
10128             }
10129             return dlg;
10130         },
10131
10132         /**
10133          * Sends the specified dialog to the back
10134          * @param {String/Object} dlg The id of the dialog or a dialog
10135          * @return {Roo.BasicDialog} this
10136          */
10137         sendToBack : function(dlg){
10138             dlg = this.get(dlg);
10139             dlg._lastAccess = -(new Date().getTime());
10140             orderDialogs();
10141             return dlg;
10142         },
10143
10144         /**
10145          * Hides all dialogs
10146          */
10147         hideAll : function(){
10148             for(var id in list){
10149                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10150                     list[id].hide();
10151                 }
10152             }
10153         }
10154     };
10155 }();
10156
10157 /**
10158  * @class Roo.LayoutDialog
10159  * @extends Roo.BasicDialog
10160  * @children Roo.ContentPanel
10161  * @parent builder none
10162  * Dialog which provides adjustments for working with a layout in a Dialog.
10163  * Add your necessary layout config options to the dialog's config.<br>
10164  * Example usage (including a nested layout):
10165  * <pre><code>
10166 if(!dialog){
10167     dialog = new Roo.LayoutDialog("download-dlg", {
10168         modal: true,
10169         width:600,
10170         height:450,
10171         shadow:true,
10172         minWidth:500,
10173         minHeight:350,
10174         autoTabs:true,
10175         proxyDrag:true,
10176         // layout config merges with the dialog config
10177         center:{
10178             tabPosition: "top",
10179             alwaysShowTabs: true
10180         }
10181     });
10182     dialog.addKeyListener(27, dialog.hide, dialog);
10183     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10184     dialog.addButton("Build It!", this.getDownload, this);
10185
10186     // we can even add nested layouts
10187     var innerLayout = new Roo.BorderLayout("dl-inner", {
10188         east: {
10189             initialSize: 200,
10190             autoScroll:true,
10191             split:true
10192         },
10193         center: {
10194             autoScroll:true
10195         }
10196     });
10197     innerLayout.beginUpdate();
10198     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10199     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10200     innerLayout.endUpdate(true);
10201
10202     var layout = dialog.getLayout();
10203     layout.beginUpdate();
10204     layout.add("center", new Roo.ContentPanel("standard-panel",
10205                         {title: "Download the Source", fitToFrame:true}));
10206     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10207                {title: "Build your own roo.js"}));
10208     layout.getRegion("center").showPanel(sp);
10209     layout.endUpdate();
10210 }
10211 </code></pre>
10212     * @constructor
10213     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10214     * @param {Object} config configuration options
10215   */
10216 Roo.LayoutDialog = function(el, cfg){
10217     
10218     var config=  cfg;
10219     if (typeof(cfg) == 'undefined') {
10220         config = Roo.apply({}, el);
10221         // not sure why we use documentElement here.. - it should always be body.
10222         // IE7 borks horribly if we use documentElement.
10223         // webkit also does not like documentElement - it creates a body element...
10224         el = Roo.get( document.body || document.documentElement ).createChild();
10225         //config.autoCreate = true;
10226     }
10227     
10228     
10229     config.autoTabs = false;
10230     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10231     this.body.setStyle({overflow:"hidden", position:"relative"});
10232     this.layout = new Roo.BorderLayout(this.body.dom, config);
10233     this.layout.monitorWindowResize = false;
10234     this.el.addClass("x-dlg-auto-layout");
10235     // fix case when center region overwrites center function
10236     this.center = Roo.BasicDialog.prototype.center;
10237     this.on("show", this.layout.layout, this.layout, true);
10238     if (config.items) {
10239         var xitems = config.items;
10240         delete config.items;
10241         Roo.each(xitems, this.addxtype, this);
10242     }
10243     
10244     
10245 };
10246 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10247     
10248     
10249     /**
10250      * @cfg {Roo.LayoutRegion} east  
10251      */
10252     /**
10253      * @cfg {Roo.LayoutRegion} west
10254      */
10255     /**
10256      * @cfg {Roo.LayoutRegion} south
10257      */
10258     /**
10259      * @cfg {Roo.LayoutRegion} north
10260      */
10261     /**
10262      * @cfg {Roo.LayoutRegion} center
10263      */
10264     /**
10265      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10266      */
10267     
10268     
10269     /**
10270      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10271      * @deprecated
10272      */
10273     endUpdate : function(){
10274         this.layout.endUpdate();
10275     },
10276
10277     /**
10278      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10279      *  @deprecated
10280      */
10281     beginUpdate : function(){
10282         this.layout.beginUpdate();
10283     },
10284
10285     /**
10286      * Get the BorderLayout for this dialog
10287      * @return {Roo.BorderLayout}
10288      */
10289     getLayout : function(){
10290         return this.layout;
10291     },
10292
10293     showEl : function(){
10294         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10295         if(Roo.isIE7){
10296             this.layout.layout();
10297         }
10298     },
10299
10300     // private
10301     // Use the syncHeightBeforeShow config option to control this automatically
10302     syncBodyHeight : function(){
10303         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10304         if(this.layout){this.layout.layout();}
10305     },
10306     
10307       /**
10308      * Add an xtype element (actually adds to the layout.)
10309      * @return {Object} xdata xtype object data.
10310      */
10311     
10312     addxtype : function(c) {
10313         return this.layout.addxtype(c);
10314     }
10315 });/*
10316  * Based on:
10317  * Ext JS Library 1.1.1
10318  * Copyright(c) 2006-2007, Ext JS, LLC.
10319  *
10320  * Originally Released Under LGPL - original licence link has changed is not relivant.
10321  *
10322  * Fork - LGPL
10323  * <script type="text/javascript">
10324  */
10325  
10326 /**
10327  * @class Roo.MessageBox
10328  * @static
10329  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10330  * Example usage:
10331  *<pre><code>
10332 // Basic alert:
10333 Roo.Msg.alert('Status', 'Changes saved successfully.');
10334
10335 // Prompt for user data:
10336 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10337     if (btn == 'ok'){
10338         // process text value...
10339     }
10340 });
10341
10342 // Show a dialog using config options:
10343 Roo.Msg.show({
10344    title:'Save Changes?',
10345    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10346    buttons: Roo.Msg.YESNOCANCEL,
10347    fn: processResult,
10348    animEl: 'elId'
10349 });
10350 </code></pre>
10351  * @static
10352  */
10353 Roo.MessageBox = function(){
10354     var dlg, opt, mask, waitTimer;
10355     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10356     var buttons, activeTextEl, bwidth;
10357
10358     // private
10359     var handleButton = function(button){
10360         dlg.hide();
10361         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10362     };
10363
10364     // private
10365     var handleHide = function(){
10366         if(opt && opt.cls){
10367             dlg.el.removeClass(opt.cls);
10368         }
10369         if(waitTimer){
10370             Roo.TaskMgr.stop(waitTimer);
10371             waitTimer = null;
10372         }
10373     };
10374
10375     // private
10376     var updateButtons = function(b){
10377         var width = 0;
10378         if(!b){
10379             buttons["ok"].hide();
10380             buttons["cancel"].hide();
10381             buttons["yes"].hide();
10382             buttons["no"].hide();
10383             dlg.footer.dom.style.display = 'none';
10384             return width;
10385         }
10386         dlg.footer.dom.style.display = '';
10387         for(var k in buttons){
10388             if(typeof buttons[k] != "function"){
10389                 if(b[k]){
10390                     buttons[k].show();
10391                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10392                     width += buttons[k].el.getWidth()+15;
10393                 }else{
10394                     buttons[k].hide();
10395                 }
10396             }
10397         }
10398         return width;
10399     };
10400
10401     // private
10402     var handleEsc = function(d, k, e){
10403         if(opt && opt.closable !== false){
10404             dlg.hide();
10405         }
10406         if(e){
10407             e.stopEvent();
10408         }
10409     };
10410
10411     return {
10412         /**
10413          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10414          * @return {Roo.BasicDialog} The BasicDialog element
10415          */
10416         getDialog : function(){
10417            if(!dlg){
10418                 dlg = new Roo.BasicDialog("x-msg-box", {
10419                     autoCreate : true,
10420                     shadow: true,
10421                     draggable: true,
10422                     resizable:false,
10423                     constraintoviewport:false,
10424                     fixedcenter:true,
10425                     collapsible : false,
10426                     shim:true,
10427                     modal: true,
10428                     width:400, height:100,
10429                     buttonAlign:"center",
10430                     closeClick : function(){
10431                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10432                             handleButton("no");
10433                         }else{
10434                             handleButton("cancel");
10435                         }
10436                     }
10437                 });
10438               
10439                 dlg.on("hide", handleHide);
10440                 mask = dlg.mask;
10441                 dlg.addKeyListener(27, handleEsc);
10442                 buttons = {};
10443                 var bt = this.buttonText;
10444                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10445                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10446                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10447                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10448                 bodyEl = dlg.body.createChild({
10449
10450                     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>'
10451                 });
10452                 msgEl = bodyEl.dom.firstChild;
10453                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10454                 textboxEl.enableDisplayMode();
10455                 textboxEl.addKeyListener([10,13], function(){
10456                     if(dlg.isVisible() && opt && opt.buttons){
10457                         if(opt.buttons.ok){
10458                             handleButton("ok");
10459                         }else if(opt.buttons.yes){
10460                             handleButton("yes");
10461                         }
10462                     }
10463                 });
10464                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10465                 textareaEl.enableDisplayMode();
10466                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10467                 progressEl.enableDisplayMode();
10468                 var pf = progressEl.dom.firstChild;
10469                 if (pf) {
10470                     pp = Roo.get(pf.firstChild);
10471                     pp.setHeight(pf.offsetHeight);
10472                 }
10473                 
10474             }
10475             return dlg;
10476         },
10477
10478         /**
10479          * Updates the message box body text
10480          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10481          * the XHTML-compliant non-breaking space character '&amp;#160;')
10482          * @return {Roo.MessageBox} This message box
10483          */
10484         updateText : function(text){
10485             if(!dlg.isVisible() && !opt.width){
10486                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10487             }
10488             msgEl.innerHTML = text || '&#160;';
10489       
10490             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10491             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10492             var w = Math.max(
10493                     Math.min(opt.width || cw , this.maxWidth), 
10494                     Math.max(opt.minWidth || this.minWidth, bwidth)
10495             );
10496             if(opt.prompt){
10497                 activeTextEl.setWidth(w);
10498             }
10499             if(dlg.isVisible()){
10500                 dlg.fixedcenter = false;
10501             }
10502             // to big, make it scroll. = But as usual stupid IE does not support
10503             // !important..
10504             
10505             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10506                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10507                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10508             } else {
10509                 bodyEl.dom.style.height = '';
10510                 bodyEl.dom.style.overflowY = '';
10511             }
10512             if (cw > w) {
10513                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10514             } else {
10515                 bodyEl.dom.style.overflowX = '';
10516             }
10517             
10518             dlg.setContentSize(w, bodyEl.getHeight());
10519             if(dlg.isVisible()){
10520                 dlg.fixedcenter = true;
10521             }
10522             return this;
10523         },
10524
10525         /**
10526          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10527          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10528          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10529          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10530          * @return {Roo.MessageBox} This message box
10531          */
10532         updateProgress : function(value, text){
10533             if(text){
10534                 this.updateText(text);
10535             }
10536             if (pp) { // weird bug on my firefox - for some reason this is not defined
10537                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10538             }
10539             return this;
10540         },        
10541
10542         /**
10543          * Returns true if the message box is currently displayed
10544          * @return {Boolean} True if the message box is visible, else false
10545          */
10546         isVisible : function(){
10547             return dlg && dlg.isVisible();  
10548         },
10549
10550         /**
10551          * Hides the message box if it is displayed
10552          */
10553         hide : function(){
10554             if(this.isVisible()){
10555                 dlg.hide();
10556             }  
10557         },
10558
10559         /**
10560          * Displays a new message box, or reinitializes an existing message box, based on the config options
10561          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10562          * The following config object properties are supported:
10563          * <pre>
10564 Property    Type             Description
10565 ----------  ---------------  ------------------------------------------------------------------------------------
10566 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10567                                    closes (defaults to undefined)
10568 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10569                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10570 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10571                                    progress and wait dialogs will ignore this property and always hide the
10572                                    close button as they can only be closed programmatically.
10573 cls               String           A custom CSS class to apply to the message box element
10574 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10575                                    displayed (defaults to 75)
10576 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10577                                    function will be btn (the name of the button that was clicked, if applicable,
10578                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10579                                    Progress and wait dialogs will ignore this option since they do not respond to
10580                                    user actions and can only be closed programmatically, so any required function
10581                                    should be called by the same code after it closes the dialog.
10582 icon              String           A CSS class that provides a background image to be used as an icon for
10583                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10584 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10585 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10586 modal             Boolean          False to allow user interaction with the page while the message box is
10587                                    displayed (defaults to true)
10588 msg               String           A string that will replace the existing message box body text (defaults
10589                                    to the XHTML-compliant non-breaking space character '&#160;')
10590 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10591 progress          Boolean          True to display a progress bar (defaults to false)
10592 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10593 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10594 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10595 title             String           The title text
10596 value             String           The string value to set into the active textbox element if displayed
10597 wait              Boolean          True to display a progress bar (defaults to false)
10598 width             Number           The width of the dialog in pixels
10599 </pre>
10600          *
10601          * Example usage:
10602          * <pre><code>
10603 Roo.Msg.show({
10604    title: 'Address',
10605    msg: 'Please enter your address:',
10606    width: 300,
10607    buttons: Roo.MessageBox.OKCANCEL,
10608    multiline: true,
10609    fn: saveAddress,
10610    animEl: 'addAddressBtn'
10611 });
10612 </code></pre>
10613          * @param {Object} config Configuration options
10614          * @return {Roo.MessageBox} This message box
10615          */
10616         show : function(options)
10617         {
10618             
10619             // this causes nightmares if you show one dialog after another
10620             // especially on callbacks..
10621              
10622             if(this.isVisible()){
10623                 
10624                 this.hide();
10625                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10626                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10627                 Roo.log("New Dialog Message:" +  options.msg )
10628                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10629                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10630                 
10631             }
10632             var d = this.getDialog();
10633             opt = options;
10634             d.setTitle(opt.title || "&#160;");
10635             d.close.setDisplayed(opt.closable !== false);
10636             activeTextEl = textboxEl;
10637             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10638             if(opt.prompt){
10639                 if(opt.multiline){
10640                     textboxEl.hide();
10641                     textareaEl.show();
10642                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10643                         opt.multiline : this.defaultTextHeight);
10644                     activeTextEl = textareaEl;
10645                 }else{
10646                     textboxEl.show();
10647                     textareaEl.hide();
10648                 }
10649             }else{
10650                 textboxEl.hide();
10651                 textareaEl.hide();
10652             }
10653             progressEl.setDisplayed(opt.progress === true);
10654             this.updateProgress(0);
10655             activeTextEl.dom.value = opt.value || "";
10656             if(opt.prompt){
10657                 dlg.setDefaultButton(activeTextEl);
10658             }else{
10659                 var bs = opt.buttons;
10660                 var db = null;
10661                 if(bs && bs.ok){
10662                     db = buttons["ok"];
10663                 }else if(bs && bs.yes){
10664                     db = buttons["yes"];
10665                 }
10666                 dlg.setDefaultButton(db);
10667             }
10668             bwidth = updateButtons(opt.buttons);
10669             this.updateText(opt.msg);
10670             if(opt.cls){
10671                 d.el.addClass(opt.cls);
10672             }
10673             d.proxyDrag = opt.proxyDrag === true;
10674             d.modal = opt.modal !== false;
10675             d.mask = opt.modal !== false ? mask : false;
10676             if(!d.isVisible()){
10677                 // force it to the end of the z-index stack so it gets a cursor in FF
10678                 document.body.appendChild(dlg.el.dom);
10679                 d.animateTarget = null;
10680                 d.show(options.animEl);
10681             }
10682             dlg.toFront();
10683             return this;
10684         },
10685
10686         /**
10687          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10688          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10689          * and closing the message box when the process is complete.
10690          * @param {String} title The title bar text
10691          * @param {String} msg The message box body text
10692          * @return {Roo.MessageBox} This message box
10693          */
10694         progress : function(title, msg){
10695             this.show({
10696                 title : title,
10697                 msg : msg,
10698                 buttons: false,
10699                 progress:true,
10700                 closable:false,
10701                 minWidth: this.minProgressWidth,
10702                 modal : true
10703             });
10704             return this;
10705         },
10706
10707         /**
10708          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10709          * If a callback function is passed it will be called after the user clicks the button, and the
10710          * id of the button that was clicked will be passed as the only parameter to the callback
10711          * (could also be the top-right close button).
10712          * @param {String} title The title bar text
10713          * @param {String} msg The message box body text
10714          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10715          * @param {Object} scope (optional) The scope of the callback function
10716          * @return {Roo.MessageBox} This message box
10717          */
10718         alert : function(title, msg, fn, scope){
10719             this.show({
10720                 title : title,
10721                 msg : msg,
10722                 buttons: this.OK,
10723                 fn: fn,
10724                 scope : scope,
10725                 modal : true
10726             });
10727             return this;
10728         },
10729
10730         /**
10731          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10732          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10733          * You are responsible for closing the message box when the process is complete.
10734          * @param {String} msg The message box body text
10735          * @param {String} title (optional) The title bar text
10736          * @return {Roo.MessageBox} This message box
10737          */
10738         wait : function(msg, title){
10739             this.show({
10740                 title : title,
10741                 msg : msg,
10742                 buttons: false,
10743                 closable:false,
10744                 progress:true,
10745                 modal:true,
10746                 width:300,
10747                 wait:true
10748             });
10749             waitTimer = Roo.TaskMgr.start({
10750                 run: function(i){
10751                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10752                 },
10753                 interval: 1000
10754             });
10755             return this;
10756         },
10757
10758         /**
10759          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10760          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10761          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10762          * @param {String} title The title bar text
10763          * @param {String} msg The message box body text
10764          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10765          * @param {Object} scope (optional) The scope of the callback function
10766          * @return {Roo.MessageBox} This message box
10767          */
10768         confirm : function(title, msg, fn, scope){
10769             this.show({
10770                 title : title,
10771                 msg : msg,
10772                 buttons: this.YESNO,
10773                 fn: fn,
10774                 scope : scope,
10775                 modal : true
10776             });
10777             return this;
10778         },
10779
10780         /**
10781          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10782          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10783          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10784          * (could also be the top-right close button) and the text that was entered will be passed as the two
10785          * parameters to the callback.
10786          * @param {String} title The title bar text
10787          * @param {String} msg The message box body text
10788          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10789          * @param {Object} scope (optional) The scope of the callback function
10790          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10791          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10792          * @return {Roo.MessageBox} This message box
10793          */
10794         prompt : function(title, msg, fn, scope, multiline){
10795             this.show({
10796                 title : title,
10797                 msg : msg,
10798                 buttons: this.OKCANCEL,
10799                 fn: fn,
10800                 minWidth:250,
10801                 scope : scope,
10802                 prompt:true,
10803                 multiline: multiline,
10804                 modal : true
10805             });
10806             return this;
10807         },
10808
10809         /**
10810          * Button config that displays a single OK button
10811          * @type Object
10812          */
10813         OK : {ok:true},
10814         /**
10815          * Button config that displays Yes and No buttons
10816          * @type Object
10817          */
10818         YESNO : {yes:true, no:true},
10819         /**
10820          * Button config that displays OK and Cancel buttons
10821          * @type Object
10822          */
10823         OKCANCEL : {ok:true, cancel:true},
10824         /**
10825          * Button config that displays Yes, No and Cancel buttons
10826          * @type Object
10827          */
10828         YESNOCANCEL : {yes:true, no:true, cancel:true},
10829
10830         /**
10831          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10832          * @type Number
10833          */
10834         defaultTextHeight : 75,
10835         /**
10836          * The maximum width in pixels of the message box (defaults to 600)
10837          * @type Number
10838          */
10839         maxWidth : 600,
10840         /**
10841          * The minimum width in pixels of the message box (defaults to 100)
10842          * @type Number
10843          */
10844         minWidth : 100,
10845         /**
10846          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10847          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10848          * @type Number
10849          */
10850         minProgressWidth : 250,
10851         /**
10852          * An object containing the default button text strings that can be overriden for localized language support.
10853          * Supported properties are: ok, cancel, yes and no.
10854          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10855          * @type Object
10856          */
10857         buttonText : {
10858             ok : "OK",
10859             cancel : "Cancel",
10860             yes : "Yes",
10861             no : "No"
10862         }
10863     };
10864 }();
10865
10866 /**
10867  * Shorthand for {@link Roo.MessageBox}
10868  */
10869 Roo.Msg = Roo.MessageBox;/*
10870  * Based on:
10871  * Ext JS Library 1.1.1
10872  * Copyright(c) 2006-2007, Ext JS, LLC.
10873  *
10874  * Originally Released Under LGPL - original licence link has changed is not relivant.
10875  *
10876  * Fork - LGPL
10877  * <script type="text/javascript">
10878  */
10879 /**
10880  * @class Roo.QuickTips
10881  * Provides attractive and customizable tooltips for any element.
10882  * @static
10883  */
10884 Roo.QuickTips = function(){
10885     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10886     var ce, bd, xy, dd;
10887     var visible = false, disabled = true, inited = false;
10888     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10889     
10890     var onOver = function(e){
10891         if(disabled){
10892             return;
10893         }
10894         var t = e.getTarget();
10895         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10896             return;
10897         }
10898         if(ce && t == ce.el){
10899             clearTimeout(hideProc);
10900             return;
10901         }
10902         if(t && tagEls[t.id]){
10903             tagEls[t.id].el = t;
10904             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10905             return;
10906         }
10907         var ttp, et = Roo.fly(t);
10908         var ns = cfg.namespace;
10909         if(tm.interceptTitles && t.title){
10910             ttp = t.title;
10911             t.qtip = ttp;
10912             t.removeAttribute("title");
10913             e.preventDefault();
10914         }else{
10915             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10916         }
10917         if(ttp){
10918             showProc = show.defer(tm.showDelay, tm, [{
10919                 el: t, 
10920                 text: ttp.replace(/\\n/g,'<br/>'),
10921                 width: et.getAttributeNS(ns, cfg.width),
10922                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10923                 title: et.getAttributeNS(ns, cfg.title),
10924                     cls: et.getAttributeNS(ns, cfg.cls)
10925             }]);
10926         }
10927     };
10928     
10929     var onOut = function(e){
10930         clearTimeout(showProc);
10931         var t = e.getTarget();
10932         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10933             hideProc = setTimeout(hide, tm.hideDelay);
10934         }
10935     };
10936     
10937     var onMove = function(e){
10938         if(disabled){
10939             return;
10940         }
10941         xy = e.getXY();
10942         xy[1] += 18;
10943         if(tm.trackMouse && ce){
10944             el.setXY(xy);
10945         }
10946     };
10947     
10948     var onDown = function(e){
10949         clearTimeout(showProc);
10950         clearTimeout(hideProc);
10951         if(!e.within(el)){
10952             if(tm.hideOnClick){
10953                 hide();
10954                 tm.disable();
10955                 tm.enable.defer(100, tm);
10956             }
10957         }
10958     };
10959     
10960     var getPad = function(){
10961         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10962     };
10963
10964     var show = function(o){
10965         if(disabled){
10966             return;
10967         }
10968         clearTimeout(dismissProc);
10969         ce = o;
10970         if(removeCls){ // in case manually hidden
10971             el.removeClass(removeCls);
10972             removeCls = null;
10973         }
10974         if(ce.cls){
10975             el.addClass(ce.cls);
10976             removeCls = ce.cls;
10977         }
10978         if(ce.title){
10979             tipTitle.update(ce.title);
10980             tipTitle.show();
10981         }else{
10982             tipTitle.update('');
10983             tipTitle.hide();
10984         }
10985         el.dom.style.width  = tm.maxWidth+'px';
10986         //tipBody.dom.style.width = '';
10987         tipBodyText.update(o.text);
10988         var p = getPad(), w = ce.width;
10989         if(!w){
10990             var td = tipBodyText.dom;
10991             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10992             if(aw > tm.maxWidth){
10993                 w = tm.maxWidth;
10994             }else if(aw < tm.minWidth){
10995                 w = tm.minWidth;
10996             }else{
10997                 w = aw;
10998             }
10999         }
11000         //tipBody.setWidth(w);
11001         el.setWidth(parseInt(w, 10) + p);
11002         if(ce.autoHide === false){
11003             close.setDisplayed(true);
11004             if(dd){
11005                 dd.unlock();
11006             }
11007         }else{
11008             close.setDisplayed(false);
11009             if(dd){
11010                 dd.lock();
11011             }
11012         }
11013         if(xy){
11014             el.avoidY = xy[1]-18;
11015             el.setXY(xy);
11016         }
11017         if(tm.animate){
11018             el.setOpacity(.1);
11019             el.setStyle("visibility", "visible");
11020             el.fadeIn({callback: afterShow});
11021         }else{
11022             afterShow();
11023         }
11024     };
11025     
11026     var afterShow = function(){
11027         if(ce){
11028             el.show();
11029             esc.enable();
11030             if(tm.autoDismiss && ce.autoHide !== false){
11031                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11032             }
11033         }
11034     };
11035     
11036     var hide = function(noanim){
11037         clearTimeout(dismissProc);
11038         clearTimeout(hideProc);
11039         ce = null;
11040         if(el.isVisible()){
11041             esc.disable();
11042             if(noanim !== true && tm.animate){
11043                 el.fadeOut({callback: afterHide});
11044             }else{
11045                 afterHide();
11046             } 
11047         }
11048     };
11049     
11050     var afterHide = function(){
11051         el.hide();
11052         if(removeCls){
11053             el.removeClass(removeCls);
11054             removeCls = null;
11055         }
11056     };
11057     
11058     return {
11059         /**
11060         * @cfg {Number} minWidth
11061         * The minimum width of the quick tip (defaults to 40)
11062         */
11063        minWidth : 40,
11064         /**
11065         * @cfg {Number} maxWidth
11066         * The maximum width of the quick tip (defaults to 300)
11067         */
11068        maxWidth : 300,
11069         /**
11070         * @cfg {Boolean} interceptTitles
11071         * True to automatically use the element's DOM title value if available (defaults to false)
11072         */
11073        interceptTitles : false,
11074         /**
11075         * @cfg {Boolean} trackMouse
11076         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11077         */
11078        trackMouse : false,
11079         /**
11080         * @cfg {Boolean} hideOnClick
11081         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11082         */
11083        hideOnClick : true,
11084         /**
11085         * @cfg {Number} showDelay
11086         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11087         */
11088        showDelay : 500,
11089         /**
11090         * @cfg {Number} hideDelay
11091         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11092         */
11093        hideDelay : 200,
11094         /**
11095         * @cfg {Boolean} autoHide
11096         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11097         * Used in conjunction with hideDelay.
11098         */
11099        autoHide : true,
11100         /**
11101         * @cfg {Boolean}
11102         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11103         * (defaults to true).  Used in conjunction with autoDismissDelay.
11104         */
11105        autoDismiss : true,
11106         /**
11107         * @cfg {Number}
11108         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11109         */
11110        autoDismissDelay : 5000,
11111        /**
11112         * @cfg {Boolean} animate
11113         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11114         */
11115        animate : false,
11116
11117        /**
11118         * @cfg {String} title
11119         * Title text to display (defaults to '').  This can be any valid HTML markup.
11120         */
11121         title: '',
11122        /**
11123         * @cfg {String} text
11124         * Body text to display (defaults to '').  This can be any valid HTML markup.
11125         */
11126         text : '',
11127        /**
11128         * @cfg {String} cls
11129         * A CSS class to apply to the base quick tip element (defaults to '').
11130         */
11131         cls : '',
11132        /**
11133         * @cfg {Number} width
11134         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11135         * minWidth or maxWidth.
11136         */
11137         width : null,
11138
11139     /**
11140      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11141      * or display QuickTips in a page.
11142      */
11143        init : function(){
11144           tm = Roo.QuickTips;
11145           cfg = tm.tagConfig;
11146           if(!inited){
11147               if(!Roo.isReady){ // allow calling of init() before onReady
11148                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11149                   return;
11150               }
11151               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11152               el.fxDefaults = {stopFx: true};
11153               // maximum custom styling
11154               //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>');
11155               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>');              
11156               tipTitle = el.child('h3');
11157               tipTitle.enableDisplayMode("block");
11158               tipBody = el.child('div.x-tip-bd');
11159               tipBodyText = el.child('div.x-tip-bd-inner');
11160               //bdLeft = el.child('div.x-tip-bd-left');
11161               //bdRight = el.child('div.x-tip-bd-right');
11162               close = el.child('div.x-tip-close');
11163               close.enableDisplayMode("block");
11164               close.on("click", hide);
11165               var d = Roo.get(document);
11166               d.on("mousedown", onDown);
11167               d.on("mouseover", onOver);
11168               d.on("mouseout", onOut);
11169               d.on("mousemove", onMove);
11170               esc = d.addKeyListener(27, hide);
11171               esc.disable();
11172               if(Roo.dd.DD){
11173                   dd = el.initDD("default", null, {
11174                       onDrag : function(){
11175                           el.sync();  
11176                       }
11177                   });
11178                   dd.setHandleElId(tipTitle.id);
11179                   dd.lock();
11180               }
11181               inited = true;
11182           }
11183           this.enable(); 
11184        },
11185
11186     /**
11187      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11188      * are supported:
11189      * <pre>
11190 Property    Type                   Description
11191 ----------  ---------------------  ------------------------------------------------------------------------
11192 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11193      * </ul>
11194      * @param {Object} config The config object
11195      */
11196        register : function(config){
11197            var cs = config instanceof Array ? config : arguments;
11198            for(var i = 0, len = cs.length; i < len; i++) {
11199                var c = cs[i];
11200                var target = c.target;
11201                if(target){
11202                    if(target instanceof Array){
11203                        for(var j = 0, jlen = target.length; j < jlen; j++){
11204                            tagEls[target[j]] = c;
11205                        }
11206                    }else{
11207                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11208                    }
11209                }
11210            }
11211        },
11212
11213     /**
11214      * Removes this quick tip from its element and destroys it.
11215      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11216      */
11217        unregister : function(el){
11218            delete tagEls[Roo.id(el)];
11219        },
11220
11221     /**
11222      * Enable this quick tip.
11223      */
11224        enable : function(){
11225            if(inited && disabled){
11226                locks.pop();
11227                if(locks.length < 1){
11228                    disabled = false;
11229                }
11230            }
11231        },
11232
11233     /**
11234      * Disable this quick tip.
11235      */
11236        disable : function(){
11237           disabled = true;
11238           clearTimeout(showProc);
11239           clearTimeout(hideProc);
11240           clearTimeout(dismissProc);
11241           if(ce){
11242               hide(true);
11243           }
11244           locks.push(1);
11245        },
11246
11247     /**
11248      * Returns true if the quick tip is enabled, else false.
11249      */
11250        isEnabled : function(){
11251             return !disabled;
11252        },
11253
11254         // private
11255        tagConfig : {
11256            namespace : "roo", // was ext?? this may break..
11257            alt_namespace : "ext",
11258            attribute : "qtip",
11259            width : "width",
11260            target : "target",
11261            title : "qtitle",
11262            hide : "hide",
11263            cls : "qclass"
11264        }
11265    };
11266 }();
11267
11268 // backwards compat
11269 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11270  * Based on:
11271  * Ext JS Library 1.1.1
11272  * Copyright(c) 2006-2007, Ext JS, LLC.
11273  *
11274  * Originally Released Under LGPL - original licence link has changed is not relivant.
11275  *
11276  * Fork - LGPL
11277  * <script type="text/javascript">
11278  */
11279  
11280
11281 /**
11282  * @class Roo.tree.TreePanel
11283  * @extends Roo.data.Tree
11284  * @cfg {Roo.tree.TreeNode} root The root node
11285  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11286  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11287  * @cfg {Boolean} enableDD true to enable drag and drop
11288  * @cfg {Boolean} enableDrag true to enable just drag
11289  * @cfg {Boolean} enableDrop true to enable just drop
11290  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11291  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11292  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11293  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11294  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11295  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11296  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11297  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11298  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11299  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11300  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11301  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11302  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11303  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11304  * @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>
11305  * @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>
11306  * 
11307  * @constructor
11308  * @param {String/HTMLElement/Element} el The container element
11309  * @param {Object} config
11310  */
11311 Roo.tree.TreePanel = function(el, config){
11312     var root = false;
11313     var loader = false;
11314     if (config.root) {
11315         root = config.root;
11316         delete config.root;
11317     }
11318     if (config.loader) {
11319         loader = config.loader;
11320         delete config.loader;
11321     }
11322     
11323     Roo.apply(this, config);
11324     Roo.tree.TreePanel.superclass.constructor.call(this);
11325     this.el = Roo.get(el);
11326     this.el.addClass('x-tree');
11327     //console.log(root);
11328     if (root) {
11329         this.setRootNode( Roo.factory(root, Roo.tree));
11330     }
11331     if (loader) {
11332         this.loader = Roo.factory(loader, Roo.tree);
11333     }
11334    /**
11335     * Read-only. The id of the container element becomes this TreePanel's id.
11336     */
11337     this.id = this.el.id;
11338     this.addEvents({
11339         /**
11340         * @event beforeload
11341         * Fires before a node is loaded, return false to cancel
11342         * @param {Node} node The node being loaded
11343         */
11344         "beforeload" : true,
11345         /**
11346         * @event load
11347         * Fires when a node is loaded
11348         * @param {Node} node The node that was loaded
11349         */
11350         "load" : true,
11351         /**
11352         * @event textchange
11353         * Fires when the text for a node is changed
11354         * @param {Node} node The node
11355         * @param {String} text The new text
11356         * @param {String} oldText The old text
11357         */
11358         "textchange" : true,
11359         /**
11360         * @event beforeexpand
11361         * Fires before a node is expanded, return false to cancel.
11362         * @param {Node} node The node
11363         * @param {Boolean} deep
11364         * @param {Boolean} anim
11365         */
11366         "beforeexpand" : true,
11367         /**
11368         * @event beforecollapse
11369         * Fires before a node is collapsed, return false to cancel.
11370         * @param {Node} node The node
11371         * @param {Boolean} deep
11372         * @param {Boolean} anim
11373         */
11374         "beforecollapse" : true,
11375         /**
11376         * @event expand
11377         * Fires when a node is expanded
11378         * @param {Node} node The node
11379         */
11380         "expand" : true,
11381         /**
11382         * @event disabledchange
11383         * Fires when the disabled status of a node changes
11384         * @param {Node} node The node
11385         * @param {Boolean} disabled
11386         */
11387         "disabledchange" : true,
11388         /**
11389         * @event collapse
11390         * Fires when a node is collapsed
11391         * @param {Node} node The node
11392         */
11393         "collapse" : true,
11394         /**
11395         * @event beforeclick
11396         * Fires before click processing on a node. Return false to cancel the default action.
11397         * @param {Node} node The node
11398         * @param {Roo.EventObject} e The event object
11399         */
11400         "beforeclick":true,
11401         /**
11402         * @event checkchange
11403         * Fires when a node with a checkbox's checked property changes
11404         * @param {Node} this This node
11405         * @param {Boolean} checked
11406         */
11407         "checkchange":true,
11408         /**
11409         * @event click
11410         * Fires when a node is clicked
11411         * @param {Node} node The node
11412         * @param {Roo.EventObject} e The event object
11413         */
11414         "click":true,
11415         /**
11416         * @event dblclick
11417         * Fires when a node is double clicked
11418         * @param {Node} node The node
11419         * @param {Roo.EventObject} e The event object
11420         */
11421         "dblclick":true,
11422         /**
11423         * @event contextmenu
11424         * Fires when a node is right clicked
11425         * @param {Node} node The node
11426         * @param {Roo.EventObject} e The event object
11427         */
11428         "contextmenu":true,
11429         /**
11430         * @event beforechildrenrendered
11431         * Fires right before the child nodes for a node are rendered
11432         * @param {Node} node The node
11433         */
11434         "beforechildrenrendered":true,
11435         /**
11436         * @event startdrag
11437         * Fires when a node starts being dragged
11438         * @param {Roo.tree.TreePanel} this
11439         * @param {Roo.tree.TreeNode} node
11440         * @param {event} e The raw browser event
11441         */ 
11442        "startdrag" : true,
11443        /**
11444         * @event enddrag
11445         * Fires when a drag operation is complete
11446         * @param {Roo.tree.TreePanel} this
11447         * @param {Roo.tree.TreeNode} node
11448         * @param {event} e The raw browser event
11449         */
11450        "enddrag" : true,
11451        /**
11452         * @event dragdrop
11453         * Fires when a dragged node is dropped on a valid DD target
11454         * @param {Roo.tree.TreePanel} this
11455         * @param {Roo.tree.TreeNode} node
11456         * @param {DD} dd The dd it was dropped on
11457         * @param {event} e The raw browser event
11458         */
11459        "dragdrop" : true,
11460        /**
11461         * @event beforenodedrop
11462         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11463         * passed to handlers has the following properties:<br />
11464         * <ul style="padding:5px;padding-left:16px;">
11465         * <li>tree - The TreePanel</li>
11466         * <li>target - The node being targeted for the drop</li>
11467         * <li>data - The drag data from the drag source</li>
11468         * <li>point - The point of the drop - append, above or below</li>
11469         * <li>source - The drag source</li>
11470         * <li>rawEvent - Raw mouse event</li>
11471         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11472         * to be inserted by setting them on this object.</li>
11473         * <li>cancel - Set this to true to cancel the drop.</li>
11474         * </ul>
11475         * @param {Object} dropEvent
11476         */
11477        "beforenodedrop" : true,
11478        /**
11479         * @event nodedrop
11480         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11481         * passed to handlers has the following properties:<br />
11482         * <ul style="padding:5px;padding-left:16px;">
11483         * <li>tree - The TreePanel</li>
11484         * <li>target - The node being targeted for the drop</li>
11485         * <li>data - The drag data from the drag source</li>
11486         * <li>point - The point of the drop - append, above or below</li>
11487         * <li>source - The drag source</li>
11488         * <li>rawEvent - Raw mouse event</li>
11489         * <li>dropNode - Dropped node(s).</li>
11490         * </ul>
11491         * @param {Object} dropEvent
11492         */
11493        "nodedrop" : true,
11494         /**
11495         * @event nodedragover
11496         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11497         * passed to handlers has the following properties:<br />
11498         * <ul style="padding:5px;padding-left:16px;">
11499         * <li>tree - The TreePanel</li>
11500         * <li>target - The node being targeted for the drop</li>
11501         * <li>data - The drag data from the drag source</li>
11502         * <li>point - The point of the drop - append, above or below</li>
11503         * <li>source - The drag source</li>
11504         * <li>rawEvent - Raw mouse event</li>
11505         * <li>dropNode - Drop node(s) provided by the source.</li>
11506         * <li>cancel - Set this to true to signal drop not allowed.</li>
11507         * </ul>
11508         * @param {Object} dragOverEvent
11509         */
11510        "nodedragover" : true,
11511        /**
11512         * @event appendnode
11513         * Fires when append node to the tree
11514         * @param {Roo.tree.TreePanel} this
11515         * @param {Roo.tree.TreeNode} node
11516         * @param {Number} index The index of the newly appended node
11517         */
11518        "appendnode" : true
11519         
11520     });
11521     if(this.singleExpand){
11522        this.on("beforeexpand", this.restrictExpand, this);
11523     }
11524     if (this.editor) {
11525         this.editor.tree = this;
11526         this.editor = Roo.factory(this.editor, Roo.tree);
11527     }
11528     
11529     if (this.selModel) {
11530         this.selModel = Roo.factory(this.selModel, Roo.tree);
11531     }
11532    
11533 };
11534 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11535     rootVisible : true,
11536     animate: Roo.enableFx,
11537     lines : true,
11538     enableDD : false,
11539     hlDrop : Roo.enableFx,
11540   
11541     renderer: false,
11542     
11543     rendererTip: false,
11544     // private
11545     restrictExpand : function(node){
11546         var p = node.parentNode;
11547         if(p){
11548             if(p.expandedChild && p.expandedChild.parentNode == p){
11549                 p.expandedChild.collapse();
11550             }
11551             p.expandedChild = node;
11552         }
11553     },
11554
11555     // private override
11556     setRootNode : function(node){
11557         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11558         if(!this.rootVisible){
11559             node.ui = new Roo.tree.RootTreeNodeUI(node);
11560         }
11561         return node;
11562     },
11563
11564     /**
11565      * Returns the container element for this TreePanel
11566      */
11567     getEl : function(){
11568         return this.el;
11569     },
11570
11571     /**
11572      * Returns the default TreeLoader for this TreePanel
11573      */
11574     getLoader : function(){
11575         return this.loader;
11576     },
11577
11578     /**
11579      * Expand all nodes
11580      */
11581     expandAll : function(){
11582         this.root.expand(true);
11583     },
11584
11585     /**
11586      * Collapse all nodes
11587      */
11588     collapseAll : function(){
11589         this.root.collapse(true);
11590     },
11591
11592     /**
11593      * Returns the selection model used by this TreePanel
11594      */
11595     getSelectionModel : function(){
11596         if(!this.selModel){
11597             this.selModel = new Roo.tree.DefaultSelectionModel();
11598         }
11599         return this.selModel;
11600     },
11601
11602     /**
11603      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11604      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11605      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11606      * @return {Array}
11607      */
11608     getChecked : function(a, startNode){
11609         startNode = startNode || this.root;
11610         var r = [];
11611         var f = function(){
11612             if(this.attributes.checked){
11613                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11614             }
11615         }
11616         startNode.cascade(f);
11617         return r;
11618     },
11619
11620     /**
11621      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11622      * @param {String} path
11623      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11624      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11625      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11626      */
11627     expandPath : function(path, attr, callback){
11628         attr = attr || "id";
11629         var keys = path.split(this.pathSeparator);
11630         var curNode = this.root;
11631         if(curNode.attributes[attr] != keys[1]){ // invalid root
11632             if(callback){
11633                 callback(false, null);
11634             }
11635             return;
11636         }
11637         var index = 1;
11638         var f = function(){
11639             if(++index == keys.length){
11640                 if(callback){
11641                     callback(true, curNode);
11642                 }
11643                 return;
11644             }
11645             var c = curNode.findChild(attr, keys[index]);
11646             if(!c){
11647                 if(callback){
11648                     callback(false, curNode);
11649                 }
11650                 return;
11651             }
11652             curNode = c;
11653             c.expand(false, false, f);
11654         };
11655         curNode.expand(false, false, f);
11656     },
11657
11658     /**
11659      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11660      * @param {String} path
11661      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11662      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11663      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11664      */
11665     selectPath : function(path, attr, callback){
11666         attr = attr || "id";
11667         var keys = path.split(this.pathSeparator);
11668         var v = keys.pop();
11669         if(keys.length > 0){
11670             var f = function(success, node){
11671                 if(success && node){
11672                     var n = node.findChild(attr, v);
11673                     if(n){
11674                         n.select();
11675                         if(callback){
11676                             callback(true, n);
11677                         }
11678                     }else if(callback){
11679                         callback(false, n);
11680                     }
11681                 }else{
11682                     if(callback){
11683                         callback(false, n);
11684                     }
11685                 }
11686             };
11687             this.expandPath(keys.join(this.pathSeparator), attr, f);
11688         }else{
11689             this.root.select();
11690             if(callback){
11691                 callback(true, this.root);
11692             }
11693         }
11694     },
11695
11696     getTreeEl : function(){
11697         return this.el;
11698     },
11699
11700     /**
11701      * Trigger rendering of this TreePanel
11702      */
11703     render : function(){
11704         if (this.innerCt) {
11705             return this; // stop it rendering more than once!!
11706         }
11707         
11708         this.innerCt = this.el.createChild({tag:"ul",
11709                cls:"x-tree-root-ct " +
11710                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11711
11712         if(this.containerScroll){
11713             Roo.dd.ScrollManager.register(this.el);
11714         }
11715         if((this.enableDD || this.enableDrop) && !this.dropZone){
11716            /**
11717             * The dropZone used by this tree if drop is enabled
11718             * @type Roo.tree.TreeDropZone
11719             */
11720              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11721                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11722            });
11723         }
11724         if((this.enableDD || this.enableDrag) && !this.dragZone){
11725            /**
11726             * The dragZone used by this tree if drag is enabled
11727             * @type Roo.tree.TreeDragZone
11728             */
11729             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11730                ddGroup: this.ddGroup || "TreeDD",
11731                scroll: this.ddScroll
11732            });
11733         }
11734         this.getSelectionModel().init(this);
11735         if (!this.root) {
11736             Roo.log("ROOT not set in tree");
11737             return this;
11738         }
11739         this.root.render();
11740         if(!this.rootVisible){
11741             this.root.renderChildren();
11742         }
11743         return this;
11744     }
11745 });/*
11746  * Based on:
11747  * Ext JS Library 1.1.1
11748  * Copyright(c) 2006-2007, Ext JS, LLC.
11749  *
11750  * Originally Released Under LGPL - original licence link has changed is not relivant.
11751  *
11752  * Fork - LGPL
11753  * <script type="text/javascript">
11754  */
11755  
11756
11757 /**
11758  * @class Roo.tree.DefaultSelectionModel
11759  * @extends Roo.util.Observable
11760  * The default single selection for a TreePanel.
11761  * @param {Object} cfg Configuration
11762  */
11763 Roo.tree.DefaultSelectionModel = function(cfg){
11764    this.selNode = null;
11765    
11766    
11767    
11768    this.addEvents({
11769        /**
11770         * @event selectionchange
11771         * Fires when the selected node changes
11772         * @param {DefaultSelectionModel} this
11773         * @param {TreeNode} node the new selection
11774         */
11775        "selectionchange" : true,
11776
11777        /**
11778         * @event beforeselect
11779         * Fires before the selected node changes, return false to cancel the change
11780         * @param {DefaultSelectionModel} this
11781         * @param {TreeNode} node the new selection
11782         * @param {TreeNode} node the old selection
11783         */
11784        "beforeselect" : true
11785    });
11786    
11787     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11788 };
11789
11790 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11791     init : function(tree){
11792         this.tree = tree;
11793         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11794         tree.on("click", this.onNodeClick, this);
11795     },
11796     
11797     onNodeClick : function(node, e){
11798         if (e.ctrlKey && this.selNode == node)  {
11799             this.unselect(node);
11800             return;
11801         }
11802         this.select(node);
11803     },
11804     
11805     /**
11806      * Select a node.
11807      * @param {TreeNode} node The node to select
11808      * @return {TreeNode} The selected node
11809      */
11810     select : function(node){
11811         var last = this.selNode;
11812         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11813             if(last){
11814                 last.ui.onSelectedChange(false);
11815             }
11816             this.selNode = node;
11817             node.ui.onSelectedChange(true);
11818             this.fireEvent("selectionchange", this, node, last);
11819         }
11820         return node;
11821     },
11822     
11823     /**
11824      * Deselect a node.
11825      * @param {TreeNode} node The node to unselect
11826      */
11827     unselect : function(node){
11828         if(this.selNode == node){
11829             this.clearSelections();
11830         }    
11831     },
11832     
11833     /**
11834      * Clear all selections
11835      */
11836     clearSelections : function(){
11837         var n = this.selNode;
11838         if(n){
11839             n.ui.onSelectedChange(false);
11840             this.selNode = null;
11841             this.fireEvent("selectionchange", this, null);
11842         }
11843         return n;
11844     },
11845     
11846     /**
11847      * Get the selected node
11848      * @return {TreeNode} The selected node
11849      */
11850     getSelectedNode : function(){
11851         return this.selNode;    
11852     },
11853     
11854     /**
11855      * Returns true if the node is selected
11856      * @param {TreeNode} node The node to check
11857      * @return {Boolean}
11858      */
11859     isSelected : function(node){
11860         return this.selNode == node;  
11861     },
11862
11863     /**
11864      * Selects the node above the selected node in the tree, intelligently walking the nodes
11865      * @return TreeNode The new selection
11866      */
11867     selectPrevious : function(){
11868         var s = this.selNode || this.lastSelNode;
11869         if(!s){
11870             return null;
11871         }
11872         var ps = s.previousSibling;
11873         if(ps){
11874             if(!ps.isExpanded() || ps.childNodes.length < 1){
11875                 return this.select(ps);
11876             } else{
11877                 var lc = ps.lastChild;
11878                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11879                     lc = lc.lastChild;
11880                 }
11881                 return this.select(lc);
11882             }
11883         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11884             return this.select(s.parentNode);
11885         }
11886         return null;
11887     },
11888
11889     /**
11890      * Selects the node above the selected node in the tree, intelligently walking the nodes
11891      * @return TreeNode The new selection
11892      */
11893     selectNext : function(){
11894         var s = this.selNode || this.lastSelNode;
11895         if(!s){
11896             return null;
11897         }
11898         if(s.firstChild && s.isExpanded()){
11899              return this.select(s.firstChild);
11900          }else if(s.nextSibling){
11901              return this.select(s.nextSibling);
11902          }else if(s.parentNode){
11903             var newS = null;
11904             s.parentNode.bubble(function(){
11905                 if(this.nextSibling){
11906                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11907                     return false;
11908                 }
11909             });
11910             return newS;
11911          }
11912         return null;
11913     },
11914
11915     onKeyDown : function(e){
11916         var s = this.selNode || this.lastSelNode;
11917         // undesirable, but required
11918         var sm = this;
11919         if(!s){
11920             return;
11921         }
11922         var k = e.getKey();
11923         switch(k){
11924              case e.DOWN:
11925                  e.stopEvent();
11926                  this.selectNext();
11927              break;
11928              case e.UP:
11929                  e.stopEvent();
11930                  this.selectPrevious();
11931              break;
11932              case e.RIGHT:
11933                  e.preventDefault();
11934                  if(s.hasChildNodes()){
11935                      if(!s.isExpanded()){
11936                          s.expand();
11937                      }else if(s.firstChild){
11938                          this.select(s.firstChild, e);
11939                      }
11940                  }
11941              break;
11942              case e.LEFT:
11943                  e.preventDefault();
11944                  if(s.hasChildNodes() && s.isExpanded()){
11945                      s.collapse();
11946                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11947                      this.select(s.parentNode, e);
11948                  }
11949              break;
11950         };
11951     }
11952 });
11953
11954 /**
11955  * @class Roo.tree.MultiSelectionModel
11956  * @extends Roo.util.Observable
11957  * Multi selection for a TreePanel.
11958  * @param {Object} cfg Configuration
11959  */
11960 Roo.tree.MultiSelectionModel = function(){
11961    this.selNodes = [];
11962    this.selMap = {};
11963    this.addEvents({
11964        /**
11965         * @event selectionchange
11966         * Fires when the selected nodes change
11967         * @param {MultiSelectionModel} this
11968         * @param {Array} nodes Array of the selected nodes
11969         */
11970        "selectionchange" : true
11971    });
11972    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11973    
11974 };
11975
11976 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11977     init : function(tree){
11978         this.tree = tree;
11979         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11980         tree.on("click", this.onNodeClick, this);
11981     },
11982     
11983     onNodeClick : function(node, e){
11984         this.select(node, e, e.ctrlKey);
11985     },
11986     
11987     /**
11988      * Select a node.
11989      * @param {TreeNode} node The node to select
11990      * @param {EventObject} e (optional) An event associated with the selection
11991      * @param {Boolean} keepExisting True to retain existing selections
11992      * @return {TreeNode} The selected node
11993      */
11994     select : function(node, e, keepExisting){
11995         if(keepExisting !== true){
11996             this.clearSelections(true);
11997         }
11998         if(this.isSelected(node)){
11999             this.lastSelNode = node;
12000             return node;
12001         }
12002         this.selNodes.push(node);
12003         this.selMap[node.id] = node;
12004         this.lastSelNode = node;
12005         node.ui.onSelectedChange(true);
12006         this.fireEvent("selectionchange", this, this.selNodes);
12007         return node;
12008     },
12009     
12010     /**
12011      * Deselect a node.
12012      * @param {TreeNode} node The node to unselect
12013      */
12014     unselect : function(node){
12015         if(this.selMap[node.id]){
12016             node.ui.onSelectedChange(false);
12017             var sn = this.selNodes;
12018             var index = -1;
12019             if(sn.indexOf){
12020                 index = sn.indexOf(node);
12021             }else{
12022                 for(var i = 0, len = sn.length; i < len; i++){
12023                     if(sn[i] == node){
12024                         index = i;
12025                         break;
12026                     }
12027                 }
12028             }
12029             if(index != -1){
12030                 this.selNodes.splice(index, 1);
12031             }
12032             delete this.selMap[node.id];
12033             this.fireEvent("selectionchange", this, this.selNodes);
12034         }
12035     },
12036     
12037     /**
12038      * Clear all selections
12039      */
12040     clearSelections : function(suppressEvent){
12041         var sn = this.selNodes;
12042         if(sn.length > 0){
12043             for(var i = 0, len = sn.length; i < len; i++){
12044                 sn[i].ui.onSelectedChange(false);
12045             }
12046             this.selNodes = [];
12047             this.selMap = {};
12048             if(suppressEvent !== true){
12049                 this.fireEvent("selectionchange", this, this.selNodes);
12050             }
12051         }
12052     },
12053     
12054     /**
12055      * Returns true if the node is selected
12056      * @param {TreeNode} node The node to check
12057      * @return {Boolean}
12058      */
12059     isSelected : function(node){
12060         return this.selMap[node.id] ? true : false;  
12061     },
12062     
12063     /**
12064      * Returns an array of the selected nodes
12065      * @return {Array}
12066      */
12067     getSelectedNodes : function(){
12068         return this.selNodes;    
12069     },
12070
12071     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12072
12073     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12074
12075     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12076 });/*
12077  * Based on:
12078  * Ext JS Library 1.1.1
12079  * Copyright(c) 2006-2007, Ext JS, LLC.
12080  *
12081  * Originally Released Under LGPL - original licence link has changed is not relivant.
12082  *
12083  * Fork - LGPL
12084  * <script type="text/javascript">
12085  */
12086  
12087 /**
12088  * @class Roo.tree.TreeNode
12089  * @extends Roo.data.Node
12090  * @cfg {String} text The text for this node
12091  * @cfg {Boolean} expanded true to start the node expanded
12092  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12093  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12094  * @cfg {Boolean} disabled true to start the node disabled
12095  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12096  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12097  * @cfg {String} cls A css class to be added to the node
12098  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12099  * @cfg {String} href URL of the link used for the node (defaults to #)
12100  * @cfg {String} hrefTarget target frame for the link
12101  * @cfg {String} qtip An Ext QuickTip for the node
12102  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12103  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12104  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12105  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12106  * (defaults to undefined with no checkbox rendered)
12107  * @constructor
12108  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12109  */
12110 Roo.tree.TreeNode = function(attributes){
12111     attributes = attributes || {};
12112     if(typeof attributes == "string"){
12113         attributes = {text: attributes};
12114     }
12115     this.childrenRendered = false;
12116     this.rendered = false;
12117     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12118     this.expanded = attributes.expanded === true;
12119     this.isTarget = attributes.isTarget !== false;
12120     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12121     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12122
12123     /**
12124      * Read-only. The text for this node. To change it use setText().
12125      * @type String
12126      */
12127     this.text = attributes.text;
12128     /**
12129      * True if this node is disabled.
12130      * @type Boolean
12131      */
12132     this.disabled = attributes.disabled === true;
12133
12134     this.addEvents({
12135         /**
12136         * @event textchange
12137         * Fires when the text for this node is changed
12138         * @param {Node} this This node
12139         * @param {String} text The new text
12140         * @param {String} oldText The old text
12141         */
12142         "textchange" : true,
12143         /**
12144         * @event beforeexpand
12145         * Fires before this node is expanded, return false to cancel.
12146         * @param {Node} this This node
12147         * @param {Boolean} deep
12148         * @param {Boolean} anim
12149         */
12150         "beforeexpand" : true,
12151         /**
12152         * @event beforecollapse
12153         * Fires before this node is collapsed, return false to cancel.
12154         * @param {Node} this This node
12155         * @param {Boolean} deep
12156         * @param {Boolean} anim
12157         */
12158         "beforecollapse" : true,
12159         /**
12160         * @event expand
12161         * Fires when this node is expanded
12162         * @param {Node} this This node
12163         */
12164         "expand" : true,
12165         /**
12166         * @event disabledchange
12167         * Fires when the disabled status of this node changes
12168         * @param {Node} this This node
12169         * @param {Boolean} disabled
12170         */
12171         "disabledchange" : true,
12172         /**
12173         * @event collapse
12174         * Fires when this node is collapsed
12175         * @param {Node} this This node
12176         */
12177         "collapse" : true,
12178         /**
12179         * @event beforeclick
12180         * Fires before click processing. Return false to cancel the default action.
12181         * @param {Node} this This node
12182         * @param {Roo.EventObject} e The event object
12183         */
12184         "beforeclick":true,
12185         /**
12186         * @event checkchange
12187         * Fires when a node with a checkbox's checked property changes
12188         * @param {Node} this This node
12189         * @param {Boolean} checked
12190         */
12191         "checkchange":true,
12192         /**
12193         * @event click
12194         * Fires when this node is clicked
12195         * @param {Node} this This node
12196         * @param {Roo.EventObject} e The event object
12197         */
12198         "click":true,
12199         /**
12200         * @event dblclick
12201         * Fires when this node is double clicked
12202         * @param {Node} this This node
12203         * @param {Roo.EventObject} e The event object
12204         */
12205         "dblclick":true,
12206         /**
12207         * @event contextmenu
12208         * Fires when this node is right clicked
12209         * @param {Node} this This node
12210         * @param {Roo.EventObject} e The event object
12211         */
12212         "contextmenu":true,
12213         /**
12214         * @event beforechildrenrendered
12215         * Fires right before the child nodes for this node are rendered
12216         * @param {Node} this This node
12217         */
12218         "beforechildrenrendered":true
12219     });
12220
12221     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12222
12223     /**
12224      * Read-only. The UI for this node
12225      * @type TreeNodeUI
12226      */
12227     this.ui = new uiClass(this);
12228     
12229     // finally support items[]
12230     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12231         return;
12232     }
12233     
12234     
12235     Roo.each(this.attributes.items, function(c) {
12236         this.appendChild(Roo.factory(c,Roo.Tree));
12237     }, this);
12238     delete this.attributes.items;
12239     
12240     
12241     
12242 };
12243 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12244     preventHScroll: true,
12245     /**
12246      * Returns true if this node is expanded
12247      * @return {Boolean}
12248      */
12249     isExpanded : function(){
12250         return this.expanded;
12251     },
12252
12253     /**
12254      * Returns the UI object for this node
12255      * @return {TreeNodeUI}
12256      */
12257     getUI : function(){
12258         return this.ui;
12259     },
12260
12261     // private override
12262     setFirstChild : function(node){
12263         var of = this.firstChild;
12264         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12265         if(this.childrenRendered && of && node != of){
12266             of.renderIndent(true, true);
12267         }
12268         if(this.rendered){
12269             this.renderIndent(true, true);
12270         }
12271     },
12272
12273     // private override
12274     setLastChild : function(node){
12275         var ol = this.lastChild;
12276         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12277         if(this.childrenRendered && ol && node != ol){
12278             ol.renderIndent(true, true);
12279         }
12280         if(this.rendered){
12281             this.renderIndent(true, true);
12282         }
12283     },
12284
12285     // these methods are overridden to provide lazy rendering support
12286     // private override
12287     appendChild : function()
12288     {
12289         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12290         if(node && this.childrenRendered){
12291             node.render();
12292         }
12293         this.ui.updateExpandIcon();
12294         return node;
12295     },
12296
12297     // private override
12298     removeChild : function(node){
12299         this.ownerTree.getSelectionModel().unselect(node);
12300         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12301         // if it's been rendered remove dom node
12302         if(this.childrenRendered){
12303             node.ui.remove();
12304         }
12305         if(this.childNodes.length < 1){
12306             this.collapse(false, false);
12307         }else{
12308             this.ui.updateExpandIcon();
12309         }
12310         if(!this.firstChild) {
12311             this.childrenRendered = false;
12312         }
12313         return node;
12314     },
12315
12316     // private override
12317     insertBefore : function(node, refNode){
12318         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12319         if(newNode && refNode && this.childrenRendered){
12320             node.render();
12321         }
12322         this.ui.updateExpandIcon();
12323         return newNode;
12324     },
12325
12326     /**
12327      * Sets the text for this node
12328      * @param {String} text
12329      */
12330     setText : function(text){
12331         var oldText = this.text;
12332         this.text = text;
12333         this.attributes.text = text;
12334         if(this.rendered){ // event without subscribing
12335             this.ui.onTextChange(this, text, oldText);
12336         }
12337         this.fireEvent("textchange", this, text, oldText);
12338     },
12339
12340     /**
12341      * Triggers selection of this node
12342      */
12343     select : function(){
12344         this.getOwnerTree().getSelectionModel().select(this);
12345     },
12346
12347     /**
12348      * Triggers deselection of this node
12349      */
12350     unselect : function(){
12351         this.getOwnerTree().getSelectionModel().unselect(this);
12352     },
12353
12354     /**
12355      * Returns true if this node is selected
12356      * @return {Boolean}
12357      */
12358     isSelected : function(){
12359         return this.getOwnerTree().getSelectionModel().isSelected(this);
12360     },
12361
12362     /**
12363      * Expand this node.
12364      * @param {Boolean} deep (optional) True to expand all children as well
12365      * @param {Boolean} anim (optional) false to cancel the default animation
12366      * @param {Function} callback (optional) A callback to be called when
12367      * expanding this node completes (does not wait for deep expand to complete).
12368      * Called with 1 parameter, this node.
12369      */
12370     expand : function(deep, anim, callback){
12371         if(!this.expanded){
12372             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12373                 return;
12374             }
12375             if(!this.childrenRendered){
12376                 this.renderChildren();
12377             }
12378             this.expanded = true;
12379             
12380             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12381                 this.ui.animExpand(function(){
12382                     this.fireEvent("expand", this);
12383                     if(typeof callback == "function"){
12384                         callback(this);
12385                     }
12386                     if(deep === true){
12387                         this.expandChildNodes(true);
12388                     }
12389                 }.createDelegate(this));
12390                 return;
12391             }else{
12392                 this.ui.expand();
12393                 this.fireEvent("expand", this);
12394                 if(typeof callback == "function"){
12395                     callback(this);
12396                 }
12397             }
12398         }else{
12399            if(typeof callback == "function"){
12400                callback(this);
12401            }
12402         }
12403         if(deep === true){
12404             this.expandChildNodes(true);
12405         }
12406     },
12407
12408     isHiddenRoot : function(){
12409         return this.isRoot && !this.getOwnerTree().rootVisible;
12410     },
12411
12412     /**
12413      * Collapse this node.
12414      * @param {Boolean} deep (optional) True to collapse all children as well
12415      * @param {Boolean} anim (optional) false to cancel the default animation
12416      */
12417     collapse : function(deep, anim){
12418         if(this.expanded && !this.isHiddenRoot()){
12419             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12420                 return;
12421             }
12422             this.expanded = false;
12423             if((this.getOwnerTree().animate && anim !== false) || anim){
12424                 this.ui.animCollapse(function(){
12425                     this.fireEvent("collapse", this);
12426                     if(deep === true){
12427                         this.collapseChildNodes(true);
12428                     }
12429                 }.createDelegate(this));
12430                 return;
12431             }else{
12432                 this.ui.collapse();
12433                 this.fireEvent("collapse", this);
12434             }
12435         }
12436         if(deep === true){
12437             var cs = this.childNodes;
12438             for(var i = 0, len = cs.length; i < len; i++) {
12439                 cs[i].collapse(true, false);
12440             }
12441         }
12442     },
12443
12444     // private
12445     delayedExpand : function(delay){
12446         if(!this.expandProcId){
12447             this.expandProcId = this.expand.defer(delay, this);
12448         }
12449     },
12450
12451     // private
12452     cancelExpand : function(){
12453         if(this.expandProcId){
12454             clearTimeout(this.expandProcId);
12455         }
12456         this.expandProcId = false;
12457     },
12458
12459     /**
12460      * Toggles expanded/collapsed state of the node
12461      */
12462     toggle : function(){
12463         if(this.expanded){
12464             this.collapse();
12465         }else{
12466             this.expand();
12467         }
12468     },
12469
12470     /**
12471      * Ensures all parent nodes are expanded
12472      */
12473     ensureVisible : function(callback){
12474         var tree = this.getOwnerTree();
12475         tree.expandPath(this.parentNode.getPath(), false, function(){
12476             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12477             Roo.callback(callback);
12478         }.createDelegate(this));
12479     },
12480
12481     /**
12482      * Expand all child nodes
12483      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12484      */
12485     expandChildNodes : function(deep){
12486         var cs = this.childNodes;
12487         for(var i = 0, len = cs.length; i < len; i++) {
12488                 cs[i].expand(deep);
12489         }
12490     },
12491
12492     /**
12493      * Collapse all child nodes
12494      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12495      */
12496     collapseChildNodes : function(deep){
12497         var cs = this.childNodes;
12498         for(var i = 0, len = cs.length; i < len; i++) {
12499                 cs[i].collapse(deep);
12500         }
12501     },
12502
12503     /**
12504      * Disables this node
12505      */
12506     disable : function(){
12507         this.disabled = true;
12508         this.unselect();
12509         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12510             this.ui.onDisableChange(this, true);
12511         }
12512         this.fireEvent("disabledchange", this, true);
12513     },
12514
12515     /**
12516      * Enables this node
12517      */
12518     enable : function(){
12519         this.disabled = false;
12520         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12521             this.ui.onDisableChange(this, false);
12522         }
12523         this.fireEvent("disabledchange", this, false);
12524     },
12525
12526     // private
12527     renderChildren : function(suppressEvent){
12528         if(suppressEvent !== false){
12529             this.fireEvent("beforechildrenrendered", this);
12530         }
12531         var cs = this.childNodes;
12532         for(var i = 0, len = cs.length; i < len; i++){
12533             cs[i].render(true);
12534         }
12535         this.childrenRendered = true;
12536     },
12537
12538     // private
12539     sort : function(fn, scope){
12540         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12541         if(this.childrenRendered){
12542             var cs = this.childNodes;
12543             for(var i = 0, len = cs.length; i < len; i++){
12544                 cs[i].render(true);
12545             }
12546         }
12547     },
12548
12549     // private
12550     render : function(bulkRender){
12551         this.ui.render(bulkRender);
12552         if(!this.rendered){
12553             this.rendered = true;
12554             if(this.expanded){
12555                 this.expanded = false;
12556                 this.expand(false, false);
12557             }
12558         }
12559     },
12560
12561     // private
12562     renderIndent : function(deep, refresh){
12563         if(refresh){
12564             this.ui.childIndent = null;
12565         }
12566         this.ui.renderIndent();
12567         if(deep === true && this.childrenRendered){
12568             var cs = this.childNodes;
12569             for(var i = 0, len = cs.length; i < len; i++){
12570                 cs[i].renderIndent(true, refresh);
12571             }
12572         }
12573     }
12574 });/*
12575  * Based on:
12576  * Ext JS Library 1.1.1
12577  * Copyright(c) 2006-2007, Ext JS, LLC.
12578  *
12579  * Originally Released Under LGPL - original licence link has changed is not relivant.
12580  *
12581  * Fork - LGPL
12582  * <script type="text/javascript">
12583  */
12584  
12585 /**
12586  * @class Roo.tree.AsyncTreeNode
12587  * @extends Roo.tree.TreeNode
12588  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12589  * @constructor
12590  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12591  */
12592  Roo.tree.AsyncTreeNode = function(config){
12593     this.loaded = false;
12594     this.loading = false;
12595     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12596     /**
12597     * @event beforeload
12598     * Fires before this node is loaded, return false to cancel
12599     * @param {Node} this This node
12600     */
12601     this.addEvents({'beforeload':true, 'load': true});
12602     /**
12603     * @event load
12604     * Fires when this node is loaded
12605     * @param {Node} this This node
12606     */
12607     /**
12608      * The loader used by this node (defaults to using the tree's defined loader)
12609      * @type TreeLoader
12610      * @property loader
12611      */
12612 };
12613 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12614     expand : function(deep, anim, callback){
12615         if(this.loading){ // if an async load is already running, waiting til it's done
12616             var timer;
12617             var f = function(){
12618                 if(!this.loading){ // done loading
12619                     clearInterval(timer);
12620                     this.expand(deep, anim, callback);
12621                 }
12622             }.createDelegate(this);
12623             timer = setInterval(f, 200);
12624             return;
12625         }
12626         if(!this.loaded){
12627             if(this.fireEvent("beforeload", this) === false){
12628                 return;
12629             }
12630             this.loading = true;
12631             this.ui.beforeLoad(this);
12632             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12633             if(loader){
12634                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12635                 return;
12636             }
12637         }
12638         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12639     },
12640     
12641     /**
12642      * Returns true if this node is currently loading
12643      * @return {Boolean}
12644      */
12645     isLoading : function(){
12646         return this.loading;  
12647     },
12648     
12649     loadComplete : function(deep, anim, callback){
12650         this.loading = false;
12651         this.loaded = true;
12652         this.ui.afterLoad(this);
12653         this.fireEvent("load", this);
12654         this.expand(deep, anim, callback);
12655     },
12656     
12657     /**
12658      * Returns true if this node has been loaded
12659      * @return {Boolean}
12660      */
12661     isLoaded : function(){
12662         return this.loaded;
12663     },
12664     
12665     hasChildNodes : function(){
12666         if(!this.isLeaf() && !this.loaded){
12667             return true;
12668         }else{
12669             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12670         }
12671     },
12672
12673     /**
12674      * Trigger a reload for this node
12675      * @param {Function} callback
12676      */
12677     reload : function(callback){
12678         this.collapse(false, false);
12679         while(this.firstChild){
12680             this.removeChild(this.firstChild);
12681         }
12682         this.childrenRendered = false;
12683         this.loaded = false;
12684         if(this.isHiddenRoot()){
12685             this.expanded = false;
12686         }
12687         this.expand(false, false, callback);
12688     }
12689 });/*
12690  * Based on:
12691  * Ext JS Library 1.1.1
12692  * Copyright(c) 2006-2007, Ext JS, LLC.
12693  *
12694  * Originally Released Under LGPL - original licence link has changed is not relivant.
12695  *
12696  * Fork - LGPL
12697  * <script type="text/javascript">
12698  */
12699  
12700 /**
12701  * @class Roo.tree.TreeNodeUI
12702  * @constructor
12703  * @param {Object} node The node to render
12704  * The TreeNode UI implementation is separate from the
12705  * tree implementation. Unless you are customizing the tree UI,
12706  * you should never have to use this directly.
12707  */
12708 Roo.tree.TreeNodeUI = function(node){
12709     this.node = node;
12710     this.rendered = false;
12711     this.animating = false;
12712     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12713 };
12714
12715 Roo.tree.TreeNodeUI.prototype = {
12716     removeChild : function(node){
12717         if(this.rendered){
12718             this.ctNode.removeChild(node.ui.getEl());
12719         }
12720     },
12721
12722     beforeLoad : function(){
12723          this.addClass("x-tree-node-loading");
12724     },
12725
12726     afterLoad : function(){
12727          this.removeClass("x-tree-node-loading");
12728     },
12729
12730     onTextChange : function(node, text, oldText){
12731         if(this.rendered){
12732             this.textNode.innerHTML = text;
12733         }
12734     },
12735
12736     onDisableChange : function(node, state){
12737         this.disabled = state;
12738         if(state){
12739             this.addClass("x-tree-node-disabled");
12740         }else{
12741             this.removeClass("x-tree-node-disabled");
12742         }
12743     },
12744
12745     onSelectedChange : function(state){
12746         if(state){
12747             this.focus();
12748             this.addClass("x-tree-selected");
12749         }else{
12750             //this.blur();
12751             this.removeClass("x-tree-selected");
12752         }
12753     },
12754
12755     onMove : function(tree, node, oldParent, newParent, index, refNode){
12756         this.childIndent = null;
12757         if(this.rendered){
12758             var targetNode = newParent.ui.getContainer();
12759             if(!targetNode){//target not rendered
12760                 this.holder = document.createElement("div");
12761                 this.holder.appendChild(this.wrap);
12762                 return;
12763             }
12764             var insertBefore = refNode ? refNode.ui.getEl() : null;
12765             if(insertBefore){
12766                 targetNode.insertBefore(this.wrap, insertBefore);
12767             }else{
12768                 targetNode.appendChild(this.wrap);
12769             }
12770             this.node.renderIndent(true);
12771         }
12772     },
12773
12774     addClass : function(cls){
12775         if(this.elNode){
12776             Roo.fly(this.elNode).addClass(cls);
12777         }
12778     },
12779
12780     removeClass : function(cls){
12781         if(this.elNode){
12782             Roo.fly(this.elNode).removeClass(cls);
12783         }
12784     },
12785
12786     remove : function(){
12787         if(this.rendered){
12788             this.holder = document.createElement("div");
12789             this.holder.appendChild(this.wrap);
12790         }
12791     },
12792
12793     fireEvent : function(){
12794         return this.node.fireEvent.apply(this.node, arguments);
12795     },
12796
12797     initEvents : function(){
12798         this.node.on("move", this.onMove, this);
12799         var E = Roo.EventManager;
12800         var a = this.anchor;
12801
12802         var el = Roo.fly(a, '_treeui');
12803
12804         if(Roo.isOpera){ // opera render bug ignores the CSS
12805             el.setStyle("text-decoration", "none");
12806         }
12807
12808         el.on("click", this.onClick, this);
12809         el.on("dblclick", this.onDblClick, this);
12810
12811         if(this.checkbox){
12812             Roo.EventManager.on(this.checkbox,
12813                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12814         }
12815
12816         el.on("contextmenu", this.onContextMenu, this);
12817
12818         var icon = Roo.fly(this.iconNode);
12819         icon.on("click", this.onClick, this);
12820         icon.on("dblclick", this.onDblClick, this);
12821         icon.on("contextmenu", this.onContextMenu, this);
12822         E.on(this.ecNode, "click", this.ecClick, this, true);
12823
12824         if(this.node.disabled){
12825             this.addClass("x-tree-node-disabled");
12826         }
12827         if(this.node.hidden){
12828             this.addClass("x-tree-node-disabled");
12829         }
12830         var ot = this.node.getOwnerTree();
12831         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12832         if(dd && (!this.node.isRoot || ot.rootVisible)){
12833             Roo.dd.Registry.register(this.elNode, {
12834                 node: this.node,
12835                 handles: this.getDDHandles(),
12836                 isHandle: false
12837             });
12838         }
12839     },
12840
12841     getDDHandles : function(){
12842         return [this.iconNode, this.textNode];
12843     },
12844
12845     hide : function(){
12846         if(this.rendered){
12847             this.wrap.style.display = "none";
12848         }
12849     },
12850
12851     show : function(){
12852         if(this.rendered){
12853             this.wrap.style.display = "";
12854         }
12855     },
12856
12857     onContextMenu : function(e){
12858         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12859             e.preventDefault();
12860             this.focus();
12861             this.fireEvent("contextmenu", this.node, e);
12862         }
12863     },
12864
12865     onClick : function(e){
12866         if(this.dropping){
12867             e.stopEvent();
12868             return;
12869         }
12870         if(this.fireEvent("beforeclick", this.node, e) !== false){
12871             if(!this.disabled && this.node.attributes.href){
12872                 this.fireEvent("click", this.node, e);
12873                 return;
12874             }
12875             e.preventDefault();
12876             if(this.disabled){
12877                 return;
12878             }
12879
12880             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12881                 this.node.toggle();
12882             }
12883
12884             this.fireEvent("click", this.node, e);
12885         }else{
12886             e.stopEvent();
12887         }
12888     },
12889
12890     onDblClick : function(e){
12891         e.preventDefault();
12892         if(this.disabled){
12893             return;
12894         }
12895         if(this.checkbox){
12896             this.toggleCheck();
12897         }
12898         if(!this.animating && this.node.hasChildNodes()){
12899             this.node.toggle();
12900         }
12901         this.fireEvent("dblclick", this.node, e);
12902     },
12903
12904     onCheckChange : function(){
12905         var checked = this.checkbox.checked;
12906         this.node.attributes.checked = checked;
12907         this.fireEvent('checkchange', this.node, checked);
12908     },
12909
12910     ecClick : function(e){
12911         if(!this.animating && this.node.hasChildNodes()){
12912             this.node.toggle();
12913         }
12914     },
12915
12916     startDrop : function(){
12917         this.dropping = true;
12918     },
12919
12920     // delayed drop so the click event doesn't get fired on a drop
12921     endDrop : function(){
12922        setTimeout(function(){
12923            this.dropping = false;
12924        }.createDelegate(this), 50);
12925     },
12926
12927     expand : function(){
12928         this.updateExpandIcon();
12929         this.ctNode.style.display = "";
12930     },
12931
12932     focus : function(){
12933         if(!this.node.preventHScroll){
12934             try{this.anchor.focus();
12935             }catch(e){}
12936         }else if(!Roo.isIE){
12937             try{
12938                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12939                 var l = noscroll.scrollLeft;
12940                 this.anchor.focus();
12941                 noscroll.scrollLeft = l;
12942             }catch(e){}
12943         }
12944     },
12945
12946     toggleCheck : function(value){
12947         var cb = this.checkbox;
12948         if(cb){
12949             cb.checked = (value === undefined ? !cb.checked : value);
12950         }
12951     },
12952
12953     blur : function(){
12954         try{
12955             this.anchor.blur();
12956         }catch(e){}
12957     },
12958
12959     animExpand : function(callback){
12960         var ct = Roo.get(this.ctNode);
12961         ct.stopFx();
12962         if(!this.node.hasChildNodes()){
12963             this.updateExpandIcon();
12964             this.ctNode.style.display = "";
12965             Roo.callback(callback);
12966             return;
12967         }
12968         this.animating = true;
12969         this.updateExpandIcon();
12970
12971         ct.slideIn('t', {
12972            callback : function(){
12973                this.animating = false;
12974                Roo.callback(callback);
12975             },
12976             scope: this,
12977             duration: this.node.ownerTree.duration || .25
12978         });
12979     },
12980
12981     highlight : function(){
12982         var tree = this.node.getOwnerTree();
12983         Roo.fly(this.wrap).highlight(
12984             tree.hlColor || "C3DAF9",
12985             {endColor: tree.hlBaseColor}
12986         );
12987     },
12988
12989     collapse : function(){
12990         this.updateExpandIcon();
12991         this.ctNode.style.display = "none";
12992     },
12993
12994     animCollapse : function(callback){
12995         var ct = Roo.get(this.ctNode);
12996         ct.enableDisplayMode('block');
12997         ct.stopFx();
12998
12999         this.animating = true;
13000         this.updateExpandIcon();
13001
13002         ct.slideOut('t', {
13003             callback : function(){
13004                this.animating = false;
13005                Roo.callback(callback);
13006             },
13007             scope: this,
13008             duration: this.node.ownerTree.duration || .25
13009         });
13010     },
13011
13012     getContainer : function(){
13013         return this.ctNode;
13014     },
13015
13016     getEl : function(){
13017         return this.wrap;
13018     },
13019
13020     appendDDGhost : function(ghostNode){
13021         ghostNode.appendChild(this.elNode.cloneNode(true));
13022     },
13023
13024     getDDRepairXY : function(){
13025         return Roo.lib.Dom.getXY(this.iconNode);
13026     },
13027
13028     onRender : function(){
13029         this.render();
13030     },
13031
13032     render : function(bulkRender){
13033         var n = this.node, a = n.attributes;
13034         var targetNode = n.parentNode ?
13035               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13036
13037         if(!this.rendered){
13038             this.rendered = true;
13039
13040             this.renderElements(n, a, targetNode, bulkRender);
13041
13042             if(a.qtip){
13043                if(this.textNode.setAttributeNS){
13044                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13045                    if(a.qtipTitle){
13046                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13047                    }
13048                }else{
13049                    this.textNode.setAttribute("ext:qtip", a.qtip);
13050                    if(a.qtipTitle){
13051                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13052                    }
13053                }
13054             }else if(a.qtipCfg){
13055                 a.qtipCfg.target = Roo.id(this.textNode);
13056                 Roo.QuickTips.register(a.qtipCfg);
13057             }
13058             this.initEvents();
13059             if(!this.node.expanded){
13060                 this.updateExpandIcon();
13061             }
13062         }else{
13063             if(bulkRender === true) {
13064                 targetNode.appendChild(this.wrap);
13065             }
13066         }
13067     },
13068
13069     renderElements : function(n, a, targetNode, bulkRender)
13070     {
13071         // add some indent caching, this helps performance when rendering a large tree
13072         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13073         var t = n.getOwnerTree();
13074         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13075         if (typeof(n.attributes.html) != 'undefined') {
13076             txt = n.attributes.html;
13077         }
13078         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13079         var cb = typeof a.checked == 'boolean';
13080         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13081         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13082             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13083             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13084             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13085             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13086             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13087              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13088                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13089             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13090             "</li>"];
13091
13092         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13093             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13094                                 n.nextSibling.ui.getEl(), buf.join(""));
13095         }else{
13096             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13097         }
13098
13099         this.elNode = this.wrap.childNodes[0];
13100         this.ctNode = this.wrap.childNodes[1];
13101         var cs = this.elNode.childNodes;
13102         this.indentNode = cs[0];
13103         this.ecNode = cs[1];
13104         this.iconNode = cs[2];
13105         var index = 3;
13106         if(cb){
13107             this.checkbox = cs[3];
13108             index++;
13109         }
13110         this.anchor = cs[index];
13111         this.textNode = cs[index].firstChild;
13112     },
13113
13114     getAnchor : function(){
13115         return this.anchor;
13116     },
13117
13118     getTextEl : function(){
13119         return this.textNode;
13120     },
13121
13122     getIconEl : function(){
13123         return this.iconNode;
13124     },
13125
13126     isChecked : function(){
13127         return this.checkbox ? this.checkbox.checked : false;
13128     },
13129
13130     updateExpandIcon : function(){
13131         if(this.rendered){
13132             var n = this.node, c1, c2;
13133             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13134             var hasChild = n.hasChildNodes();
13135             if(hasChild){
13136                 if(n.expanded){
13137                     cls += "-minus";
13138                     c1 = "x-tree-node-collapsed";
13139                     c2 = "x-tree-node-expanded";
13140                 }else{
13141                     cls += "-plus";
13142                     c1 = "x-tree-node-expanded";
13143                     c2 = "x-tree-node-collapsed";
13144                 }
13145                 if(this.wasLeaf){
13146                     this.removeClass("x-tree-node-leaf");
13147                     this.wasLeaf = false;
13148                 }
13149                 if(this.c1 != c1 || this.c2 != c2){
13150                     Roo.fly(this.elNode).replaceClass(c1, c2);
13151                     this.c1 = c1; this.c2 = c2;
13152                 }
13153             }else{
13154                 // this changes non-leafs into leafs if they have no children.
13155                 // it's not very rational behaviour..
13156                 
13157                 if(!this.wasLeaf && this.node.leaf){
13158                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13159                     delete this.c1;
13160                     delete this.c2;
13161                     this.wasLeaf = true;
13162                 }
13163             }
13164             var ecc = "x-tree-ec-icon "+cls;
13165             if(this.ecc != ecc){
13166                 this.ecNode.className = ecc;
13167                 this.ecc = ecc;
13168             }
13169         }
13170     },
13171
13172     getChildIndent : function(){
13173         if(!this.childIndent){
13174             var buf = [];
13175             var p = this.node;
13176             while(p){
13177                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13178                     if(!p.isLast()) {
13179                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13180                     } else {
13181                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13182                     }
13183                 }
13184                 p = p.parentNode;
13185             }
13186             this.childIndent = buf.join("");
13187         }
13188         return this.childIndent;
13189     },
13190
13191     renderIndent : function(){
13192         if(this.rendered){
13193             var indent = "";
13194             var p = this.node.parentNode;
13195             if(p){
13196                 indent = p.ui.getChildIndent();
13197             }
13198             if(this.indentMarkup != indent){ // don't rerender if not required
13199                 this.indentNode.innerHTML = indent;
13200                 this.indentMarkup = indent;
13201             }
13202             this.updateExpandIcon();
13203         }
13204     }
13205 };
13206
13207 Roo.tree.RootTreeNodeUI = function(){
13208     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13209 };
13210 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13211     render : function(){
13212         if(!this.rendered){
13213             var targetNode = this.node.ownerTree.innerCt.dom;
13214             this.node.expanded = true;
13215             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13216             this.wrap = this.ctNode = targetNode.firstChild;
13217         }
13218     },
13219     collapse : function(){
13220     },
13221     expand : function(){
13222     }
13223 });/*
13224  * Based on:
13225  * Ext JS Library 1.1.1
13226  * Copyright(c) 2006-2007, Ext JS, LLC.
13227  *
13228  * Originally Released Under LGPL - original licence link has changed is not relivant.
13229  *
13230  * Fork - LGPL
13231  * <script type="text/javascript">
13232  */
13233 /**
13234  * @class Roo.tree.TreeLoader
13235  * @extends Roo.util.Observable
13236  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13237  * nodes from a specified URL. The response must be a javascript Array definition
13238  * who's elements are node definition objects. eg:
13239  * <pre><code>
13240 {  success : true,
13241    data :      [
13242    
13243     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13244     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13245     ]
13246 }
13247
13248
13249 </code></pre>
13250  * <br><br>
13251  * The old style respose with just an array is still supported, but not recommended.
13252  * <br><br>
13253  *
13254  * A server request is sent, and child nodes are loaded only when a node is expanded.
13255  * The loading node's id is passed to the server under the parameter name "node" to
13256  * enable the server to produce the correct child nodes.
13257  * <br><br>
13258  * To pass extra parameters, an event handler may be attached to the "beforeload"
13259  * event, and the parameters specified in the TreeLoader's baseParams property:
13260  * <pre><code>
13261     myTreeLoader.on("beforeload", function(treeLoader, node) {
13262         this.baseParams.category = node.attributes.category;
13263     }, this);
13264     
13265 </code></pre>
13266  *
13267  * This would pass an HTTP parameter called "category" to the server containing
13268  * the value of the Node's "category" attribute.
13269  * @constructor
13270  * Creates a new Treeloader.
13271  * @param {Object} config A config object containing config properties.
13272  */
13273 Roo.tree.TreeLoader = function(config){
13274     this.baseParams = {};
13275     this.requestMethod = "POST";
13276     Roo.apply(this, config);
13277
13278     this.addEvents({
13279     
13280         /**
13281          * @event beforeload
13282          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13283          * @param {Object} This TreeLoader object.
13284          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13285          * @param {Object} callback The callback function specified in the {@link #load} call.
13286          */
13287         beforeload : true,
13288         /**
13289          * @event load
13290          * Fires when the node has been successfuly loaded.
13291          * @param {Object} This TreeLoader object.
13292          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13293          * @param {Object} response The response object containing the data from the server.
13294          */
13295         load : true,
13296         /**
13297          * @event loadexception
13298          * Fires if the network request failed.
13299          * @param {Object} This TreeLoader object.
13300          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13301          * @param {Object} response The response object containing the data from the server.
13302          */
13303         loadexception : true,
13304         /**
13305          * @event create
13306          * Fires before a node is created, enabling you to return custom Node types 
13307          * @param {Object} This TreeLoader object.
13308          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13309          */
13310         create : true
13311     });
13312
13313     Roo.tree.TreeLoader.superclass.constructor.call(this);
13314 };
13315
13316 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13317     /**
13318     * @cfg {String} dataUrl The URL from which to request a Json string which
13319     * specifies an array of node definition object representing the child nodes
13320     * to be loaded.
13321     */
13322     /**
13323     * @cfg {String} requestMethod either GET or POST
13324     * defaults to POST (due to BC)
13325     * to be loaded.
13326     */
13327     /**
13328     * @cfg {Object} baseParams (optional) An object containing properties which
13329     * specify HTTP parameters to be passed to each request for child nodes.
13330     */
13331     /**
13332     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13333     * created by this loader. If the attributes sent by the server have an attribute in this object,
13334     * they take priority.
13335     */
13336     /**
13337     * @cfg {Object} uiProviders (optional) An object containing properties which
13338     * 
13339     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13340     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13341     * <i>uiProvider</i> attribute of a returned child node is a string rather
13342     * than a reference to a TreeNodeUI implementation, this that string value
13343     * is used as a property name in the uiProviders object. You can define the provider named
13344     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13345     */
13346     uiProviders : {},
13347
13348     /**
13349     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13350     * child nodes before loading.
13351     */
13352     clearOnLoad : true,
13353
13354     /**
13355     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13356     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13357     * Grid query { data : [ .....] }
13358     */
13359     
13360     root : false,
13361      /**
13362     * @cfg {String} queryParam (optional) 
13363     * Name of the query as it will be passed on the querystring (defaults to 'node')
13364     * eg. the request will be ?node=[id]
13365     */
13366     
13367     
13368     queryParam: false,
13369     
13370     /**
13371      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13372      * This is called automatically when a node is expanded, but may be used to reload
13373      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13374      * @param {Roo.tree.TreeNode} node
13375      * @param {Function} callback
13376      */
13377     load : function(node, callback){
13378         if(this.clearOnLoad){
13379             while(node.firstChild){
13380                 node.removeChild(node.firstChild);
13381             }
13382         }
13383         if(node.attributes.children){ // preloaded json children
13384             var cs = node.attributes.children;
13385             for(var i = 0, len = cs.length; i < len; i++){
13386                 node.appendChild(this.createNode(cs[i]));
13387             }
13388             if(typeof callback == "function"){
13389                 callback();
13390             }
13391         }else if(this.dataUrl){
13392             this.requestData(node, callback);
13393         }
13394     },
13395
13396     getParams: function(node){
13397         var buf = [], bp = this.baseParams;
13398         for(var key in bp){
13399             if(typeof bp[key] != "function"){
13400                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13401             }
13402         }
13403         var n = this.queryParam === false ? 'node' : this.queryParam;
13404         buf.push(n + "=", encodeURIComponent(node.id));
13405         return buf.join("");
13406     },
13407
13408     requestData : function(node, callback){
13409         if(this.fireEvent("beforeload", this, node, callback) !== false){
13410             this.transId = Roo.Ajax.request({
13411                 method:this.requestMethod,
13412                 url: this.dataUrl||this.url,
13413                 success: this.handleResponse,
13414                 failure: this.handleFailure,
13415                 scope: this,
13416                 argument: {callback: callback, node: node},
13417                 params: this.getParams(node)
13418             });
13419         }else{
13420             // if the load is cancelled, make sure we notify
13421             // the node that we are done
13422             if(typeof callback == "function"){
13423                 callback();
13424             }
13425         }
13426     },
13427
13428     isLoading : function(){
13429         return this.transId ? true : false;
13430     },
13431
13432     abort : function(){
13433         if(this.isLoading()){
13434             Roo.Ajax.abort(this.transId);
13435         }
13436     },
13437
13438     // private
13439     createNode : function(attr)
13440     {
13441         // apply baseAttrs, nice idea Corey!
13442         if(this.baseAttrs){
13443             Roo.applyIf(attr, this.baseAttrs);
13444         }
13445         if(this.applyLoader !== false){
13446             attr.loader = this;
13447         }
13448         // uiProvider = depreciated..
13449         
13450         if(typeof(attr.uiProvider) == 'string'){
13451            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13452                 /**  eval:var:attr */ eval(attr.uiProvider);
13453         }
13454         if(typeof(this.uiProviders['default']) != 'undefined') {
13455             attr.uiProvider = this.uiProviders['default'];
13456         }
13457         
13458         this.fireEvent('create', this, attr);
13459         
13460         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13461         return(attr.leaf ?
13462                         new Roo.tree.TreeNode(attr) :
13463                         new Roo.tree.AsyncTreeNode(attr));
13464     },
13465
13466     processResponse : function(response, node, callback)
13467     {
13468         var json = response.responseText;
13469         try {
13470             
13471             var o = Roo.decode(json);
13472             
13473             if (this.root === false && typeof(o.success) != undefined) {
13474                 this.root = 'data'; // the default behaviour for list like data..
13475                 }
13476                 
13477             if (this.root !== false &&  !o.success) {
13478                 // it's a failure condition.
13479                 var a = response.argument;
13480                 this.fireEvent("loadexception", this, a.node, response);
13481                 Roo.log("Load failed - should have a handler really");
13482                 return;
13483             }
13484             
13485             
13486             
13487             if (this.root !== false) {
13488                  o = o[this.root];
13489             }
13490             
13491             for(var i = 0, len = o.length; i < len; i++){
13492                 var n = this.createNode(o[i]);
13493                 if(n){
13494                     node.appendChild(n);
13495                 }
13496             }
13497             if(typeof callback == "function"){
13498                 callback(this, node);
13499             }
13500         }catch(e){
13501             this.handleFailure(response);
13502         }
13503     },
13504
13505     handleResponse : function(response){
13506         this.transId = false;
13507         var a = response.argument;
13508         this.processResponse(response, a.node, a.callback);
13509         this.fireEvent("load", this, a.node, response);
13510     },
13511
13512     handleFailure : function(response)
13513     {
13514         // should handle failure better..
13515         this.transId = false;
13516         var a = response.argument;
13517         this.fireEvent("loadexception", this, a.node, response);
13518         if(typeof a.callback == "function"){
13519             a.callback(this, a.node);
13520         }
13521     }
13522 });/*
13523  * Based on:
13524  * Ext JS Library 1.1.1
13525  * Copyright(c) 2006-2007, Ext JS, LLC.
13526  *
13527  * Originally Released Under LGPL - original licence link has changed is not relivant.
13528  *
13529  * Fork - LGPL
13530  * <script type="text/javascript">
13531  */
13532
13533 /**
13534 * @class Roo.tree.TreeFilter
13535 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13536 * @param {TreePanel} tree
13537 * @param {Object} config (optional)
13538  */
13539 Roo.tree.TreeFilter = function(tree, config){
13540     this.tree = tree;
13541     this.filtered = {};
13542     Roo.apply(this, config);
13543 };
13544
13545 Roo.tree.TreeFilter.prototype = {
13546     clearBlank:false,
13547     reverse:false,
13548     autoClear:false,
13549     remove:false,
13550
13551      /**
13552      * Filter the data by a specific attribute.
13553      * @param {String/RegExp} value Either string that the attribute value
13554      * should start with or a RegExp to test against the attribute
13555      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13556      * @param {TreeNode} startNode (optional) The node to start the filter at.
13557      */
13558     filter : function(value, attr, startNode){
13559         attr = attr || "text";
13560         var f;
13561         if(typeof value == "string"){
13562             var vlen = value.length;
13563             // auto clear empty filter
13564             if(vlen == 0 && this.clearBlank){
13565                 this.clear();
13566                 return;
13567             }
13568             value = value.toLowerCase();
13569             f = function(n){
13570                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13571             };
13572         }else if(value.exec){ // regex?
13573             f = function(n){
13574                 return value.test(n.attributes[attr]);
13575             };
13576         }else{
13577             throw 'Illegal filter type, must be string or regex';
13578         }
13579         this.filterBy(f, null, startNode);
13580         },
13581
13582     /**
13583      * Filter by a function. The passed function will be called with each
13584      * node in the tree (or from the startNode). If the function returns true, the node is kept
13585      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13586      * @param {Function} fn The filter function
13587      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13588      */
13589     filterBy : function(fn, scope, startNode){
13590         startNode = startNode || this.tree.root;
13591         if(this.autoClear){
13592             this.clear();
13593         }
13594         var af = this.filtered, rv = this.reverse;
13595         var f = function(n){
13596             if(n == startNode){
13597                 return true;
13598             }
13599             if(af[n.id]){
13600                 return false;
13601             }
13602             var m = fn.call(scope || n, n);
13603             if(!m || rv){
13604                 af[n.id] = n;
13605                 n.ui.hide();
13606                 return false;
13607             }
13608             return true;
13609         };
13610         startNode.cascade(f);
13611         if(this.remove){
13612            for(var id in af){
13613                if(typeof id != "function"){
13614                    var n = af[id];
13615                    if(n && n.parentNode){
13616                        n.parentNode.removeChild(n);
13617                    }
13618                }
13619            }
13620         }
13621     },
13622
13623     /**
13624      * Clears the current filter. Note: with the "remove" option
13625      * set a filter cannot be cleared.
13626      */
13627     clear : function(){
13628         var t = this.tree;
13629         var af = this.filtered;
13630         for(var id in af){
13631             if(typeof id != "function"){
13632                 var n = af[id];
13633                 if(n){
13634                     n.ui.show();
13635                 }
13636             }
13637         }
13638         this.filtered = {};
13639     }
13640 };
13641 /*
13642  * Based on:
13643  * Ext JS Library 1.1.1
13644  * Copyright(c) 2006-2007, Ext JS, LLC.
13645  *
13646  * Originally Released Under LGPL - original licence link has changed is not relivant.
13647  *
13648  * Fork - LGPL
13649  * <script type="text/javascript">
13650  */
13651  
13652
13653 /**
13654  * @class Roo.tree.TreeSorter
13655  * Provides sorting of nodes in a TreePanel
13656  * 
13657  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13658  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13659  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13660  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13661  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13662  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13663  * @constructor
13664  * @param {TreePanel} tree
13665  * @param {Object} config
13666  */
13667 Roo.tree.TreeSorter = function(tree, config){
13668     Roo.apply(this, config);
13669     tree.on("beforechildrenrendered", this.doSort, this);
13670     tree.on("append", this.updateSort, this);
13671     tree.on("insert", this.updateSort, this);
13672     
13673     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13674     var p = this.property || "text";
13675     var sortType = this.sortType;
13676     var fs = this.folderSort;
13677     var cs = this.caseSensitive === true;
13678     var leafAttr = this.leafAttr || 'leaf';
13679
13680     this.sortFn = function(n1, n2){
13681         if(fs){
13682             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13683                 return 1;
13684             }
13685             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13686                 return -1;
13687             }
13688         }
13689         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13690         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13691         if(v1 < v2){
13692                         return dsc ? +1 : -1;
13693                 }else if(v1 > v2){
13694                         return dsc ? -1 : +1;
13695         }else{
13696                 return 0;
13697         }
13698     };
13699 };
13700
13701 Roo.tree.TreeSorter.prototype = {
13702     doSort : function(node){
13703         node.sort(this.sortFn);
13704     },
13705     
13706     compareNodes : function(n1, n2){
13707         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13708     },
13709     
13710     updateSort : function(tree, node){
13711         if(node.childrenRendered){
13712             this.doSort.defer(1, this, [node]);
13713         }
13714     }
13715 };/*
13716  * Based on:
13717  * Ext JS Library 1.1.1
13718  * Copyright(c) 2006-2007, Ext JS, LLC.
13719  *
13720  * Originally Released Under LGPL - original licence link has changed is not relivant.
13721  *
13722  * Fork - LGPL
13723  * <script type="text/javascript">
13724  */
13725
13726 if(Roo.dd.DropZone){
13727     
13728 Roo.tree.TreeDropZone = function(tree, config){
13729     this.allowParentInsert = false;
13730     this.allowContainerDrop = false;
13731     this.appendOnly = false;
13732     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13733     this.tree = tree;
13734     this.lastInsertClass = "x-tree-no-status";
13735     this.dragOverData = {};
13736 };
13737
13738 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13739     ddGroup : "TreeDD",
13740     scroll:  true,
13741     
13742     expandDelay : 1000,
13743     
13744     expandNode : function(node){
13745         if(node.hasChildNodes() && !node.isExpanded()){
13746             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13747         }
13748     },
13749     
13750     queueExpand : function(node){
13751         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13752     },
13753     
13754     cancelExpand : function(){
13755         if(this.expandProcId){
13756             clearTimeout(this.expandProcId);
13757             this.expandProcId = false;
13758         }
13759     },
13760     
13761     isValidDropPoint : function(n, pt, dd, e, data){
13762         if(!n || !data){ return false; }
13763         var targetNode = n.node;
13764         var dropNode = data.node;
13765         // default drop rules
13766         if(!(targetNode && targetNode.isTarget && pt)){
13767             return false;
13768         }
13769         if(pt == "append" && targetNode.allowChildren === false){
13770             return false;
13771         }
13772         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13773             return false;
13774         }
13775         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13776             return false;
13777         }
13778         // reuse the object
13779         var overEvent = this.dragOverData;
13780         overEvent.tree = this.tree;
13781         overEvent.target = targetNode;
13782         overEvent.data = data;
13783         overEvent.point = pt;
13784         overEvent.source = dd;
13785         overEvent.rawEvent = e;
13786         overEvent.dropNode = dropNode;
13787         overEvent.cancel = false;  
13788         var result = this.tree.fireEvent("nodedragover", overEvent);
13789         return overEvent.cancel === false && result !== false;
13790     },
13791     
13792     getDropPoint : function(e, n, dd)
13793     {
13794         var tn = n.node;
13795         if(tn.isRoot){
13796             return tn.allowChildren !== false ? "append" : false; // always append for root
13797         }
13798         var dragEl = n.ddel;
13799         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13800         var y = Roo.lib.Event.getPageY(e);
13801         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13802         
13803         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13804         var noAppend = tn.allowChildren === false;
13805         if(this.appendOnly || tn.parentNode.allowChildren === false){
13806             return noAppend ? false : "append";
13807         }
13808         var noBelow = false;
13809         if(!this.allowParentInsert){
13810             noBelow = tn.hasChildNodes() && tn.isExpanded();
13811         }
13812         var q = (b - t) / (noAppend ? 2 : 3);
13813         if(y >= t && y < (t + q)){
13814             return "above";
13815         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13816             return "below";
13817         }else{
13818             return "append";
13819         }
13820     },
13821     
13822     onNodeEnter : function(n, dd, e, data)
13823     {
13824         this.cancelExpand();
13825     },
13826     
13827     onNodeOver : function(n, dd, e, data)
13828     {
13829        
13830         var pt = this.getDropPoint(e, n, dd);
13831         var node = n.node;
13832         
13833         // auto node expand check
13834         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13835             this.queueExpand(node);
13836         }else if(pt != "append"){
13837             this.cancelExpand();
13838         }
13839         
13840         // set the insert point style on the target node
13841         var returnCls = this.dropNotAllowed;
13842         if(this.isValidDropPoint(n, pt, dd, e, data)){
13843            if(pt){
13844                var el = n.ddel;
13845                var cls;
13846                if(pt == "above"){
13847                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13848                    cls = "x-tree-drag-insert-above";
13849                }else if(pt == "below"){
13850                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13851                    cls = "x-tree-drag-insert-below";
13852                }else{
13853                    returnCls = "x-tree-drop-ok-append";
13854                    cls = "x-tree-drag-append";
13855                }
13856                if(this.lastInsertClass != cls){
13857                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13858                    this.lastInsertClass = cls;
13859                }
13860            }
13861        }
13862        return returnCls;
13863     },
13864     
13865     onNodeOut : function(n, dd, e, data){
13866         
13867         this.cancelExpand();
13868         this.removeDropIndicators(n);
13869     },
13870     
13871     onNodeDrop : function(n, dd, e, data){
13872         var point = this.getDropPoint(e, n, dd);
13873         var targetNode = n.node;
13874         targetNode.ui.startDrop();
13875         if(!this.isValidDropPoint(n, point, dd, e, data)){
13876             targetNode.ui.endDrop();
13877             return false;
13878         }
13879         // first try to find the drop node
13880         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13881         var dropEvent = {
13882             tree : this.tree,
13883             target: targetNode,
13884             data: data,
13885             point: point,
13886             source: dd,
13887             rawEvent: e,
13888             dropNode: dropNode,
13889             cancel: !dropNode   
13890         };
13891         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13892         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13893             targetNode.ui.endDrop();
13894             return false;
13895         }
13896         // allow target changing
13897         targetNode = dropEvent.target;
13898         if(point == "append" && !targetNode.isExpanded()){
13899             targetNode.expand(false, null, function(){
13900                 this.completeDrop(dropEvent);
13901             }.createDelegate(this));
13902         }else{
13903             this.completeDrop(dropEvent);
13904         }
13905         return true;
13906     },
13907     
13908     completeDrop : function(de){
13909         var ns = de.dropNode, p = de.point, t = de.target;
13910         if(!(ns instanceof Array)){
13911             ns = [ns];
13912         }
13913         var n;
13914         for(var i = 0, len = ns.length; i < len; i++){
13915             n = ns[i];
13916             if(p == "above"){
13917                 t.parentNode.insertBefore(n, t);
13918             }else if(p == "below"){
13919                 t.parentNode.insertBefore(n, t.nextSibling);
13920             }else{
13921                 t.appendChild(n);
13922             }
13923         }
13924         n.ui.focus();
13925         if(this.tree.hlDrop){
13926             n.ui.highlight();
13927         }
13928         t.ui.endDrop();
13929         this.tree.fireEvent("nodedrop", de);
13930     },
13931     
13932     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13933         if(this.tree.hlDrop){
13934             dropNode.ui.focus();
13935             dropNode.ui.highlight();
13936         }
13937         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13938     },
13939     
13940     getTree : function(){
13941         return this.tree;
13942     },
13943     
13944     removeDropIndicators : function(n){
13945         if(n && n.ddel){
13946             var el = n.ddel;
13947             Roo.fly(el).removeClass([
13948                     "x-tree-drag-insert-above",
13949                     "x-tree-drag-insert-below",
13950                     "x-tree-drag-append"]);
13951             this.lastInsertClass = "_noclass";
13952         }
13953     },
13954     
13955     beforeDragDrop : function(target, e, id){
13956         this.cancelExpand();
13957         return true;
13958     },
13959     
13960     afterRepair : function(data){
13961         if(data && Roo.enableFx){
13962             data.node.ui.highlight();
13963         }
13964         this.hideProxy();
13965     } 
13966     
13967 });
13968
13969 }
13970 /*
13971  * Based on:
13972  * Ext JS Library 1.1.1
13973  * Copyright(c) 2006-2007, Ext JS, LLC.
13974  *
13975  * Originally Released Under LGPL - original licence link has changed is not relivant.
13976  *
13977  * Fork - LGPL
13978  * <script type="text/javascript">
13979  */
13980  
13981
13982 if(Roo.dd.DragZone){
13983 Roo.tree.TreeDragZone = function(tree, config){
13984     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13985     this.tree = tree;
13986 };
13987
13988 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13989     ddGroup : "TreeDD",
13990    
13991     onBeforeDrag : function(data, e){
13992         var n = data.node;
13993         return n && n.draggable && !n.disabled;
13994     },
13995      
13996     
13997     onInitDrag : function(e){
13998         var data = this.dragData;
13999         this.tree.getSelectionModel().select(data.node);
14000         this.proxy.update("");
14001         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14002         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14003     },
14004     
14005     getRepairXY : function(e, data){
14006         return data.node.ui.getDDRepairXY();
14007     },
14008     
14009     onEndDrag : function(data, e){
14010         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14011         
14012         
14013     },
14014     
14015     onValidDrop : function(dd, e, id){
14016         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14017         this.hideProxy();
14018     },
14019     
14020     beforeInvalidDrop : function(e, id){
14021         // this scrolls the original position back into view
14022         var sm = this.tree.getSelectionModel();
14023         sm.clearSelections();
14024         sm.select(this.dragData.node);
14025     }
14026 });
14027 }/*
14028  * Based on:
14029  * Ext JS Library 1.1.1
14030  * Copyright(c) 2006-2007, Ext JS, LLC.
14031  *
14032  * Originally Released Under LGPL - original licence link has changed is not relivant.
14033  *
14034  * Fork - LGPL
14035  * <script type="text/javascript">
14036  */
14037 /**
14038  * @class Roo.tree.TreeEditor
14039  * @extends Roo.Editor
14040  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14041  * as the editor field.
14042  * @constructor
14043  * @param {Object} config (used to be the tree panel.)
14044  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14045  * 
14046  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14047  * @cfg {Roo.form.TextField} field [required] The field configuration
14048  *
14049  * 
14050  */
14051 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14052     var tree = config;
14053     var field;
14054     if (oldconfig) { // old style..
14055         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14056     } else {
14057         // new style..
14058         tree = config.tree;
14059         config.field = config.field  || {};
14060         config.field.xtype = 'TextField';
14061         field = Roo.factory(config.field, Roo.form);
14062     }
14063     config = config || {};
14064     
14065     
14066     this.addEvents({
14067         /**
14068          * @event beforenodeedit
14069          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14070          * false from the handler of this event.
14071          * @param {Editor} this
14072          * @param {Roo.tree.Node} node 
14073          */
14074         "beforenodeedit" : true
14075     });
14076     
14077     //Roo.log(config);
14078     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14079
14080     this.tree = tree;
14081
14082     tree.on('beforeclick', this.beforeNodeClick, this);
14083     tree.getTreeEl().on('mousedown', this.hide, this);
14084     this.on('complete', this.updateNode, this);
14085     this.on('beforestartedit', this.fitToTree, this);
14086     this.on('startedit', this.bindScroll, this, {delay:10});
14087     this.on('specialkey', this.onSpecialKey, this);
14088 };
14089
14090 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14091     /**
14092      * @cfg {String} alignment
14093      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14094      */
14095     alignment: "l-l",
14096     // inherit
14097     autoSize: false,
14098     /**
14099      * @cfg {Boolean} hideEl
14100      * True to hide the bound element while the editor is displayed (defaults to false)
14101      */
14102     hideEl : false,
14103     /**
14104      * @cfg {String} cls
14105      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14106      */
14107     cls: "x-small-editor x-tree-editor",
14108     /**
14109      * @cfg {Boolean} shim
14110      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14111      */
14112     shim:false,
14113     // inherit
14114     shadow:"frame",
14115     /**
14116      * @cfg {Number} maxWidth
14117      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14118      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14119      * scroll and client offsets into account prior to each edit.
14120      */
14121     maxWidth: 250,
14122
14123     editDelay : 350,
14124
14125     // private
14126     fitToTree : function(ed, el){
14127         var td = this.tree.getTreeEl().dom, nd = el.dom;
14128         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14129             td.scrollLeft = nd.offsetLeft;
14130         }
14131         var w = Math.min(
14132                 this.maxWidth,
14133                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14134         this.setSize(w, '');
14135         
14136         return this.fireEvent('beforenodeedit', this, this.editNode);
14137         
14138     },
14139
14140     // private
14141     triggerEdit : function(node){
14142         this.completeEdit();
14143         this.editNode = node;
14144         this.startEdit(node.ui.textNode, node.text);
14145     },
14146
14147     // private
14148     bindScroll : function(){
14149         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14150     },
14151
14152     // private
14153     beforeNodeClick : function(node, e){
14154         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14155         this.lastClick = new Date();
14156         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14157             e.stopEvent();
14158             this.triggerEdit(node);
14159             return false;
14160         }
14161         return true;
14162     },
14163
14164     // private
14165     updateNode : function(ed, value){
14166         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14167         this.editNode.setText(value);
14168     },
14169
14170     // private
14171     onHide : function(){
14172         Roo.tree.TreeEditor.superclass.onHide.call(this);
14173         if(this.editNode){
14174             this.editNode.ui.focus();
14175         }
14176     },
14177
14178     // private
14179     onSpecialKey : function(field, e){
14180         var k = e.getKey();
14181         if(k == e.ESC){
14182             e.stopEvent();
14183             this.cancelEdit();
14184         }else if(k == e.ENTER && !e.hasModifier()){
14185             e.stopEvent();
14186             this.completeEdit();
14187         }
14188     }
14189 });//<Script type="text/javascript">
14190 /*
14191  * Based on:
14192  * Ext JS Library 1.1.1
14193  * Copyright(c) 2006-2007, Ext JS, LLC.
14194  *
14195  * Originally Released Under LGPL - original licence link has changed is not relivant.
14196  *
14197  * Fork - LGPL
14198  * <script type="text/javascript">
14199  */
14200  
14201 /**
14202  * Not documented??? - probably should be...
14203  */
14204
14205 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14206     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14207     
14208     renderElements : function(n, a, targetNode, bulkRender){
14209         //consel.log("renderElements?");
14210         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14211
14212         var t = n.getOwnerTree();
14213         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14214         
14215         var cols = t.columns;
14216         var bw = t.borderWidth;
14217         var c = cols[0];
14218         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14219          var cb = typeof a.checked == "boolean";
14220         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14221         var colcls = 'x-t-' + tid + '-c0';
14222         var buf = [
14223             '<li class="x-tree-node">',
14224             
14225                 
14226                 '<div class="x-tree-node-el ', a.cls,'">',
14227                     // extran...
14228                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14229                 
14230                 
14231                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14232                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14233                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14234                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14235                            (a.iconCls ? ' '+a.iconCls : ''),
14236                            '" unselectable="on" />',
14237                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14238                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14239                              
14240                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14241                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14242                             '<span unselectable="on" qtip="' + tx + '">',
14243                              tx,
14244                              '</span></a>' ,
14245                     '</div>',
14246                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14247                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14248                  ];
14249         for(var i = 1, len = cols.length; i < len; i++){
14250             c = cols[i];
14251             colcls = 'x-t-' + tid + '-c' +i;
14252             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14253             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14254                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14255                       "</div>");
14256          }
14257          
14258          buf.push(
14259             '</a>',
14260             '<div class="x-clear"></div></div>',
14261             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14262             "</li>");
14263         
14264         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14265             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14266                                 n.nextSibling.ui.getEl(), buf.join(""));
14267         }else{
14268             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14269         }
14270         var el = this.wrap.firstChild;
14271         this.elRow = el;
14272         this.elNode = el.firstChild;
14273         this.ranchor = el.childNodes[1];
14274         this.ctNode = this.wrap.childNodes[1];
14275         var cs = el.firstChild.childNodes;
14276         this.indentNode = cs[0];
14277         this.ecNode = cs[1];
14278         this.iconNode = cs[2];
14279         var index = 3;
14280         if(cb){
14281             this.checkbox = cs[3];
14282             index++;
14283         }
14284         this.anchor = cs[index];
14285         
14286         this.textNode = cs[index].firstChild;
14287         
14288         //el.on("click", this.onClick, this);
14289         //el.on("dblclick", this.onDblClick, this);
14290         
14291         
14292        // console.log(this);
14293     },
14294     initEvents : function(){
14295         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14296         
14297             
14298         var a = this.ranchor;
14299
14300         var el = Roo.get(a);
14301
14302         if(Roo.isOpera){ // opera render bug ignores the CSS
14303             el.setStyle("text-decoration", "none");
14304         }
14305
14306         el.on("click", this.onClick, this);
14307         el.on("dblclick", this.onDblClick, this);
14308         el.on("contextmenu", this.onContextMenu, this);
14309         
14310     },
14311     
14312     /*onSelectedChange : function(state){
14313         if(state){
14314             this.focus();
14315             this.addClass("x-tree-selected");
14316         }else{
14317             //this.blur();
14318             this.removeClass("x-tree-selected");
14319         }
14320     },*/
14321     addClass : function(cls){
14322         if(this.elRow){
14323             Roo.fly(this.elRow).addClass(cls);
14324         }
14325         
14326     },
14327     
14328     
14329     removeClass : function(cls){
14330         if(this.elRow){
14331             Roo.fly(this.elRow).removeClass(cls);
14332         }
14333     }
14334
14335     
14336     
14337 });//<Script type="text/javascript">
14338
14339 /*
14340  * Based on:
14341  * Ext JS Library 1.1.1
14342  * Copyright(c) 2006-2007, Ext JS, LLC.
14343  *
14344  * Originally Released Under LGPL - original licence link has changed is not relivant.
14345  *
14346  * Fork - LGPL
14347  * <script type="text/javascript">
14348  */
14349  
14350
14351 /**
14352  * @class Roo.tree.ColumnTree
14353  * @extends Roo.tree.TreePanel
14354  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14355  * @cfg {int} borderWidth  compined right/left border allowance
14356  * @constructor
14357  * @param {String/HTMLElement/Element} el The container element
14358  * @param {Object} config
14359  */
14360 Roo.tree.ColumnTree =  function(el, config)
14361 {
14362    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14363    this.addEvents({
14364         /**
14365         * @event resize
14366         * Fire this event on a container when it resizes
14367         * @param {int} w Width
14368         * @param {int} h Height
14369         */
14370        "resize" : true
14371     });
14372     this.on('resize', this.onResize, this);
14373 };
14374
14375 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14376     //lines:false,
14377     
14378     
14379     borderWidth: Roo.isBorderBox ? 0 : 2, 
14380     headEls : false,
14381     
14382     render : function(){
14383         // add the header.....
14384        
14385         Roo.tree.ColumnTree.superclass.render.apply(this);
14386         
14387         this.el.addClass('x-column-tree');
14388         
14389         this.headers = this.el.createChild(
14390             {cls:'x-tree-headers'},this.innerCt.dom);
14391    
14392         var cols = this.columns, c;
14393         var totalWidth = 0;
14394         this.headEls = [];
14395         var  len = cols.length;
14396         for(var i = 0; i < len; i++){
14397              c = cols[i];
14398              totalWidth += c.width;
14399             this.headEls.push(this.headers.createChild({
14400                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14401                  cn: {
14402                      cls:'x-tree-hd-text',
14403                      html: c.header
14404                  },
14405                  style:'width:'+(c.width-this.borderWidth)+'px;'
14406              }));
14407         }
14408         this.headers.createChild({cls:'x-clear'});
14409         // prevent floats from wrapping when clipped
14410         this.headers.setWidth(totalWidth);
14411         //this.innerCt.setWidth(totalWidth);
14412         this.innerCt.setStyle({ overflow: 'auto' });
14413         this.onResize(this.width, this.height);
14414              
14415         
14416     },
14417     onResize : function(w,h)
14418     {
14419         this.height = h;
14420         this.width = w;
14421         // resize cols..
14422         this.innerCt.setWidth(this.width);
14423         this.innerCt.setHeight(this.height-20);
14424         
14425         // headers...
14426         var cols = this.columns, c;
14427         var totalWidth = 0;
14428         var expEl = false;
14429         var len = cols.length;
14430         for(var i = 0; i < len; i++){
14431             c = cols[i];
14432             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14433                 // it's the expander..
14434                 expEl  = this.headEls[i];
14435                 continue;
14436             }
14437             totalWidth += c.width;
14438             
14439         }
14440         if (expEl) {
14441             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14442         }
14443         this.headers.setWidth(w-20);
14444
14445         
14446         
14447         
14448     }
14449 });
14450 /*
14451  * Based on:
14452  * Ext JS Library 1.1.1
14453  * Copyright(c) 2006-2007, Ext JS, LLC.
14454  *
14455  * Originally Released Under LGPL - original licence link has changed is not relivant.
14456  *
14457  * Fork - LGPL
14458  * <script type="text/javascript">
14459  */
14460  
14461 /**
14462  * @class Roo.menu.Menu
14463  * @extends Roo.util.Observable
14464  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14465  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14466  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14467  * @constructor
14468  * Creates a new Menu
14469  * @param {Object} config Configuration options
14470  */
14471 Roo.menu.Menu = function(config){
14472     
14473     Roo.menu.Menu.superclass.constructor.call(this, config);
14474     
14475     this.id = this.id || Roo.id();
14476     this.addEvents({
14477         /**
14478          * @event beforeshow
14479          * Fires before this menu is displayed
14480          * @param {Roo.menu.Menu} this
14481          */
14482         beforeshow : true,
14483         /**
14484          * @event beforehide
14485          * Fires before this menu is hidden
14486          * @param {Roo.menu.Menu} this
14487          */
14488         beforehide : true,
14489         /**
14490          * @event show
14491          * Fires after this menu is displayed
14492          * @param {Roo.menu.Menu} this
14493          */
14494         show : true,
14495         /**
14496          * @event hide
14497          * Fires after this menu is hidden
14498          * @param {Roo.menu.Menu} this
14499          */
14500         hide : true,
14501         /**
14502          * @event click
14503          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14504          * @param {Roo.menu.Menu} this
14505          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14506          * @param {Roo.EventObject} e
14507          */
14508         click : true,
14509         /**
14510          * @event mouseover
14511          * Fires when the mouse is hovering over this menu
14512          * @param {Roo.menu.Menu} this
14513          * @param {Roo.EventObject} e
14514          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14515          */
14516         mouseover : true,
14517         /**
14518          * @event mouseout
14519          * Fires when the mouse exits this menu
14520          * @param {Roo.menu.Menu} this
14521          * @param {Roo.EventObject} e
14522          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14523          */
14524         mouseout : true,
14525         /**
14526          * @event itemclick
14527          * Fires when a menu item contained in this menu is clicked
14528          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14529          * @param {Roo.EventObject} e
14530          */
14531         itemclick: true
14532     });
14533     if (this.registerMenu) {
14534         Roo.menu.MenuMgr.register(this);
14535     }
14536     
14537     var mis = this.items;
14538     this.items = new Roo.util.MixedCollection();
14539     if(mis){
14540         this.add.apply(this, mis);
14541     }
14542 };
14543
14544 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14545     /**
14546      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14547      */
14548     minWidth : 120,
14549     /**
14550      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14551      * for bottom-right shadow (defaults to "sides")
14552      */
14553     shadow : "sides",
14554     /**
14555      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14556      * this menu (defaults to "tl-tr?")
14557      */
14558     subMenuAlign : "tl-tr?",
14559     /**
14560      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14561      * relative to its element of origin (defaults to "tl-bl?")
14562      */
14563     defaultAlign : "tl-bl?",
14564     /**
14565      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14566      */
14567     allowOtherMenus : false,
14568     /**
14569      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14570      */
14571     registerMenu : true,
14572
14573     hidden:true,
14574
14575     // private
14576     render : function(){
14577         if(this.el){
14578             return;
14579         }
14580         var el = this.el = new Roo.Layer({
14581             cls: "x-menu",
14582             shadow:this.shadow,
14583             constrain: false,
14584             parentEl: this.parentEl || document.body,
14585             zindex:15000
14586         });
14587
14588         this.keyNav = new Roo.menu.MenuNav(this);
14589
14590         if(this.plain){
14591             el.addClass("x-menu-plain");
14592         }
14593         if(this.cls){
14594             el.addClass(this.cls);
14595         }
14596         // generic focus element
14597         this.focusEl = el.createChild({
14598             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14599         });
14600         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14601         //disabling touch- as it's causing issues ..
14602         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14603         ul.on('click'   , this.onClick, this);
14604         
14605         
14606         ul.on("mouseover", this.onMouseOver, this);
14607         ul.on("mouseout", this.onMouseOut, this);
14608         this.items.each(function(item){
14609             if (item.hidden) {
14610                 return;
14611             }
14612             
14613             var li = document.createElement("li");
14614             li.className = "x-menu-list-item";
14615             ul.dom.appendChild(li);
14616             item.render(li, this);
14617         }, this);
14618         this.ul = ul;
14619         this.autoWidth();
14620     },
14621
14622     // private
14623     autoWidth : function(){
14624         var el = this.el, ul = this.ul;
14625         if(!el){
14626             return;
14627         }
14628         var w = this.width;
14629         if(w){
14630             el.setWidth(w);
14631         }else if(Roo.isIE){
14632             el.setWidth(this.minWidth);
14633             var t = el.dom.offsetWidth; // force recalc
14634             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14635         }
14636     },
14637
14638     // private
14639     delayAutoWidth : function(){
14640         if(this.rendered){
14641             if(!this.awTask){
14642                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14643             }
14644             this.awTask.delay(20);
14645         }
14646     },
14647
14648     // private
14649     findTargetItem : function(e){
14650         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14651         if(t && t.menuItemId){
14652             return this.items.get(t.menuItemId);
14653         }
14654     },
14655
14656     // private
14657     onClick : function(e){
14658         Roo.log("menu.onClick");
14659         var t = this.findTargetItem(e);
14660         if(!t){
14661             return;
14662         }
14663         Roo.log(e);
14664         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14665             if(t == this.activeItem && t.shouldDeactivate(e)){
14666                 this.activeItem.deactivate();
14667                 delete this.activeItem;
14668                 return;
14669             }
14670             if(t.canActivate){
14671                 this.setActiveItem(t, true);
14672             }
14673             return;
14674             
14675             
14676         }
14677         
14678         t.onClick(e);
14679         this.fireEvent("click", this, t, e);
14680     },
14681
14682     // private
14683     setActiveItem : function(item, autoExpand){
14684         if(item != this.activeItem){
14685             if(this.activeItem){
14686                 this.activeItem.deactivate();
14687             }
14688             this.activeItem = item;
14689             item.activate(autoExpand);
14690         }else if(autoExpand){
14691             item.expandMenu();
14692         }
14693     },
14694
14695     // private
14696     tryActivate : function(start, step){
14697         var items = this.items;
14698         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14699             var item = items.get(i);
14700             if(!item.disabled && item.canActivate){
14701                 this.setActiveItem(item, false);
14702                 return item;
14703             }
14704         }
14705         return false;
14706     },
14707
14708     // private
14709     onMouseOver : function(e){
14710         var t;
14711         if(t = this.findTargetItem(e)){
14712             if(t.canActivate && !t.disabled){
14713                 this.setActiveItem(t, true);
14714             }
14715         }
14716         this.fireEvent("mouseover", this, e, t);
14717     },
14718
14719     // private
14720     onMouseOut : function(e){
14721         var t;
14722         if(t = this.findTargetItem(e)){
14723             if(t == this.activeItem && t.shouldDeactivate(e)){
14724                 this.activeItem.deactivate();
14725                 delete this.activeItem;
14726             }
14727         }
14728         this.fireEvent("mouseout", this, e, t);
14729     },
14730
14731     /**
14732      * Read-only.  Returns true if the menu is currently displayed, else false.
14733      * @type Boolean
14734      */
14735     isVisible : function(){
14736         return this.el && !this.hidden;
14737     },
14738
14739     /**
14740      * Displays this menu relative to another element
14741      * @param {String/HTMLElement/Roo.Element} element The element to align to
14742      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14743      * the element (defaults to this.defaultAlign)
14744      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14745      */
14746     show : function(el, pos, parentMenu){
14747         this.parentMenu = parentMenu;
14748         if(!this.el){
14749             this.render();
14750         }
14751         this.fireEvent("beforeshow", this);
14752         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14753     },
14754
14755     /**
14756      * Displays this menu at a specific xy position
14757      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14758      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14759      */
14760     showAt : function(xy, parentMenu, /* private: */_e){
14761         this.parentMenu = parentMenu;
14762         if(!this.el){
14763             this.render();
14764         }
14765         if(_e !== false){
14766             this.fireEvent("beforeshow", this);
14767             xy = this.el.adjustForConstraints(xy);
14768         }
14769         this.el.setXY(xy);
14770         this.el.show();
14771         this.hidden = false;
14772         this.focus();
14773         this.fireEvent("show", this);
14774     },
14775
14776     focus : function(){
14777         if(!this.hidden){
14778             this.doFocus.defer(50, this);
14779         }
14780     },
14781
14782     doFocus : function(){
14783         if(!this.hidden){
14784             this.focusEl.focus();
14785         }
14786     },
14787
14788     /**
14789      * Hides this menu and optionally all parent menus
14790      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14791      */
14792     hide : function(deep){
14793         if(this.el && this.isVisible()){
14794             this.fireEvent("beforehide", this);
14795             if(this.activeItem){
14796                 this.activeItem.deactivate();
14797                 this.activeItem = null;
14798             }
14799             this.el.hide();
14800             this.hidden = true;
14801             this.fireEvent("hide", this);
14802         }
14803         if(deep === true && this.parentMenu){
14804             this.parentMenu.hide(true);
14805         }
14806     },
14807
14808     /**
14809      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14810      * Any of the following are valid:
14811      * <ul>
14812      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14813      * <li>An HTMLElement object which will be converted to a menu item</li>
14814      * <li>A menu item config object that will be created as a new menu item</li>
14815      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14816      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14817      * </ul>
14818      * Usage:
14819      * <pre><code>
14820 // Create the menu
14821 var menu = new Roo.menu.Menu();
14822
14823 // Create a menu item to add by reference
14824 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14825
14826 // Add a bunch of items at once using different methods.
14827 // Only the last item added will be returned.
14828 var item = menu.add(
14829     menuItem,                // add existing item by ref
14830     'Dynamic Item',          // new TextItem
14831     '-',                     // new separator
14832     { text: 'Config Item' }  // new item by config
14833 );
14834 </code></pre>
14835      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14836      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14837      */
14838     add : function(){
14839         var a = arguments, l = a.length, item;
14840         for(var i = 0; i < l; i++){
14841             var el = a[i];
14842             if ((typeof(el) == "object") && el.xtype && el.xns) {
14843                 el = Roo.factory(el, Roo.menu);
14844             }
14845             
14846             if(el.render){ // some kind of Item
14847                 item = this.addItem(el);
14848             }else if(typeof el == "string"){ // string
14849                 if(el == "separator" || el == "-"){
14850                     item = this.addSeparator();
14851                 }else{
14852                     item = this.addText(el);
14853                 }
14854             }else if(el.tagName || el.el){ // element
14855                 item = this.addElement(el);
14856             }else if(typeof el == "object"){ // must be menu item config?
14857                 item = this.addMenuItem(el);
14858             }
14859         }
14860         return item;
14861     },
14862
14863     /**
14864      * Returns this menu's underlying {@link Roo.Element} object
14865      * @return {Roo.Element} The element
14866      */
14867     getEl : function(){
14868         if(!this.el){
14869             this.render();
14870         }
14871         return this.el;
14872     },
14873
14874     /**
14875      * Adds a separator bar to the menu
14876      * @return {Roo.menu.Item} The menu item that was added
14877      */
14878     addSeparator : function(){
14879         return this.addItem(new Roo.menu.Separator());
14880     },
14881
14882     /**
14883      * Adds an {@link Roo.Element} object to the menu
14884      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14885      * @return {Roo.menu.Item} The menu item that was added
14886      */
14887     addElement : function(el){
14888         return this.addItem(new Roo.menu.BaseItem(el));
14889     },
14890
14891     /**
14892      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14893      * @param {Roo.menu.Item} item The menu item to add
14894      * @return {Roo.menu.Item} The menu item that was added
14895      */
14896     addItem : function(item){
14897         this.items.add(item);
14898         if(this.ul){
14899             var li = document.createElement("li");
14900             li.className = "x-menu-list-item";
14901             this.ul.dom.appendChild(li);
14902             item.render(li, this);
14903             this.delayAutoWidth();
14904         }
14905         return item;
14906     },
14907
14908     /**
14909      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14910      * @param {Object} config A MenuItem config object
14911      * @return {Roo.menu.Item} The menu item that was added
14912      */
14913     addMenuItem : function(config){
14914         if(!(config instanceof Roo.menu.Item)){
14915             if(typeof config.checked == "boolean"){ // must be check menu item config?
14916                 config = new Roo.menu.CheckItem(config);
14917             }else{
14918                 config = new Roo.menu.Item(config);
14919             }
14920         }
14921         return this.addItem(config);
14922     },
14923
14924     /**
14925      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14926      * @param {String} text The text to display in the menu item
14927      * @return {Roo.menu.Item} The menu item that was added
14928      */
14929     addText : function(text){
14930         return this.addItem(new Roo.menu.TextItem({ text : text }));
14931     },
14932
14933     /**
14934      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14935      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14936      * @param {Roo.menu.Item} item The menu item to add
14937      * @return {Roo.menu.Item} The menu item that was added
14938      */
14939     insert : function(index, item){
14940         this.items.insert(index, item);
14941         if(this.ul){
14942             var li = document.createElement("li");
14943             li.className = "x-menu-list-item";
14944             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14945             item.render(li, this);
14946             this.delayAutoWidth();
14947         }
14948         return item;
14949     },
14950
14951     /**
14952      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14953      * @param {Roo.menu.Item} item The menu item to remove
14954      */
14955     remove : function(item){
14956         this.items.removeKey(item.id);
14957         item.destroy();
14958     },
14959
14960     /**
14961      * Removes and destroys all items in the menu
14962      */
14963     removeAll : function(){
14964         var f;
14965         while(f = this.items.first()){
14966             this.remove(f);
14967         }
14968     }
14969 });
14970
14971 // MenuNav is a private utility class used internally by the Menu
14972 Roo.menu.MenuNav = function(menu){
14973     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14974     this.scope = this.menu = menu;
14975 };
14976
14977 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14978     doRelay : function(e, h){
14979         var k = e.getKey();
14980         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14981             this.menu.tryActivate(0, 1);
14982             return false;
14983         }
14984         return h.call(this.scope || this, e, this.menu);
14985     },
14986
14987     up : function(e, m){
14988         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14989             m.tryActivate(m.items.length-1, -1);
14990         }
14991     },
14992
14993     down : function(e, m){
14994         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14995             m.tryActivate(0, 1);
14996         }
14997     },
14998
14999     right : function(e, m){
15000         if(m.activeItem){
15001             m.activeItem.expandMenu(true);
15002         }
15003     },
15004
15005     left : function(e, m){
15006         m.hide();
15007         if(m.parentMenu && m.parentMenu.activeItem){
15008             m.parentMenu.activeItem.activate();
15009         }
15010     },
15011
15012     enter : function(e, m){
15013         if(m.activeItem){
15014             e.stopPropagation();
15015             m.activeItem.onClick(e);
15016             m.fireEvent("click", this, m.activeItem);
15017             return true;
15018         }
15019     }
15020 });/*
15021  * Based on:
15022  * Ext JS Library 1.1.1
15023  * Copyright(c) 2006-2007, Ext JS, LLC.
15024  *
15025  * Originally Released Under LGPL - original licence link has changed is not relivant.
15026  *
15027  * Fork - LGPL
15028  * <script type="text/javascript">
15029  */
15030  
15031 /**
15032  * @class Roo.menu.MenuMgr
15033  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15034  * @static
15035  */
15036 Roo.menu.MenuMgr = function(){
15037    var menus, active, groups = {}, attached = false, lastShow = new Date();
15038
15039    // private - called when first menu is created
15040    function init(){
15041        menus = {};
15042        active = new Roo.util.MixedCollection();
15043        Roo.get(document).addKeyListener(27, function(){
15044            if(active.length > 0){
15045                hideAll();
15046            }
15047        });
15048    }
15049
15050    // private
15051    function hideAll(){
15052        if(active && active.length > 0){
15053            var c = active.clone();
15054            c.each(function(m){
15055                m.hide();
15056            });
15057        }
15058    }
15059
15060    // private
15061    function onHide(m){
15062        active.remove(m);
15063        if(active.length < 1){
15064            Roo.get(document).un("mousedown", onMouseDown);
15065            attached = false;
15066        }
15067    }
15068
15069    // private
15070    function onShow(m){
15071        var last = active.last();
15072        lastShow = new Date();
15073        active.add(m);
15074        if(!attached){
15075            Roo.get(document).on("mousedown", onMouseDown);
15076            attached = true;
15077        }
15078        if(m.parentMenu){
15079           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15080           m.parentMenu.activeChild = m;
15081        }else if(last && last.isVisible()){
15082           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15083        }
15084    }
15085
15086    // private
15087    function onBeforeHide(m){
15088        if(m.activeChild){
15089            m.activeChild.hide();
15090        }
15091        if(m.autoHideTimer){
15092            clearTimeout(m.autoHideTimer);
15093            delete m.autoHideTimer;
15094        }
15095    }
15096
15097    // private
15098    function onBeforeShow(m){
15099        var pm = m.parentMenu;
15100        if(!pm && !m.allowOtherMenus){
15101            hideAll();
15102        }else if(pm && pm.activeChild && active != m){
15103            pm.activeChild.hide();
15104        }
15105    }
15106
15107    // private
15108    function onMouseDown(e){
15109        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15110            hideAll();
15111        }
15112    }
15113
15114    // private
15115    function onBeforeCheck(mi, state){
15116        if(state){
15117            var g = groups[mi.group];
15118            for(var i = 0, l = g.length; i < l; i++){
15119                if(g[i] != mi){
15120                    g[i].setChecked(false);
15121                }
15122            }
15123        }
15124    }
15125
15126    return {
15127
15128        /**
15129         * Hides all menus that are currently visible
15130         */
15131        hideAll : function(){
15132             hideAll();  
15133        },
15134
15135        // private
15136        register : function(menu){
15137            if(!menus){
15138                init();
15139            }
15140            menus[menu.id] = menu;
15141            menu.on("beforehide", onBeforeHide);
15142            menu.on("hide", onHide);
15143            menu.on("beforeshow", onBeforeShow);
15144            menu.on("show", onShow);
15145            var g = menu.group;
15146            if(g && menu.events["checkchange"]){
15147                if(!groups[g]){
15148                    groups[g] = [];
15149                }
15150                groups[g].push(menu);
15151                menu.on("checkchange", onCheck);
15152            }
15153        },
15154
15155         /**
15156          * Returns a {@link Roo.menu.Menu} object
15157          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15158          * be used to generate and return a new Menu instance.
15159          */
15160        get : function(menu){
15161            if(typeof menu == "string"){ // menu id
15162                return menus[menu];
15163            }else if(menu.events){  // menu instance
15164                return menu;
15165            }else if(typeof menu.length == 'number'){ // array of menu items?
15166                return new Roo.menu.Menu({items:menu});
15167            }else{ // otherwise, must be a config
15168                return new Roo.menu.Menu(menu);
15169            }
15170        },
15171
15172        // private
15173        unregister : function(menu){
15174            delete menus[menu.id];
15175            menu.un("beforehide", onBeforeHide);
15176            menu.un("hide", onHide);
15177            menu.un("beforeshow", onBeforeShow);
15178            menu.un("show", onShow);
15179            var g = menu.group;
15180            if(g && menu.events["checkchange"]){
15181                groups[g].remove(menu);
15182                menu.un("checkchange", onCheck);
15183            }
15184        },
15185
15186        // private
15187        registerCheckable : function(menuItem){
15188            var g = menuItem.group;
15189            if(g){
15190                if(!groups[g]){
15191                    groups[g] = [];
15192                }
15193                groups[g].push(menuItem);
15194                menuItem.on("beforecheckchange", onBeforeCheck);
15195            }
15196        },
15197
15198        // private
15199        unregisterCheckable : function(menuItem){
15200            var g = menuItem.group;
15201            if(g){
15202                groups[g].remove(menuItem);
15203                menuItem.un("beforecheckchange", onBeforeCheck);
15204            }
15205        }
15206    };
15207 }();/*
15208  * Based on:
15209  * Ext JS Library 1.1.1
15210  * Copyright(c) 2006-2007, Ext JS, LLC.
15211  *
15212  * Originally Released Under LGPL - original licence link has changed is not relivant.
15213  *
15214  * Fork - LGPL
15215  * <script type="text/javascript">
15216  */
15217  
15218
15219 /**
15220  * @class Roo.menu.BaseItem
15221  * @extends Roo.Component
15222  * @abstract
15223  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15224  * management and base configuration options shared by all menu components.
15225  * @constructor
15226  * Creates a new BaseItem
15227  * @param {Object} config Configuration options
15228  */
15229 Roo.menu.BaseItem = function(config){
15230     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15231
15232     this.addEvents({
15233         /**
15234          * @event click
15235          * Fires when this item is clicked
15236          * @param {Roo.menu.BaseItem} this
15237          * @param {Roo.EventObject} e
15238          */
15239         click: true,
15240         /**
15241          * @event activate
15242          * Fires when this item is activated
15243          * @param {Roo.menu.BaseItem} this
15244          */
15245         activate : true,
15246         /**
15247          * @event deactivate
15248          * Fires when this item is deactivated
15249          * @param {Roo.menu.BaseItem} this
15250          */
15251         deactivate : true
15252     });
15253
15254     if(this.handler){
15255         this.on("click", this.handler, this.scope, true);
15256     }
15257 };
15258
15259 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15260     /**
15261      * @cfg {Function} handler
15262      * A function that will handle the click event of this menu item (defaults to undefined)
15263      */
15264     /**
15265      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15266      */
15267     canActivate : false,
15268     
15269      /**
15270      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15271      */
15272     hidden: false,
15273     
15274     /**
15275      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15276      */
15277     activeClass : "x-menu-item-active",
15278     /**
15279      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15280      */
15281     hideOnClick : true,
15282     /**
15283      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15284      */
15285     hideDelay : 100,
15286
15287     // private
15288     ctype: "Roo.menu.BaseItem",
15289
15290     // private
15291     actionMode : "container",
15292
15293     // private
15294     render : function(container, parentMenu){
15295         this.parentMenu = parentMenu;
15296         Roo.menu.BaseItem.superclass.render.call(this, container);
15297         this.container.menuItemId = this.id;
15298     },
15299
15300     // private
15301     onRender : function(container, position){
15302         this.el = Roo.get(this.el);
15303         container.dom.appendChild(this.el.dom);
15304     },
15305
15306     // private
15307     onClick : function(e){
15308         if(!this.disabled && this.fireEvent("click", this, e) !== false
15309                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15310             this.handleClick(e);
15311         }else{
15312             e.stopEvent();
15313         }
15314     },
15315
15316     // private
15317     activate : function(){
15318         if(this.disabled){
15319             return false;
15320         }
15321         var li = this.container;
15322         li.addClass(this.activeClass);
15323         this.region = li.getRegion().adjust(2, 2, -2, -2);
15324         this.fireEvent("activate", this);
15325         return true;
15326     },
15327
15328     // private
15329     deactivate : function(){
15330         this.container.removeClass(this.activeClass);
15331         this.fireEvent("deactivate", this);
15332     },
15333
15334     // private
15335     shouldDeactivate : function(e){
15336         return !this.region || !this.region.contains(e.getPoint());
15337     },
15338
15339     // private
15340     handleClick : function(e){
15341         if(this.hideOnClick){
15342             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15343         }
15344     },
15345
15346     // private
15347     expandMenu : function(autoActivate){
15348         // do nothing
15349     },
15350
15351     // private
15352     hideMenu : function(){
15353         // do nothing
15354     }
15355 });/*
15356  * Based on:
15357  * Ext JS Library 1.1.1
15358  * Copyright(c) 2006-2007, Ext JS, LLC.
15359  *
15360  * Originally Released Under LGPL - original licence link has changed is not relivant.
15361  *
15362  * Fork - LGPL
15363  * <script type="text/javascript">
15364  */
15365  
15366 /**
15367  * @class Roo.menu.Adapter
15368  * @extends Roo.menu.BaseItem
15369  * @abstract
15370  * 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.
15371  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15372  * @constructor
15373  * Creates a new Adapter
15374  * @param {Object} config Configuration options
15375  */
15376 Roo.menu.Adapter = function(component, config){
15377     Roo.menu.Adapter.superclass.constructor.call(this, config);
15378     this.component = component;
15379 };
15380 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15381     // private
15382     canActivate : true,
15383
15384     // private
15385     onRender : function(container, position){
15386         this.component.render(container);
15387         this.el = this.component.getEl();
15388     },
15389
15390     // private
15391     activate : function(){
15392         if(this.disabled){
15393             return false;
15394         }
15395         this.component.focus();
15396         this.fireEvent("activate", this);
15397         return true;
15398     },
15399
15400     // private
15401     deactivate : function(){
15402         this.fireEvent("deactivate", this);
15403     },
15404
15405     // private
15406     disable : function(){
15407         this.component.disable();
15408         Roo.menu.Adapter.superclass.disable.call(this);
15409     },
15410
15411     // private
15412     enable : function(){
15413         this.component.enable();
15414         Roo.menu.Adapter.superclass.enable.call(this);
15415     }
15416 });/*
15417  * Based on:
15418  * Ext JS Library 1.1.1
15419  * Copyright(c) 2006-2007, Ext JS, LLC.
15420  *
15421  * Originally Released Under LGPL - original licence link has changed is not relivant.
15422  *
15423  * Fork - LGPL
15424  * <script type="text/javascript">
15425  */
15426
15427 /**
15428  * @class Roo.menu.TextItem
15429  * @extends Roo.menu.BaseItem
15430  * Adds a static text string to a menu, usually used as either a heading or group separator.
15431  * Note: old style constructor with text is still supported.
15432  * 
15433  * @constructor
15434  * Creates a new TextItem
15435  * @param {Object} cfg Configuration
15436  */
15437 Roo.menu.TextItem = function(cfg){
15438     if (typeof(cfg) == 'string') {
15439         this.text = cfg;
15440     } else {
15441         Roo.apply(this,cfg);
15442     }
15443     
15444     Roo.menu.TextItem.superclass.constructor.call(this);
15445 };
15446
15447 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15448     /**
15449      * @cfg {String} text Text to show on item.
15450      */
15451     text : '',
15452     
15453     /**
15454      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15455      */
15456     hideOnClick : false,
15457     /**
15458      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15459      */
15460     itemCls : "x-menu-text",
15461
15462     // private
15463     onRender : function(){
15464         var s = document.createElement("span");
15465         s.className = this.itemCls;
15466         s.innerHTML = this.text;
15467         this.el = s;
15468         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15469     }
15470 });/*
15471  * Based on:
15472  * Ext JS Library 1.1.1
15473  * Copyright(c) 2006-2007, Ext JS, LLC.
15474  *
15475  * Originally Released Under LGPL - original licence link has changed is not relivant.
15476  *
15477  * Fork - LGPL
15478  * <script type="text/javascript">
15479  */
15480
15481 /**
15482  * @class Roo.menu.Separator
15483  * @extends Roo.menu.BaseItem
15484  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15485  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15486  * @constructor
15487  * @param {Object} config Configuration options
15488  */
15489 Roo.menu.Separator = function(config){
15490     Roo.menu.Separator.superclass.constructor.call(this, config);
15491 };
15492
15493 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15494     /**
15495      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15496      */
15497     itemCls : "x-menu-sep",
15498     /**
15499      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15500      */
15501     hideOnClick : false,
15502
15503     // private
15504     onRender : function(li){
15505         var s = document.createElement("span");
15506         s.className = this.itemCls;
15507         s.innerHTML = "&#160;";
15508         this.el = s;
15509         li.addClass("x-menu-sep-li");
15510         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15511     }
15512 });/*
15513  * Based on:
15514  * Ext JS Library 1.1.1
15515  * Copyright(c) 2006-2007, Ext JS, LLC.
15516  *
15517  * Originally Released Under LGPL - original licence link has changed is not relivant.
15518  *
15519  * Fork - LGPL
15520  * <script type="text/javascript">
15521  */
15522 /**
15523  * @class Roo.menu.Item
15524  * @extends Roo.menu.BaseItem
15525  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15526  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15527  * activation and click handling.
15528  * @constructor
15529  * Creates a new Item
15530  * @param {Object} config Configuration options
15531  */
15532 Roo.menu.Item = function(config){
15533     Roo.menu.Item.superclass.constructor.call(this, config);
15534     if(this.menu){
15535         this.menu = Roo.menu.MenuMgr.get(this.menu);
15536     }
15537 };
15538 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15539     /**
15540      * @cfg {Roo.menu.Menu} menu
15541      * A Sub menu
15542      */
15543     /**
15544      * @cfg {String} text
15545      * The text to show on the menu item.
15546      */
15547     text: '',
15548      /**
15549      * @cfg {String} html to render in menu
15550      * The text to show on the menu item (HTML version).
15551      */
15552     html: '',
15553     /**
15554      * @cfg {String} icon
15555      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15556      */
15557     icon: undefined,
15558     /**
15559      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15560      */
15561     itemCls : "x-menu-item",
15562     /**
15563      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15564      */
15565     canActivate : true,
15566     /**
15567      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15568      */
15569     showDelay: 200,
15570     // doc'd in BaseItem
15571     hideDelay: 200,
15572
15573     // private
15574     ctype: "Roo.menu.Item",
15575     
15576     // private
15577     onRender : function(container, position){
15578         var el = document.createElement("a");
15579         el.hideFocus = true;
15580         el.unselectable = "on";
15581         el.href = this.href || "#";
15582         if(this.hrefTarget){
15583             el.target = this.hrefTarget;
15584         }
15585         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15586         
15587         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15588         
15589         el.innerHTML = String.format(
15590                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15591                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15592         this.el = el;
15593         Roo.menu.Item.superclass.onRender.call(this, container, position);
15594     },
15595
15596     /**
15597      * Sets the text to display in this menu item
15598      * @param {String} text The text to display
15599      * @param {Boolean} isHTML true to indicate text is pure html.
15600      */
15601     setText : function(text, isHTML){
15602         if (isHTML) {
15603             this.html = text;
15604         } else {
15605             this.text = text;
15606             this.html = '';
15607         }
15608         if(this.rendered){
15609             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15610      
15611             this.el.update(String.format(
15612                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15613                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15614             this.parentMenu.autoWidth();
15615         }
15616     },
15617
15618     // private
15619     handleClick : function(e){
15620         if(!this.href){ // if no link defined, stop the event automatically
15621             e.stopEvent();
15622         }
15623         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15624     },
15625
15626     // private
15627     activate : function(autoExpand){
15628         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15629             this.focus();
15630             if(autoExpand){
15631                 this.expandMenu();
15632             }
15633         }
15634         return true;
15635     },
15636
15637     // private
15638     shouldDeactivate : function(e){
15639         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15640             if(this.menu && this.menu.isVisible()){
15641                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15642             }
15643             return true;
15644         }
15645         return false;
15646     },
15647
15648     // private
15649     deactivate : function(){
15650         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15651         this.hideMenu();
15652     },
15653
15654     // private
15655     expandMenu : function(autoActivate){
15656         if(!this.disabled && this.menu){
15657             clearTimeout(this.hideTimer);
15658             delete this.hideTimer;
15659             if(!this.menu.isVisible() && !this.showTimer){
15660                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15661             }else if (this.menu.isVisible() && autoActivate){
15662                 this.menu.tryActivate(0, 1);
15663             }
15664         }
15665     },
15666
15667     // private
15668     deferExpand : function(autoActivate){
15669         delete this.showTimer;
15670         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15671         if(autoActivate){
15672             this.menu.tryActivate(0, 1);
15673         }
15674     },
15675
15676     // private
15677     hideMenu : function(){
15678         clearTimeout(this.showTimer);
15679         delete this.showTimer;
15680         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15681             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15682         }
15683     },
15684
15685     // private
15686     deferHide : function(){
15687         delete this.hideTimer;
15688         this.menu.hide();
15689     }
15690 });/*
15691  * Based on:
15692  * Ext JS Library 1.1.1
15693  * Copyright(c) 2006-2007, Ext JS, LLC.
15694  *
15695  * Originally Released Under LGPL - original licence link has changed is not relivant.
15696  *
15697  * Fork - LGPL
15698  * <script type="text/javascript">
15699  */
15700  
15701 /**
15702  * @class Roo.menu.CheckItem
15703  * @extends Roo.menu.Item
15704  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15705  * @constructor
15706  * Creates a new CheckItem
15707  * @param {Object} config Configuration options
15708  */
15709 Roo.menu.CheckItem = function(config){
15710     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15711     this.addEvents({
15712         /**
15713          * @event beforecheckchange
15714          * Fires before the checked value is set, providing an opportunity to cancel if needed
15715          * @param {Roo.menu.CheckItem} this
15716          * @param {Boolean} checked The new checked value that will be set
15717          */
15718         "beforecheckchange" : true,
15719         /**
15720          * @event checkchange
15721          * Fires after the checked value has been set
15722          * @param {Roo.menu.CheckItem} this
15723          * @param {Boolean} checked The checked value that was set
15724          */
15725         "checkchange" : true
15726     });
15727     if(this.checkHandler){
15728         this.on('checkchange', this.checkHandler, this.scope);
15729     }
15730 };
15731 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15732     /**
15733      * @cfg {String} group
15734      * All check items with the same group name will automatically be grouped into a single-select
15735      * radio button group (defaults to '')
15736      */
15737     /**
15738      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15739      */
15740     itemCls : "x-menu-item x-menu-check-item",
15741     /**
15742      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15743      */
15744     groupClass : "x-menu-group-item",
15745
15746     /**
15747      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15748      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15749      * initialized with checked = true will be rendered as checked.
15750      */
15751     checked: false,
15752
15753     // private
15754     ctype: "Roo.menu.CheckItem",
15755
15756     // private
15757     onRender : function(c){
15758         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15759         if(this.group){
15760             this.el.addClass(this.groupClass);
15761         }
15762         Roo.menu.MenuMgr.registerCheckable(this);
15763         if(this.checked){
15764             this.checked = false;
15765             this.setChecked(true, true);
15766         }
15767     },
15768
15769     // private
15770     destroy : function(){
15771         if(this.rendered){
15772             Roo.menu.MenuMgr.unregisterCheckable(this);
15773         }
15774         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15775     },
15776
15777     /**
15778      * Set the checked state of this item
15779      * @param {Boolean} checked The new checked value
15780      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15781      */
15782     setChecked : function(state, suppressEvent){
15783         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15784             if(this.container){
15785                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15786             }
15787             this.checked = state;
15788             if(suppressEvent !== true){
15789                 this.fireEvent("checkchange", this, state);
15790             }
15791         }
15792     },
15793
15794     // private
15795     handleClick : function(e){
15796        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15797            this.setChecked(!this.checked);
15798        }
15799        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15800     }
15801 });/*
15802  * Based on:
15803  * Ext JS Library 1.1.1
15804  * Copyright(c) 2006-2007, Ext JS, LLC.
15805  *
15806  * Originally Released Under LGPL - original licence link has changed is not relivant.
15807  *
15808  * Fork - LGPL
15809  * <script type="text/javascript">
15810  */
15811  
15812 /**
15813  * @class Roo.menu.DateItem
15814  * @extends Roo.menu.Adapter
15815  * A menu item that wraps the {@link Roo.DatPicker} component.
15816  * @constructor
15817  * Creates a new DateItem
15818  * @param {Object} config Configuration options
15819  */
15820 Roo.menu.DateItem = function(config){
15821     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15822     /** The Roo.DatePicker object @type Roo.DatePicker */
15823     this.picker = this.component;
15824     this.addEvents({select: true});
15825     
15826     this.picker.on("render", function(picker){
15827         picker.getEl().swallowEvent("click");
15828         picker.container.addClass("x-menu-date-item");
15829     });
15830
15831     this.picker.on("select", this.onSelect, this);
15832 };
15833
15834 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15835     // private
15836     onSelect : function(picker, date){
15837         this.fireEvent("select", this, date, picker);
15838         Roo.menu.DateItem.superclass.handleClick.call(this);
15839     }
15840 });/*
15841  * Based on:
15842  * Ext JS Library 1.1.1
15843  * Copyright(c) 2006-2007, Ext JS, LLC.
15844  *
15845  * Originally Released Under LGPL - original licence link has changed is not relivant.
15846  *
15847  * Fork - LGPL
15848  * <script type="text/javascript">
15849  */
15850  
15851 /**
15852  * @class Roo.menu.ColorItem
15853  * @extends Roo.menu.Adapter
15854  * A menu item that wraps the {@link Roo.ColorPalette} component.
15855  * @constructor
15856  * Creates a new ColorItem
15857  * @param {Object} config Configuration options
15858  */
15859 Roo.menu.ColorItem = function(config){
15860     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15861     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15862     this.palette = this.component;
15863     this.relayEvents(this.palette, ["select"]);
15864     if(this.selectHandler){
15865         this.on('select', this.selectHandler, this.scope);
15866     }
15867 };
15868 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15869  * Based on:
15870  * Ext JS Library 1.1.1
15871  * Copyright(c) 2006-2007, Ext JS, LLC.
15872  *
15873  * Originally Released Under LGPL - original licence link has changed is not relivant.
15874  *
15875  * Fork - LGPL
15876  * <script type="text/javascript">
15877  */
15878  
15879
15880 /**
15881  * @class Roo.menu.DateMenu
15882  * @extends Roo.menu.Menu
15883  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15884  * @constructor
15885  * Creates a new DateMenu
15886  * @param {Object} config Configuration options
15887  */
15888 Roo.menu.DateMenu = function(config){
15889     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15890     this.plain = true;
15891     var di = new Roo.menu.DateItem(config);
15892     this.add(di);
15893     /**
15894      * The {@link Roo.DatePicker} instance for this DateMenu
15895      * @type DatePicker
15896      */
15897     this.picker = di.picker;
15898     /**
15899      * @event select
15900      * @param {DatePicker} picker
15901      * @param {Date} date
15902      */
15903     this.relayEvents(di, ["select"]);
15904     this.on('beforeshow', function(){
15905         if(this.picker){
15906             this.picker.hideMonthPicker(false);
15907         }
15908     }, this);
15909 };
15910 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15911     cls:'x-date-menu'
15912 });/*
15913  * Based on:
15914  * Ext JS Library 1.1.1
15915  * Copyright(c) 2006-2007, Ext JS, LLC.
15916  *
15917  * Originally Released Under LGPL - original licence link has changed is not relivant.
15918  *
15919  * Fork - LGPL
15920  * <script type="text/javascript">
15921  */
15922  
15923
15924 /**
15925  * @class Roo.menu.ColorMenu
15926  * @extends Roo.menu.Menu
15927  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15928  * @constructor
15929  * Creates a new ColorMenu
15930  * @param {Object} config Configuration options
15931  */
15932 Roo.menu.ColorMenu = function(config){
15933     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15934     this.plain = true;
15935     var ci = new Roo.menu.ColorItem(config);
15936     this.add(ci);
15937     /**
15938      * The {@link Roo.ColorPalette} instance for this ColorMenu
15939      * @type ColorPalette
15940      */
15941     this.palette = ci.palette;
15942     /**
15943      * @event select
15944      * @param {ColorPalette} palette
15945      * @param {String} color
15946      */
15947     this.relayEvents(ci, ["select"]);
15948 };
15949 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15950  * Based on:
15951  * Ext JS Library 1.1.1
15952  * Copyright(c) 2006-2007, Ext JS, LLC.
15953  *
15954  * Originally Released Under LGPL - original licence link has changed is not relivant.
15955  *
15956  * Fork - LGPL
15957  * <script type="text/javascript">
15958  */
15959  
15960 /**
15961  * @class Roo.form.TextItem
15962  * @extends Roo.BoxComponent
15963  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15964  * @constructor
15965  * Creates a new TextItem
15966  * @param {Object} config Configuration options
15967  */
15968 Roo.form.TextItem = function(config){
15969     Roo.form.TextItem.superclass.constructor.call(this, config);
15970 };
15971
15972 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15973     
15974     /**
15975      * @cfg {String} tag the tag for this item (default div)
15976      */
15977     tag : 'div',
15978     /**
15979      * @cfg {String} html the content for this item
15980      */
15981     html : '',
15982     
15983     getAutoCreate : function()
15984     {
15985         var cfg = {
15986             id: this.id,
15987             tag: this.tag,
15988             html: this.html,
15989             cls: 'x-form-item'
15990         };
15991         
15992         return cfg;
15993         
15994     },
15995     
15996     onRender : function(ct, position)
15997     {
15998         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15999         
16000         if(!this.el){
16001             var cfg = this.getAutoCreate();
16002             if(!cfg.name){
16003                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16004             }
16005             if (!cfg.name.length) {
16006                 delete cfg.name;
16007             }
16008             this.el = ct.createChild(cfg, position);
16009         }
16010     },
16011     /*
16012      * setHTML
16013      * @param {String} html update the Contents of the element.
16014      */
16015     setHTML : function(html)
16016     {
16017         this.fieldEl.dom.innerHTML = html;
16018     }
16019     
16020 });/*
16021  * Based on:
16022  * Ext JS Library 1.1.1
16023  * Copyright(c) 2006-2007, Ext JS, LLC.
16024  *
16025  * Originally Released Under LGPL - original licence link has changed is not relivant.
16026  *
16027  * Fork - LGPL
16028  * <script type="text/javascript">
16029  */
16030  
16031 /**
16032  * @class Roo.form.Field
16033  * @extends Roo.BoxComponent
16034  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16035  * @constructor
16036  * Creates a new Field
16037  * @param {Object} config Configuration options
16038  */
16039 Roo.form.Field = function(config){
16040     Roo.form.Field.superclass.constructor.call(this, config);
16041 };
16042
16043 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16044     /**
16045      * @cfg {String} fieldLabel Label to use when rendering a form.
16046      */
16047        /**
16048      * @cfg {String} qtip Mouse over tip
16049      */
16050      
16051     /**
16052      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16053      */
16054     invalidClass : "x-form-invalid",
16055     /**
16056      * @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")
16057      */
16058     invalidText : "The value in this field is invalid",
16059     /**
16060      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16061      */
16062     focusClass : "x-form-focus",
16063     /**
16064      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16065       automatic validation (defaults to "keyup").
16066      */
16067     validationEvent : "keyup",
16068     /**
16069      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16070      */
16071     validateOnBlur : true,
16072     /**
16073      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16074      */
16075     validationDelay : 250,
16076     /**
16077      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16078      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16079      */
16080     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16081     /**
16082      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16083      */
16084     fieldClass : "x-form-field",
16085     /**
16086      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16087      *<pre>
16088 Value         Description
16089 -----------   ----------------------------------------------------------------------
16090 qtip          Display a quick tip when the user hovers over the field
16091 title         Display a default browser title attribute popup
16092 under         Add a block div beneath the field containing the error text
16093 side          Add an error icon to the right of the field with a popup on hover
16094 [element id]  Add the error text directly to the innerHTML of the specified element
16095 </pre>
16096      */
16097     msgTarget : 'qtip',
16098     /**
16099      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16100      */
16101     msgFx : 'normal',
16102
16103     /**
16104      * @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.
16105      */
16106     readOnly : false,
16107
16108     /**
16109      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16110      */
16111     disabled : false,
16112
16113     /**
16114      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16115      */
16116     inputType : undefined,
16117     
16118     /**
16119      * @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).
16120          */
16121         tabIndex : undefined,
16122         
16123     // private
16124     isFormField : true,
16125
16126     // private
16127     hasFocus : false,
16128     /**
16129      * @property {Roo.Element} fieldEl
16130      * Element Containing the rendered Field (with label etc.)
16131      */
16132     /**
16133      * @cfg {Mixed} value A value to initialize this field with.
16134      */
16135     value : undefined,
16136
16137     /**
16138      * @cfg {String} name The field's HTML name attribute.
16139      */
16140     /**
16141      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16142      */
16143     // private
16144     loadedValue : false,
16145      
16146      
16147         // private ??
16148         initComponent : function(){
16149         Roo.form.Field.superclass.initComponent.call(this);
16150         this.addEvents({
16151             /**
16152              * @event focus
16153              * Fires when this field receives input focus.
16154              * @param {Roo.form.Field} this
16155              */
16156             focus : true,
16157             /**
16158              * @event blur
16159              * Fires when this field loses input focus.
16160              * @param {Roo.form.Field} this
16161              */
16162             blur : true,
16163             /**
16164              * @event specialkey
16165              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16166              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16167              * @param {Roo.form.Field} this
16168              * @param {Roo.EventObject} e The event object
16169              */
16170             specialkey : true,
16171             /**
16172              * @event change
16173              * Fires just before the field blurs if the field value has changed.
16174              * @param {Roo.form.Field} this
16175              * @param {Mixed} newValue The new value
16176              * @param {Mixed} oldValue The original value
16177              */
16178             change : true,
16179             /**
16180              * @event invalid
16181              * Fires after the field has been marked as invalid.
16182              * @param {Roo.form.Field} this
16183              * @param {String} msg The validation message
16184              */
16185             invalid : true,
16186             /**
16187              * @event valid
16188              * Fires after the field has been validated with no errors.
16189              * @param {Roo.form.Field} this
16190              */
16191             valid : true,
16192              /**
16193              * @event keyup
16194              * Fires after the key up
16195              * @param {Roo.form.Field} this
16196              * @param {Roo.EventObject}  e The event Object
16197              */
16198             keyup : true
16199         });
16200     },
16201
16202     /**
16203      * Returns the name attribute of the field if available
16204      * @return {String} name The field name
16205      */
16206     getName: function(){
16207          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16208     },
16209
16210     // private
16211     onRender : function(ct, position){
16212         Roo.form.Field.superclass.onRender.call(this, ct, position);
16213         if(!this.el){
16214             var cfg = this.getAutoCreate();
16215             if(!cfg.name){
16216                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16217             }
16218             if (!cfg.name.length) {
16219                 delete cfg.name;
16220             }
16221             if(this.inputType){
16222                 cfg.type = this.inputType;
16223             }
16224             this.el = ct.createChild(cfg, position);
16225         }
16226         var type = this.el.dom.type;
16227         if(type){
16228             if(type == 'password'){
16229                 type = 'text';
16230             }
16231             this.el.addClass('x-form-'+type);
16232         }
16233         if(this.readOnly){
16234             this.el.dom.readOnly = true;
16235         }
16236         if(this.tabIndex !== undefined){
16237             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16238         }
16239
16240         this.el.addClass([this.fieldClass, this.cls]);
16241         this.initValue();
16242     },
16243
16244     /**
16245      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16246      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16247      * @return {Roo.form.Field} this
16248      */
16249     applyTo : function(target){
16250         this.allowDomMove = false;
16251         this.el = Roo.get(target);
16252         this.render(this.el.dom.parentNode);
16253         return this;
16254     },
16255
16256     // private
16257     initValue : function(){
16258         if(this.value !== undefined){
16259             this.setValue(this.value);
16260         }else if(this.el.dom.value.length > 0){
16261             this.setValue(this.el.dom.value);
16262         }
16263     },
16264
16265     /**
16266      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16267      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16268      */
16269     isDirty : function() {
16270         if(this.disabled) {
16271             return false;
16272         }
16273         return String(this.getValue()) !== String(this.originalValue);
16274     },
16275
16276     /**
16277      * stores the current value in loadedValue
16278      */
16279     resetHasChanged : function()
16280     {
16281         this.loadedValue = String(this.getValue());
16282     },
16283     /**
16284      * checks the current value against the 'loaded' value.
16285      * Note - will return false if 'resetHasChanged' has not been called first.
16286      */
16287     hasChanged : function()
16288     {
16289         if(this.disabled || this.readOnly) {
16290             return false;
16291         }
16292         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16293     },
16294     
16295     
16296     
16297     // private
16298     afterRender : function(){
16299         Roo.form.Field.superclass.afterRender.call(this);
16300         this.initEvents();
16301     },
16302
16303     // private
16304     fireKey : function(e){
16305         //Roo.log('field ' + e.getKey());
16306         if(e.isNavKeyPress()){
16307             this.fireEvent("specialkey", this, e);
16308         }
16309     },
16310
16311     /**
16312      * Resets the current field value to the originally loaded value and clears any validation messages
16313      */
16314     reset : function(){
16315         this.setValue(this.resetValue);
16316         this.originalValue = this.getValue();
16317         this.clearInvalid();
16318     },
16319
16320     // private
16321     initEvents : function(){
16322         // safari killled keypress - so keydown is now used..
16323         this.el.on("keydown" , this.fireKey,  this);
16324         this.el.on("focus", this.onFocus,  this);
16325         this.el.on("blur", this.onBlur,  this);
16326         this.el.relayEvent('keyup', this);
16327
16328         // reference to original value for reset
16329         this.originalValue = this.getValue();
16330         this.resetValue =  this.getValue();
16331     },
16332
16333     // private
16334     onFocus : function(){
16335         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16336             this.el.addClass(this.focusClass);
16337         }
16338         if(!this.hasFocus){
16339             this.hasFocus = true;
16340             this.startValue = this.getValue();
16341             this.fireEvent("focus", this);
16342         }
16343     },
16344
16345     beforeBlur : Roo.emptyFn,
16346
16347     // private
16348     onBlur : function(){
16349         this.beforeBlur();
16350         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16351             this.el.removeClass(this.focusClass);
16352         }
16353         this.hasFocus = false;
16354         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16355             this.validate();
16356         }
16357         var v = this.getValue();
16358         if(String(v) !== String(this.startValue)){
16359             this.fireEvent('change', this, v, this.startValue);
16360         }
16361         this.fireEvent("blur", this);
16362     },
16363
16364     /**
16365      * Returns whether or not the field value is currently valid
16366      * @param {Boolean} preventMark True to disable marking the field invalid
16367      * @return {Boolean} True if the value is valid, else false
16368      */
16369     isValid : function(preventMark){
16370         if(this.disabled){
16371             return true;
16372         }
16373         var restore = this.preventMark;
16374         this.preventMark = preventMark === true;
16375         var v = this.validateValue(this.processValue(this.getRawValue()));
16376         this.preventMark = restore;
16377         return v;
16378     },
16379
16380     /**
16381      * Validates the field value
16382      * @return {Boolean} True if the value is valid, else false
16383      */
16384     validate : function(){
16385         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16386             this.clearInvalid();
16387             return true;
16388         }
16389         return false;
16390     },
16391
16392     processValue : function(value){
16393         return value;
16394     },
16395
16396     // private
16397     // Subclasses should provide the validation implementation by overriding this
16398     validateValue : function(value){
16399         return true;
16400     },
16401
16402     /**
16403      * Mark this field as invalid
16404      * @param {String} msg The validation message
16405      */
16406     markInvalid : function(msg){
16407         if(!this.rendered || this.preventMark){ // not rendered
16408             return;
16409         }
16410         
16411         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16412         
16413         obj.el.addClass(this.invalidClass);
16414         msg = msg || this.invalidText;
16415         switch(this.msgTarget){
16416             case 'qtip':
16417                 obj.el.dom.qtip = msg;
16418                 obj.el.dom.qclass = 'x-form-invalid-tip';
16419                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16420                     Roo.QuickTips.enable();
16421                 }
16422                 break;
16423             case 'title':
16424                 this.el.dom.title = msg;
16425                 break;
16426             case 'under':
16427                 if(!this.errorEl){
16428                     var elp = this.el.findParent('.x-form-element', 5, true);
16429                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16430                     this.errorEl.setWidth(elp.getWidth(true)-20);
16431                 }
16432                 this.errorEl.update(msg);
16433                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16434                 break;
16435             case 'side':
16436                 if(!this.errorIcon){
16437                     var elp = this.el.findParent('.x-form-element', 5, true);
16438                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16439                 }
16440                 this.alignErrorIcon();
16441                 this.errorIcon.dom.qtip = msg;
16442                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16443                 this.errorIcon.show();
16444                 this.on('resize', this.alignErrorIcon, this);
16445                 break;
16446             default:
16447                 var t = Roo.getDom(this.msgTarget);
16448                 t.innerHTML = msg;
16449                 t.style.display = this.msgDisplay;
16450                 break;
16451         }
16452         this.fireEvent('invalid', this, msg);
16453     },
16454
16455     // private
16456     alignErrorIcon : function(){
16457         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16458     },
16459
16460     /**
16461      * Clear any invalid styles/messages for this field
16462      */
16463     clearInvalid : function(){
16464         if(!this.rendered || this.preventMark){ // not rendered
16465             return;
16466         }
16467         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16468         
16469         obj.el.removeClass(this.invalidClass);
16470         switch(this.msgTarget){
16471             case 'qtip':
16472                 obj.el.dom.qtip = '';
16473                 break;
16474             case 'title':
16475                 this.el.dom.title = '';
16476                 break;
16477             case 'under':
16478                 if(this.errorEl){
16479                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16480                 }
16481                 break;
16482             case 'side':
16483                 if(this.errorIcon){
16484                     this.errorIcon.dom.qtip = '';
16485                     this.errorIcon.hide();
16486                     this.un('resize', this.alignErrorIcon, this);
16487                 }
16488                 break;
16489             default:
16490                 var t = Roo.getDom(this.msgTarget);
16491                 t.innerHTML = '';
16492                 t.style.display = 'none';
16493                 break;
16494         }
16495         this.fireEvent('valid', this);
16496     },
16497
16498     /**
16499      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16500      * @return {Mixed} value The field value
16501      */
16502     getRawValue : function(){
16503         var v = this.el.getValue();
16504         
16505         return v;
16506     },
16507
16508     /**
16509      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16510      * @return {Mixed} value The field value
16511      */
16512     getValue : function(){
16513         var v = this.el.getValue();
16514          
16515         return v;
16516     },
16517
16518     /**
16519      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16520      * @param {Mixed} value The value to set
16521      */
16522     setRawValue : function(v){
16523         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16524     },
16525
16526     /**
16527      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16528      * @param {Mixed} value The value to set
16529      */
16530     setValue : function(v){
16531         this.value = v;
16532         if(this.rendered){
16533             this.el.dom.value = (v === null || v === undefined ? '' : v);
16534              this.validate();
16535         }
16536     },
16537
16538     adjustSize : function(w, h){
16539         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16540         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16541         return s;
16542     },
16543
16544     adjustWidth : function(tag, w){
16545         tag = tag.toLowerCase();
16546         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16547             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16548                 if(tag == 'input'){
16549                     return w + 2;
16550                 }
16551                 if(tag == 'textarea'){
16552                     return w-2;
16553                 }
16554             }else if(Roo.isOpera){
16555                 if(tag == 'input'){
16556                     return w + 2;
16557                 }
16558                 if(tag == 'textarea'){
16559                     return w-2;
16560                 }
16561             }
16562         }
16563         return w;
16564     }
16565 });
16566
16567
16568 // anything other than normal should be considered experimental
16569 Roo.form.Field.msgFx = {
16570     normal : {
16571         show: function(msgEl, f){
16572             msgEl.setDisplayed('block');
16573         },
16574
16575         hide : function(msgEl, f){
16576             msgEl.setDisplayed(false).update('');
16577         }
16578     },
16579
16580     slide : {
16581         show: function(msgEl, f){
16582             msgEl.slideIn('t', {stopFx:true});
16583         },
16584
16585         hide : function(msgEl, f){
16586             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16587         }
16588     },
16589
16590     slideRight : {
16591         show: function(msgEl, f){
16592             msgEl.fixDisplay();
16593             msgEl.alignTo(f.el, 'tl-tr');
16594             msgEl.slideIn('l', {stopFx:true});
16595         },
16596
16597         hide : function(msgEl, f){
16598             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16599         }
16600     }
16601 };/*
16602  * Based on:
16603  * Ext JS Library 1.1.1
16604  * Copyright(c) 2006-2007, Ext JS, LLC.
16605  *
16606  * Originally Released Under LGPL - original licence link has changed is not relivant.
16607  *
16608  * Fork - LGPL
16609  * <script type="text/javascript">
16610  */
16611  
16612
16613 /**
16614  * @class Roo.form.TextField
16615  * @extends Roo.form.Field
16616  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16617  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16618  * @constructor
16619  * Creates a new TextField
16620  * @param {Object} config Configuration options
16621  */
16622 Roo.form.TextField = function(config){
16623     Roo.form.TextField.superclass.constructor.call(this, config);
16624     this.addEvents({
16625         /**
16626          * @event autosize
16627          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16628          * according to the default logic, but this event provides a hook for the developer to apply additional
16629          * logic at runtime to resize the field if needed.
16630              * @param {Roo.form.Field} this This text field
16631              * @param {Number} width The new field width
16632              */
16633         autosize : true
16634     });
16635 };
16636
16637 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16638     /**
16639      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16640      */
16641     grow : false,
16642     /**
16643      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16644      */
16645     growMin : 30,
16646     /**
16647      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16648      */
16649     growMax : 800,
16650     /**
16651      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16652      */
16653     vtype : null,
16654     /**
16655      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16656      */
16657     maskRe : null,
16658     /**
16659      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16660      */
16661     disableKeyFilter : false,
16662     /**
16663      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16664      */
16665     allowBlank : true,
16666     /**
16667      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16668      */
16669     minLength : 0,
16670     /**
16671      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16672      */
16673     maxLength : Number.MAX_VALUE,
16674     /**
16675      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16676      */
16677     minLengthText : "The minimum length for this field is {0}",
16678     /**
16679      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16680      */
16681     maxLengthText : "The maximum length for this field is {0}",
16682     /**
16683      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16684      */
16685     selectOnFocus : false,
16686     /**
16687      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16688      */    
16689     allowLeadingSpace : false,
16690     /**
16691      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16692      */
16693     blankText : "This field is required",
16694     /**
16695      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16696      * If available, this function will be called only after the basic validators all return true, and will be passed the
16697      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16698      */
16699     validator : null,
16700     /**
16701      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16702      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16703      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16704      */
16705     regex : null,
16706     /**
16707      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16708      */
16709     regexText : "",
16710     /**
16711      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16712      */
16713     emptyText : null,
16714    
16715
16716     // private
16717     initEvents : function()
16718     {
16719         if (this.emptyText) {
16720             this.el.attr('placeholder', this.emptyText);
16721         }
16722         
16723         Roo.form.TextField.superclass.initEvents.call(this);
16724         if(this.validationEvent == 'keyup'){
16725             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16726             this.el.on('keyup', this.filterValidation, this);
16727         }
16728         else if(this.validationEvent !== false){
16729             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16730         }
16731         
16732         if(this.selectOnFocus){
16733             this.on("focus", this.preFocus, this);
16734         }
16735         if (!this.allowLeadingSpace) {
16736             this.on('blur', this.cleanLeadingSpace, this);
16737         }
16738         
16739         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16740             this.el.on("keypress", this.filterKeys, this);
16741         }
16742         if(this.grow){
16743             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16744             this.el.on("click", this.autoSize,  this);
16745         }
16746         if(this.el.is('input[type=password]') && Roo.isSafari){
16747             this.el.on('keydown', this.SafariOnKeyDown, this);
16748         }
16749     },
16750
16751     processValue : function(value){
16752         if(this.stripCharsRe){
16753             var newValue = value.replace(this.stripCharsRe, '');
16754             if(newValue !== value){
16755                 this.setRawValue(newValue);
16756                 return newValue;
16757             }
16758         }
16759         return value;
16760     },
16761
16762     filterValidation : function(e){
16763         if(!e.isNavKeyPress()){
16764             this.validationTask.delay(this.validationDelay);
16765         }
16766     },
16767
16768     // private
16769     onKeyUp : function(e){
16770         if(!e.isNavKeyPress()){
16771             this.autoSize();
16772         }
16773     },
16774     // private - clean the leading white space
16775     cleanLeadingSpace : function(e)
16776     {
16777         if ( this.inputType == 'file') {
16778             return;
16779         }
16780         
16781         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16782     },
16783     /**
16784      * Resets the current field value to the originally-loaded value and clears any validation messages.
16785      *  
16786      */
16787     reset : function(){
16788         Roo.form.TextField.superclass.reset.call(this);
16789        
16790     }, 
16791     // private
16792     preFocus : function(){
16793         
16794         if(this.selectOnFocus){
16795             this.el.dom.select();
16796         }
16797     },
16798
16799     
16800     // private
16801     filterKeys : function(e){
16802         var k = e.getKey();
16803         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16804             return;
16805         }
16806         var c = e.getCharCode(), cc = String.fromCharCode(c);
16807         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16808             return;
16809         }
16810         if(!this.maskRe.test(cc)){
16811             e.stopEvent();
16812         }
16813     },
16814
16815     setValue : function(v){
16816         
16817         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16818         
16819         this.autoSize();
16820     },
16821
16822     /**
16823      * Validates a value according to the field's validation rules and marks the field as invalid
16824      * if the validation fails
16825      * @param {Mixed} value The value to validate
16826      * @return {Boolean} True if the value is valid, else false
16827      */
16828     validateValue : function(value){
16829         if(value.length < 1)  { // if it's blank
16830              if(this.allowBlank){
16831                 this.clearInvalid();
16832                 return true;
16833              }else{
16834                 this.markInvalid(this.blankText);
16835                 return false;
16836              }
16837         }
16838         if(value.length < this.minLength){
16839             this.markInvalid(String.format(this.minLengthText, this.minLength));
16840             return false;
16841         }
16842         if(value.length > this.maxLength){
16843             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16844             return false;
16845         }
16846         if(this.vtype){
16847             var vt = Roo.form.VTypes;
16848             if(!vt[this.vtype](value, this)){
16849                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16850                 return false;
16851             }
16852         }
16853         if(typeof this.validator == "function"){
16854             var msg = this.validator(value);
16855             if(msg !== true){
16856                 this.markInvalid(msg);
16857                 return false;
16858             }
16859         }
16860         if(this.regex && !this.regex.test(value)){
16861             this.markInvalid(this.regexText);
16862             return false;
16863         }
16864         return true;
16865     },
16866
16867     /**
16868      * Selects text in this field
16869      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16870      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16871      */
16872     selectText : function(start, end){
16873         var v = this.getRawValue();
16874         if(v.length > 0){
16875             start = start === undefined ? 0 : start;
16876             end = end === undefined ? v.length : end;
16877             var d = this.el.dom;
16878             if(d.setSelectionRange){
16879                 d.setSelectionRange(start, end);
16880             }else if(d.createTextRange){
16881                 var range = d.createTextRange();
16882                 range.moveStart("character", start);
16883                 range.moveEnd("character", v.length-end);
16884                 range.select();
16885             }
16886         }
16887     },
16888
16889     /**
16890      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16891      * This only takes effect if grow = true, and fires the autosize event.
16892      */
16893     autoSize : function(){
16894         if(!this.grow || !this.rendered){
16895             return;
16896         }
16897         if(!this.metrics){
16898             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16899         }
16900         var el = this.el;
16901         var v = el.dom.value;
16902         var d = document.createElement('div');
16903         d.appendChild(document.createTextNode(v));
16904         v = d.innerHTML;
16905         d = null;
16906         v += "&#160;";
16907         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16908         this.el.setWidth(w);
16909         this.fireEvent("autosize", this, w);
16910     },
16911     
16912     // private
16913     SafariOnKeyDown : function(event)
16914     {
16915         // this is a workaround for a password hang bug on chrome/ webkit.
16916         
16917         var isSelectAll = false;
16918         
16919         if(this.el.dom.selectionEnd > 0){
16920             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16921         }
16922         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16923             event.preventDefault();
16924             this.setValue('');
16925             return;
16926         }
16927         
16928         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16929             
16930             event.preventDefault();
16931             // this is very hacky as keydown always get's upper case.
16932             
16933             var cc = String.fromCharCode(event.getCharCode());
16934             
16935             
16936             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16937             
16938         }
16939         
16940         
16941     }
16942 });/*
16943  * Based on:
16944  * Ext JS Library 1.1.1
16945  * Copyright(c) 2006-2007, Ext JS, LLC.
16946  *
16947  * Originally Released Under LGPL - original licence link has changed is not relivant.
16948  *
16949  * Fork - LGPL
16950  * <script type="text/javascript">
16951  */
16952  
16953 /**
16954  * @class Roo.form.Hidden
16955  * @extends Roo.form.TextField
16956  * Simple Hidden element used on forms 
16957  * 
16958  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16959  * 
16960  * @constructor
16961  * Creates a new Hidden form element.
16962  * @param {Object} config Configuration options
16963  */
16964
16965
16966
16967 // easy hidden field...
16968 Roo.form.Hidden = function(config){
16969     Roo.form.Hidden.superclass.constructor.call(this, config);
16970 };
16971   
16972 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16973     fieldLabel:      '',
16974     inputType:      'hidden',
16975     width:          50,
16976     allowBlank:     true,
16977     labelSeparator: '',
16978     hidden:         true,
16979     itemCls :       'x-form-item-display-none'
16980
16981
16982 });
16983
16984
16985 /*
16986  * Based on:
16987  * Ext JS Library 1.1.1
16988  * Copyright(c) 2006-2007, Ext JS, LLC.
16989  *
16990  * Originally Released Under LGPL - original licence link has changed is not relivant.
16991  *
16992  * Fork - LGPL
16993  * <script type="text/javascript">
16994  */
16995  
16996 /**
16997  * @class Roo.form.TriggerField
16998  * @extends Roo.form.TextField
16999  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17000  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17001  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17002  * for which you can provide a custom implementation.  For example:
17003  * <pre><code>
17004 var trigger = new Roo.form.TriggerField();
17005 trigger.onTriggerClick = myTriggerFn;
17006 trigger.applyTo('my-field');
17007 </code></pre>
17008  *
17009  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17010  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17011  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17012  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17013  * @constructor
17014  * Create a new TriggerField.
17015  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17016  * to the base TextField)
17017  */
17018 Roo.form.TriggerField = function(config){
17019     this.mimicing = false;
17020     Roo.form.TriggerField.superclass.constructor.call(this, config);
17021 };
17022
17023 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17024     /**
17025      * @cfg {String} triggerClass A CSS class to apply to the trigger
17026      */
17027     /**
17028      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17029      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17030      */
17031     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17032     /**
17033      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17034      */
17035     hideTrigger:false,
17036
17037     /** @cfg {Boolean} grow @hide */
17038     /** @cfg {Number} growMin @hide */
17039     /** @cfg {Number} growMax @hide */
17040
17041     /**
17042      * @hide 
17043      * @method
17044      */
17045     autoSize: Roo.emptyFn,
17046     // private
17047     monitorTab : true,
17048     // private
17049     deferHeight : true,
17050
17051     
17052     actionMode : 'wrap',
17053     // private
17054     onResize : function(w, h){
17055         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17056         if(typeof w == 'number'){
17057             var x = w - this.trigger.getWidth();
17058             this.el.setWidth(this.adjustWidth('input', x));
17059             this.trigger.setStyle('left', x+'px');
17060         }
17061     },
17062
17063     // private
17064     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17065
17066     // private
17067     getResizeEl : function(){
17068         return this.wrap;
17069     },
17070
17071     // private
17072     getPositionEl : function(){
17073         return this.wrap;
17074     },
17075
17076     // private
17077     alignErrorIcon : function(){
17078         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17079     },
17080
17081     // private
17082     onRender : function(ct, position){
17083         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17084         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17085         this.trigger = this.wrap.createChild(this.triggerConfig ||
17086                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17087         if(this.hideTrigger){
17088             this.trigger.setDisplayed(false);
17089         }
17090         this.initTrigger();
17091         if(!this.width){
17092             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17093         }
17094     },
17095
17096     // private
17097     initTrigger : function(){
17098         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17099         this.trigger.addClassOnOver('x-form-trigger-over');
17100         this.trigger.addClassOnClick('x-form-trigger-click');
17101     },
17102
17103     // private
17104     onDestroy : function(){
17105         if(this.trigger){
17106             this.trigger.removeAllListeners();
17107             this.trigger.remove();
17108         }
17109         if(this.wrap){
17110             this.wrap.remove();
17111         }
17112         Roo.form.TriggerField.superclass.onDestroy.call(this);
17113     },
17114
17115     // private
17116     onFocus : function(){
17117         Roo.form.TriggerField.superclass.onFocus.call(this);
17118         if(!this.mimicing){
17119             this.wrap.addClass('x-trigger-wrap-focus');
17120             this.mimicing = true;
17121             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17122             if(this.monitorTab){
17123                 this.el.on("keydown", this.checkTab, this);
17124             }
17125         }
17126     },
17127
17128     // private
17129     checkTab : function(e){
17130         if(e.getKey() == e.TAB){
17131             this.triggerBlur();
17132         }
17133     },
17134
17135     // private
17136     onBlur : function(){
17137         // do nothing
17138     },
17139
17140     // private
17141     mimicBlur : function(e, t){
17142         if(!this.wrap.contains(t) && this.validateBlur()){
17143             this.triggerBlur();
17144         }
17145     },
17146
17147     // private
17148     triggerBlur : function(){
17149         this.mimicing = false;
17150         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17151         if(this.monitorTab){
17152             this.el.un("keydown", this.checkTab, this);
17153         }
17154         this.wrap.removeClass('x-trigger-wrap-focus');
17155         Roo.form.TriggerField.superclass.onBlur.call(this);
17156     },
17157
17158     // private
17159     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17160     validateBlur : function(e, t){
17161         return true;
17162     },
17163
17164     // private
17165     onDisable : function(){
17166         Roo.form.TriggerField.superclass.onDisable.call(this);
17167         if(this.wrap){
17168             this.wrap.addClass('x-item-disabled');
17169         }
17170     },
17171
17172     // private
17173     onEnable : function(){
17174         Roo.form.TriggerField.superclass.onEnable.call(this);
17175         if(this.wrap){
17176             this.wrap.removeClass('x-item-disabled');
17177         }
17178     },
17179
17180     // private
17181     onShow : function(){
17182         var ae = this.getActionEl();
17183         
17184         if(ae){
17185             ae.dom.style.display = '';
17186             ae.dom.style.visibility = 'visible';
17187         }
17188     },
17189
17190     // private
17191     
17192     onHide : function(){
17193         var ae = this.getActionEl();
17194         ae.dom.style.display = 'none';
17195     },
17196
17197     /**
17198      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17199      * by an implementing function.
17200      * @method
17201      * @param {EventObject} e
17202      */
17203     onTriggerClick : Roo.emptyFn
17204 });
17205
17206 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17207 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17208 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17209 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17210     initComponent : function(){
17211         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17212
17213         this.triggerConfig = {
17214             tag:'span', cls:'x-form-twin-triggers', cn:[
17215             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17216             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17217         ]};
17218     },
17219
17220     getTrigger : function(index){
17221         return this.triggers[index];
17222     },
17223
17224     initTrigger : function(){
17225         var ts = this.trigger.select('.x-form-trigger', true);
17226         this.wrap.setStyle('overflow', 'hidden');
17227         var triggerField = this;
17228         ts.each(function(t, all, index){
17229             t.hide = function(){
17230                 var w = triggerField.wrap.getWidth();
17231                 this.dom.style.display = 'none';
17232                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17233             };
17234             t.show = function(){
17235                 var w = triggerField.wrap.getWidth();
17236                 this.dom.style.display = '';
17237                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17238             };
17239             var triggerIndex = 'Trigger'+(index+1);
17240
17241             if(this['hide'+triggerIndex]){
17242                 t.dom.style.display = 'none';
17243             }
17244             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17245             t.addClassOnOver('x-form-trigger-over');
17246             t.addClassOnClick('x-form-trigger-click');
17247         }, this);
17248         this.triggers = ts.elements;
17249     },
17250
17251     onTrigger1Click : Roo.emptyFn,
17252     onTrigger2Click : Roo.emptyFn
17253 });/*
17254  * Based on:
17255  * Ext JS Library 1.1.1
17256  * Copyright(c) 2006-2007, Ext JS, LLC.
17257  *
17258  * Originally Released Under LGPL - original licence link has changed is not relivant.
17259  *
17260  * Fork - LGPL
17261  * <script type="text/javascript">
17262  */
17263  
17264 /**
17265  * @class Roo.form.TextArea
17266  * @extends Roo.form.TextField
17267  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17268  * support for auto-sizing.
17269  * @constructor
17270  * Creates a new TextArea
17271  * @param {Object} config Configuration options
17272  */
17273 Roo.form.TextArea = function(config){
17274     Roo.form.TextArea.superclass.constructor.call(this, config);
17275     // these are provided exchanges for backwards compat
17276     // minHeight/maxHeight were replaced by growMin/growMax to be
17277     // compatible with TextField growing config values
17278     if(this.minHeight !== undefined){
17279         this.growMin = this.minHeight;
17280     }
17281     if(this.maxHeight !== undefined){
17282         this.growMax = this.maxHeight;
17283     }
17284 };
17285
17286 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17287     /**
17288      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17289      */
17290     growMin : 60,
17291     /**
17292      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17293      */
17294     growMax: 1000,
17295     /**
17296      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17297      * in the field (equivalent to setting overflow: hidden, defaults to false)
17298      */
17299     preventScrollbars: false,
17300     /**
17301      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17302      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17303      */
17304
17305     // private
17306     onRender : function(ct, position){
17307         if(!this.el){
17308             this.defaultAutoCreate = {
17309                 tag: "textarea",
17310                 style:"width:300px;height:60px;",
17311                 autocomplete: "new-password"
17312             };
17313         }
17314         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17315         if(this.grow){
17316             this.textSizeEl = Roo.DomHelper.append(document.body, {
17317                 tag: "pre", cls: "x-form-grow-sizer"
17318             });
17319             if(this.preventScrollbars){
17320                 this.el.setStyle("overflow", "hidden");
17321             }
17322             this.el.setHeight(this.growMin);
17323         }
17324     },
17325
17326     onDestroy : function(){
17327         if(this.textSizeEl){
17328             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17329         }
17330         Roo.form.TextArea.superclass.onDestroy.call(this);
17331     },
17332
17333     // private
17334     onKeyUp : function(e){
17335         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17336             this.autoSize();
17337         }
17338     },
17339
17340     /**
17341      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17342      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17343      */
17344     autoSize : function(){
17345         if(!this.grow || !this.textSizeEl){
17346             return;
17347         }
17348         var el = this.el;
17349         var v = el.dom.value;
17350         var ts = this.textSizeEl;
17351
17352         ts.innerHTML = '';
17353         ts.appendChild(document.createTextNode(v));
17354         v = ts.innerHTML;
17355
17356         Roo.fly(ts).setWidth(this.el.getWidth());
17357         if(v.length < 1){
17358             v = "&#160;&#160;";
17359         }else{
17360             if(Roo.isIE){
17361                 v = v.replace(/\n/g, '<p>&#160;</p>');
17362             }
17363             v += "&#160;\n&#160;";
17364         }
17365         ts.innerHTML = v;
17366         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17367         if(h != this.lastHeight){
17368             this.lastHeight = h;
17369             this.el.setHeight(h);
17370             this.fireEvent("autosize", this, h);
17371         }
17372     }
17373 });/*
17374  * Based on:
17375  * Ext JS Library 1.1.1
17376  * Copyright(c) 2006-2007, Ext JS, LLC.
17377  *
17378  * Originally Released Under LGPL - original licence link has changed is not relivant.
17379  *
17380  * Fork - LGPL
17381  * <script type="text/javascript">
17382  */
17383  
17384
17385 /**
17386  * @class Roo.form.NumberField
17387  * @extends Roo.form.TextField
17388  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17389  * @constructor
17390  * Creates a new NumberField
17391  * @param {Object} config Configuration options
17392  */
17393 Roo.form.NumberField = function(config){
17394     Roo.form.NumberField.superclass.constructor.call(this, config);
17395 };
17396
17397 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17398     /**
17399      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17400      */
17401     fieldClass: "x-form-field x-form-num-field",
17402     /**
17403      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17404      */
17405     allowDecimals : true,
17406     /**
17407      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17408      */
17409     decimalSeparator : ".",
17410     /**
17411      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17412      */
17413     decimalPrecision : 2,
17414     /**
17415      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17416      */
17417     allowNegative : true,
17418     /**
17419      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17420      */
17421     minValue : Number.NEGATIVE_INFINITY,
17422     /**
17423      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17424      */
17425     maxValue : Number.MAX_VALUE,
17426     /**
17427      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17428      */
17429     minText : "The minimum value for this field is {0}",
17430     /**
17431      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17432      */
17433     maxText : "The maximum value for this field is {0}",
17434     /**
17435      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17436      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17437      */
17438     nanText : "{0} is not a valid number",
17439
17440     // private
17441     initEvents : function(){
17442         Roo.form.NumberField.superclass.initEvents.call(this);
17443         var allowed = "0123456789";
17444         if(this.allowDecimals){
17445             allowed += this.decimalSeparator;
17446         }
17447         if(this.allowNegative){
17448             allowed += "-";
17449         }
17450         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17451         var keyPress = function(e){
17452             var k = e.getKey();
17453             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17454                 return;
17455             }
17456             var c = e.getCharCode();
17457             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17458                 e.stopEvent();
17459             }
17460         };
17461         this.el.on("keypress", keyPress, this);
17462     },
17463
17464     // private
17465     validateValue : function(value){
17466         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17467             return false;
17468         }
17469         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17470              return true;
17471         }
17472         var num = this.parseValue(value);
17473         if(isNaN(num)){
17474             this.markInvalid(String.format(this.nanText, value));
17475             return false;
17476         }
17477         if(num < this.minValue){
17478             this.markInvalid(String.format(this.minText, this.minValue));
17479             return false;
17480         }
17481         if(num > this.maxValue){
17482             this.markInvalid(String.format(this.maxText, this.maxValue));
17483             return false;
17484         }
17485         return true;
17486     },
17487
17488     getValue : function(){
17489         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17490     },
17491
17492     // private
17493     parseValue : function(value){
17494         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17495         return isNaN(value) ? '' : value;
17496     },
17497
17498     // private
17499     fixPrecision : function(value){
17500         var nan = isNaN(value);
17501         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17502             return nan ? '' : value;
17503         }
17504         return parseFloat(value).toFixed(this.decimalPrecision);
17505     },
17506
17507     setValue : function(v){
17508         v = this.fixPrecision(v);
17509         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17510     },
17511
17512     // private
17513     decimalPrecisionFcn : function(v){
17514         return Math.floor(v);
17515     },
17516
17517     beforeBlur : function(){
17518         var v = this.parseValue(this.getRawValue());
17519         if(v){
17520             this.setValue(v);
17521         }
17522     }
17523 });/*
17524  * Based on:
17525  * Ext JS Library 1.1.1
17526  * Copyright(c) 2006-2007, Ext JS, LLC.
17527  *
17528  * Originally Released Under LGPL - original licence link has changed is not relivant.
17529  *
17530  * Fork - LGPL
17531  * <script type="text/javascript">
17532  */
17533  
17534 /**
17535  * @class Roo.form.DateField
17536  * @extends Roo.form.TriggerField
17537  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17538 * @constructor
17539 * Create a new DateField
17540 * @param {Object} config
17541  */
17542 Roo.form.DateField = function(config)
17543 {
17544     Roo.form.DateField.superclass.constructor.call(this, config);
17545     
17546       this.addEvents({
17547          
17548         /**
17549          * @event select
17550          * Fires when a date is selected
17551              * @param {Roo.form.DateField} combo This combo box
17552              * @param {Date} date The date selected
17553              */
17554         'select' : true
17555          
17556     });
17557     
17558     
17559     if(typeof this.minValue == "string") {
17560         this.minValue = this.parseDate(this.minValue);
17561     }
17562     if(typeof this.maxValue == "string") {
17563         this.maxValue = this.parseDate(this.maxValue);
17564     }
17565     this.ddMatch = null;
17566     if(this.disabledDates){
17567         var dd = this.disabledDates;
17568         var re = "(?:";
17569         for(var i = 0; i < dd.length; i++){
17570             re += dd[i];
17571             if(i != dd.length-1) {
17572                 re += "|";
17573             }
17574         }
17575         this.ddMatch = new RegExp(re + ")");
17576     }
17577 };
17578
17579 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17580     /**
17581      * @cfg {String} format
17582      * The default date format string which can be overriden for localization support.  The format must be
17583      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17584      */
17585     format : "m/d/y",
17586     /**
17587      * @cfg {String} altFormats
17588      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17589      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17590      */
17591     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17592     /**
17593      * @cfg {Array} disabledDays
17594      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17595      */
17596     disabledDays : null,
17597     /**
17598      * @cfg {String} disabledDaysText
17599      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17600      */
17601     disabledDaysText : "Disabled",
17602     /**
17603      * @cfg {Array} disabledDates
17604      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17605      * expression so they are very powerful. Some examples:
17606      * <ul>
17607      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17608      * <li>["03/08", "09/16"] would disable those days for every year</li>
17609      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17610      * <li>["03/../2006"] would disable every day in March 2006</li>
17611      * <li>["^03"] would disable every day in every March</li>
17612      * </ul>
17613      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17614      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17615      */
17616     disabledDates : null,
17617     /**
17618      * @cfg {String} disabledDatesText
17619      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17620      */
17621     disabledDatesText : "Disabled",
17622         
17623         
17624         /**
17625      * @cfg {Date/String} zeroValue
17626      * if the date is less that this number, then the field is rendered as empty
17627      * default is 1800
17628      */
17629         zeroValue : '1800-01-01',
17630         
17631         
17632     /**
17633      * @cfg {Date/String} minValue
17634      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17635      * valid format (defaults to null).
17636      */
17637     minValue : null,
17638     /**
17639      * @cfg {Date/String} maxValue
17640      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17641      * valid format (defaults to null).
17642      */
17643     maxValue : null,
17644     /**
17645      * @cfg {String} minText
17646      * The error text to display when the date in the cell is before minValue (defaults to
17647      * 'The date in this field must be after {minValue}').
17648      */
17649     minText : "The date in this field must be equal to or after {0}",
17650     /**
17651      * @cfg {String} maxText
17652      * The error text to display when the date in the cell is after maxValue (defaults to
17653      * 'The date in this field must be before {maxValue}').
17654      */
17655     maxText : "The date in this field must be equal to or before {0}",
17656     /**
17657      * @cfg {String} invalidText
17658      * The error text to display when the date in the field is invalid (defaults to
17659      * '{value} is not a valid date - it must be in the format {format}').
17660      */
17661     invalidText : "{0} is not a valid date - it must be in the format {1}",
17662     /**
17663      * @cfg {String} triggerClass
17664      * An additional CSS class used to style the trigger button.  The trigger will always get the
17665      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17666      * which displays a calendar icon).
17667      */
17668     triggerClass : 'x-form-date-trigger',
17669     
17670
17671     /**
17672      * @cfg {Boolean} useIso
17673      * if enabled, then the date field will use a hidden field to store the 
17674      * real value as iso formated date. default (false)
17675      */ 
17676     useIso : false,
17677     /**
17678      * @cfg {String/Object} autoCreate
17679      * A DomHelper element spec, or true for a default element spec (defaults to
17680      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17681      */ 
17682     // private
17683     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17684     
17685     // private
17686     hiddenField: false,
17687     
17688     onRender : function(ct, position)
17689     {
17690         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17691         if (this.useIso) {
17692             //this.el.dom.removeAttribute('name'); 
17693             Roo.log("Changing name?");
17694             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17695             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17696                     'before', true);
17697             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17698             // prevent input submission
17699             this.hiddenName = this.name;
17700         }
17701             
17702             
17703     },
17704     
17705     // private
17706     validateValue : function(value)
17707     {
17708         value = this.formatDate(value);
17709         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17710             Roo.log('super failed');
17711             return false;
17712         }
17713         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17714              return true;
17715         }
17716         var svalue = value;
17717         value = this.parseDate(value);
17718         if(!value){
17719             Roo.log('parse date failed' + svalue);
17720             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17721             return false;
17722         }
17723         var time = value.getTime();
17724         if(this.minValue && time < this.minValue.getTime()){
17725             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17726             return false;
17727         }
17728         if(this.maxValue && time > this.maxValue.getTime()){
17729             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17730             return false;
17731         }
17732         if(this.disabledDays){
17733             var day = value.getDay();
17734             for(var i = 0; i < this.disabledDays.length; i++) {
17735                 if(day === this.disabledDays[i]){
17736                     this.markInvalid(this.disabledDaysText);
17737                     return false;
17738                 }
17739             }
17740         }
17741         var fvalue = this.formatDate(value);
17742         if(this.ddMatch && this.ddMatch.test(fvalue)){
17743             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17744             return false;
17745         }
17746         return true;
17747     },
17748
17749     // private
17750     // Provides logic to override the default TriggerField.validateBlur which just returns true
17751     validateBlur : function(){
17752         return !this.menu || !this.menu.isVisible();
17753     },
17754     
17755     getName: function()
17756     {
17757         // returns hidden if it's set..
17758         if (!this.rendered) {return ''};
17759         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17760         
17761     },
17762
17763     /**
17764      * Returns the current date value of the date field.
17765      * @return {Date} The date value
17766      */
17767     getValue : function(){
17768         
17769         return  this.hiddenField ?
17770                 this.hiddenField.value :
17771                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17772     },
17773
17774     /**
17775      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17776      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17777      * (the default format used is "m/d/y").
17778      * <br />Usage:
17779      * <pre><code>
17780 //All of these calls set the same date value (May 4, 2006)
17781
17782 //Pass a date object:
17783 var dt = new Date('5/4/06');
17784 dateField.setValue(dt);
17785
17786 //Pass a date string (default format):
17787 dateField.setValue('5/4/06');
17788
17789 //Pass a date string (custom format):
17790 dateField.format = 'Y-m-d';
17791 dateField.setValue('2006-5-4');
17792 </code></pre>
17793      * @param {String/Date} date The date or valid date string
17794      */
17795     setValue : function(date){
17796         if (this.hiddenField) {
17797             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17798         }
17799         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17800         // make sure the value field is always stored as a date..
17801         this.value = this.parseDate(date);
17802         
17803         
17804     },
17805
17806     // private
17807     parseDate : function(value){
17808                 
17809                 if (value instanceof Date) {
17810                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17811                                 return  '';
17812                         }
17813                         return value;
17814                 }
17815                 
17816                 
17817         if(!value || value instanceof Date){
17818             return value;
17819         }
17820         var v = Date.parseDate(value, this.format);
17821          if (!v && this.useIso) {
17822             v = Date.parseDate(value, 'Y-m-d');
17823         }
17824         if(!v && this.altFormats){
17825             if(!this.altFormatsArray){
17826                 this.altFormatsArray = this.altFormats.split("|");
17827             }
17828             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17829                 v = Date.parseDate(value, this.altFormatsArray[i]);
17830             }
17831         }
17832                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17833                         v = '';
17834                 }
17835         return v;
17836     },
17837
17838     // private
17839     formatDate : function(date, fmt){
17840         return (!date || !(date instanceof Date)) ?
17841                date : date.dateFormat(fmt || this.format);
17842     },
17843
17844     // private
17845     menuListeners : {
17846         select: function(m, d){
17847             
17848             this.setValue(d);
17849             this.fireEvent('select', this, d);
17850         },
17851         show : function(){ // retain focus styling
17852             this.onFocus();
17853         },
17854         hide : function(){
17855             this.focus.defer(10, this);
17856             var ml = this.menuListeners;
17857             this.menu.un("select", ml.select,  this);
17858             this.menu.un("show", ml.show,  this);
17859             this.menu.un("hide", ml.hide,  this);
17860         }
17861     },
17862
17863     // private
17864     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17865     onTriggerClick : function(){
17866         if(this.disabled){
17867             return;
17868         }
17869         if(this.menu == null){
17870             this.menu = new Roo.menu.DateMenu();
17871         }
17872         Roo.apply(this.menu.picker,  {
17873             showClear: this.allowBlank,
17874             minDate : this.minValue,
17875             maxDate : this.maxValue,
17876             disabledDatesRE : this.ddMatch,
17877             disabledDatesText : this.disabledDatesText,
17878             disabledDays : this.disabledDays,
17879             disabledDaysText : this.disabledDaysText,
17880             format : this.useIso ? 'Y-m-d' : this.format,
17881             minText : String.format(this.minText, this.formatDate(this.minValue)),
17882             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17883         });
17884         this.menu.on(Roo.apply({}, this.menuListeners, {
17885             scope:this
17886         }));
17887         this.menu.picker.setValue(this.getValue() || new Date());
17888         this.menu.show(this.el, "tl-bl?");
17889     },
17890
17891     beforeBlur : function(){
17892         var v = this.parseDate(this.getRawValue());
17893         if(v){
17894             this.setValue(v);
17895         }
17896     },
17897
17898     /*@
17899      * overide
17900      * 
17901      */
17902     isDirty : function() {
17903         if(this.disabled) {
17904             return false;
17905         }
17906         
17907         if(typeof(this.startValue) === 'undefined'){
17908             return false;
17909         }
17910         
17911         return String(this.getValue()) !== String(this.startValue);
17912         
17913     },
17914     // @overide
17915     cleanLeadingSpace : function(e)
17916     {
17917        return;
17918     }
17919     
17920 });/*
17921  * Based on:
17922  * Ext JS Library 1.1.1
17923  * Copyright(c) 2006-2007, Ext JS, LLC.
17924  *
17925  * Originally Released Under LGPL - original licence link has changed is not relivant.
17926  *
17927  * Fork - LGPL
17928  * <script type="text/javascript">
17929  */
17930  
17931 /**
17932  * @class Roo.form.MonthField
17933  * @extends Roo.form.TriggerField
17934  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17935 * @constructor
17936 * Create a new MonthField
17937 * @param {Object} config
17938  */
17939 Roo.form.MonthField = function(config){
17940     
17941     Roo.form.MonthField.superclass.constructor.call(this, config);
17942     
17943       this.addEvents({
17944          
17945         /**
17946          * @event select
17947          * Fires when a date is selected
17948              * @param {Roo.form.MonthFieeld} combo This combo box
17949              * @param {Date} date The date selected
17950              */
17951         'select' : true
17952          
17953     });
17954     
17955     
17956     if(typeof this.minValue == "string") {
17957         this.minValue = this.parseDate(this.minValue);
17958     }
17959     if(typeof this.maxValue == "string") {
17960         this.maxValue = this.parseDate(this.maxValue);
17961     }
17962     this.ddMatch = null;
17963     if(this.disabledDates){
17964         var dd = this.disabledDates;
17965         var re = "(?:";
17966         for(var i = 0; i < dd.length; i++){
17967             re += dd[i];
17968             if(i != dd.length-1) {
17969                 re += "|";
17970             }
17971         }
17972         this.ddMatch = new RegExp(re + ")");
17973     }
17974 };
17975
17976 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17977     /**
17978      * @cfg {String} format
17979      * The default date format string which can be overriden for localization support.  The format must be
17980      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17981      */
17982     format : "M Y",
17983     /**
17984      * @cfg {String} altFormats
17985      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17986      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17987      */
17988     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17989     /**
17990      * @cfg {Array} disabledDays
17991      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17992      */
17993     disabledDays : [0,1,2,3,4,5,6],
17994     /**
17995      * @cfg {String} disabledDaysText
17996      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17997      */
17998     disabledDaysText : "Disabled",
17999     /**
18000      * @cfg {Array} disabledDates
18001      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18002      * expression so they are very powerful. Some examples:
18003      * <ul>
18004      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18005      * <li>["03/08", "09/16"] would disable those days for every year</li>
18006      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18007      * <li>["03/../2006"] would disable every day in March 2006</li>
18008      * <li>["^03"] would disable every day in every March</li>
18009      * </ul>
18010      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18011      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18012      */
18013     disabledDates : null,
18014     /**
18015      * @cfg {String} disabledDatesText
18016      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18017      */
18018     disabledDatesText : "Disabled",
18019     /**
18020      * @cfg {Date/String} minValue
18021      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18022      * valid format (defaults to null).
18023      */
18024     minValue : null,
18025     /**
18026      * @cfg {Date/String} maxValue
18027      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18028      * valid format (defaults to null).
18029      */
18030     maxValue : null,
18031     /**
18032      * @cfg {String} minText
18033      * The error text to display when the date in the cell is before minValue (defaults to
18034      * 'The date in this field must be after {minValue}').
18035      */
18036     minText : "The date in this field must be equal to or after {0}",
18037     /**
18038      * @cfg {String} maxTextf
18039      * The error text to display when the date in the cell is after maxValue (defaults to
18040      * 'The date in this field must be before {maxValue}').
18041      */
18042     maxText : "The date in this field must be equal to or before {0}",
18043     /**
18044      * @cfg {String} invalidText
18045      * The error text to display when the date in the field is invalid (defaults to
18046      * '{value} is not a valid date - it must be in the format {format}').
18047      */
18048     invalidText : "{0} is not a valid date - it must be in the format {1}",
18049     /**
18050      * @cfg {String} triggerClass
18051      * An additional CSS class used to style the trigger button.  The trigger will always get the
18052      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18053      * which displays a calendar icon).
18054      */
18055     triggerClass : 'x-form-date-trigger',
18056     
18057
18058     /**
18059      * @cfg {Boolean} useIso
18060      * if enabled, then the date field will use a hidden field to store the 
18061      * real value as iso formated date. default (true)
18062      */ 
18063     useIso : true,
18064     /**
18065      * @cfg {String/Object} autoCreate
18066      * A DomHelper element spec, or true for a default element spec (defaults to
18067      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18068      */ 
18069     // private
18070     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18071     
18072     // private
18073     hiddenField: false,
18074     
18075     hideMonthPicker : false,
18076     
18077     onRender : function(ct, position)
18078     {
18079         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18080         if (this.useIso) {
18081             this.el.dom.removeAttribute('name'); 
18082             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18083                     'before', true);
18084             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18085             // prevent input submission
18086             this.hiddenName = this.name;
18087         }
18088             
18089             
18090     },
18091     
18092     // private
18093     validateValue : function(value)
18094     {
18095         value = this.formatDate(value);
18096         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18097             return false;
18098         }
18099         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18100              return true;
18101         }
18102         var svalue = value;
18103         value = this.parseDate(value);
18104         if(!value){
18105             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18106             return false;
18107         }
18108         var time = value.getTime();
18109         if(this.minValue && time < this.minValue.getTime()){
18110             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18111             return false;
18112         }
18113         if(this.maxValue && time > this.maxValue.getTime()){
18114             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18115             return false;
18116         }
18117         /*if(this.disabledDays){
18118             var day = value.getDay();
18119             for(var i = 0; i < this.disabledDays.length; i++) {
18120                 if(day === this.disabledDays[i]){
18121                     this.markInvalid(this.disabledDaysText);
18122                     return false;
18123                 }
18124             }
18125         }
18126         */
18127         var fvalue = this.formatDate(value);
18128         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18129             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18130             return false;
18131         }
18132         */
18133         return true;
18134     },
18135
18136     // private
18137     // Provides logic to override the default TriggerField.validateBlur which just returns true
18138     validateBlur : function(){
18139         return !this.menu || !this.menu.isVisible();
18140     },
18141
18142     /**
18143      * Returns the current date value of the date field.
18144      * @return {Date} The date value
18145      */
18146     getValue : function(){
18147         
18148         
18149         
18150         return  this.hiddenField ?
18151                 this.hiddenField.value :
18152                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18153     },
18154
18155     /**
18156      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18157      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18158      * (the default format used is "m/d/y").
18159      * <br />Usage:
18160      * <pre><code>
18161 //All of these calls set the same date value (May 4, 2006)
18162
18163 //Pass a date object:
18164 var dt = new Date('5/4/06');
18165 monthField.setValue(dt);
18166
18167 //Pass a date string (default format):
18168 monthField.setValue('5/4/06');
18169
18170 //Pass a date string (custom format):
18171 monthField.format = 'Y-m-d';
18172 monthField.setValue('2006-5-4');
18173 </code></pre>
18174      * @param {String/Date} date The date or valid date string
18175      */
18176     setValue : function(date){
18177         Roo.log('month setValue' + date);
18178         // can only be first of month..
18179         
18180         var val = this.parseDate(date);
18181         
18182         if (this.hiddenField) {
18183             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18184         }
18185         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18186         this.value = this.parseDate(date);
18187     },
18188
18189     // private
18190     parseDate : function(value){
18191         if(!value || value instanceof Date){
18192             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18193             return value;
18194         }
18195         var v = Date.parseDate(value, this.format);
18196         if (!v && this.useIso) {
18197             v = Date.parseDate(value, 'Y-m-d');
18198         }
18199         if (v) {
18200             // 
18201             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18202         }
18203         
18204         
18205         if(!v && this.altFormats){
18206             if(!this.altFormatsArray){
18207                 this.altFormatsArray = this.altFormats.split("|");
18208             }
18209             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18210                 v = Date.parseDate(value, this.altFormatsArray[i]);
18211             }
18212         }
18213         return v;
18214     },
18215
18216     // private
18217     formatDate : function(date, fmt){
18218         return (!date || !(date instanceof Date)) ?
18219                date : date.dateFormat(fmt || this.format);
18220     },
18221
18222     // private
18223     menuListeners : {
18224         select: function(m, d){
18225             this.setValue(d);
18226             this.fireEvent('select', this, d);
18227         },
18228         show : function(){ // retain focus styling
18229             this.onFocus();
18230         },
18231         hide : function(){
18232             this.focus.defer(10, this);
18233             var ml = this.menuListeners;
18234             this.menu.un("select", ml.select,  this);
18235             this.menu.un("show", ml.show,  this);
18236             this.menu.un("hide", ml.hide,  this);
18237         }
18238     },
18239     // private
18240     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18241     onTriggerClick : function(){
18242         if(this.disabled){
18243             return;
18244         }
18245         if(this.menu == null){
18246             this.menu = new Roo.menu.DateMenu();
18247            
18248         }
18249         
18250         Roo.apply(this.menu.picker,  {
18251             
18252             showClear: this.allowBlank,
18253             minDate : this.minValue,
18254             maxDate : this.maxValue,
18255             disabledDatesRE : this.ddMatch,
18256             disabledDatesText : this.disabledDatesText,
18257             
18258             format : this.useIso ? 'Y-m-d' : this.format,
18259             minText : String.format(this.minText, this.formatDate(this.minValue)),
18260             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18261             
18262         });
18263          this.menu.on(Roo.apply({}, this.menuListeners, {
18264             scope:this
18265         }));
18266        
18267         
18268         var m = this.menu;
18269         var p = m.picker;
18270         
18271         // hide month picker get's called when we called by 'before hide';
18272         
18273         var ignorehide = true;
18274         p.hideMonthPicker  = function(disableAnim){
18275             if (ignorehide) {
18276                 return;
18277             }
18278              if(this.monthPicker){
18279                 Roo.log("hideMonthPicker called");
18280                 if(disableAnim === true){
18281                     this.monthPicker.hide();
18282                 }else{
18283                     this.monthPicker.slideOut('t', {duration:.2});
18284                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18285                     p.fireEvent("select", this, this.value);
18286                     m.hide();
18287                 }
18288             }
18289         }
18290         
18291         Roo.log('picker set value');
18292         Roo.log(this.getValue());
18293         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18294         m.show(this.el, 'tl-bl?');
18295         ignorehide  = false;
18296         // this will trigger hideMonthPicker..
18297         
18298         
18299         // hidden the day picker
18300         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18301         
18302         
18303         
18304       
18305         
18306         p.showMonthPicker.defer(100, p);
18307     
18308         
18309        
18310     },
18311
18312     beforeBlur : function(){
18313         var v = this.parseDate(this.getRawValue());
18314         if(v){
18315             this.setValue(v);
18316         }
18317     }
18318
18319     /** @cfg {Boolean} grow @hide */
18320     /** @cfg {Number} growMin @hide */
18321     /** @cfg {Number} growMax @hide */
18322     /**
18323      * @hide
18324      * @method autoSize
18325      */
18326 });/*
18327  * Based on:
18328  * Ext JS Library 1.1.1
18329  * Copyright(c) 2006-2007, Ext JS, LLC.
18330  *
18331  * Originally Released Under LGPL - original licence link has changed is not relivant.
18332  *
18333  * Fork - LGPL
18334  * <script type="text/javascript">
18335  */
18336  
18337
18338 /**
18339  * @class Roo.form.ComboBox
18340  * @extends Roo.form.TriggerField
18341  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18342  * @constructor
18343  * Create a new ComboBox.
18344  * @param {Object} config Configuration options
18345  */
18346 Roo.form.ComboBox = function(config){
18347     Roo.form.ComboBox.superclass.constructor.call(this, config);
18348     this.addEvents({
18349         /**
18350          * @event expand
18351          * Fires when the dropdown list is expanded
18352              * @param {Roo.form.ComboBox} combo This combo box
18353              */
18354         'expand' : true,
18355         /**
18356          * @event collapse
18357          * Fires when the dropdown list is collapsed
18358              * @param {Roo.form.ComboBox} combo This combo box
18359              */
18360         'collapse' : true,
18361         /**
18362          * @event beforeselect
18363          * Fires before a list item is selected. Return false to cancel the selection.
18364              * @param {Roo.form.ComboBox} combo This combo box
18365              * @param {Roo.data.Record} record The data record returned from the underlying store
18366              * @param {Number} index The index of the selected item in the dropdown list
18367              */
18368         'beforeselect' : true,
18369         /**
18370          * @event select
18371          * Fires when a list item is selected
18372              * @param {Roo.form.ComboBox} combo This combo box
18373              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18374              * @param {Number} index The index of the selected item in the dropdown list
18375              */
18376         'select' : true,
18377         /**
18378          * @event beforequery
18379          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18380          * The event object passed has these properties:
18381              * @param {Roo.form.ComboBox} combo This combo box
18382              * @param {String} query The query
18383              * @param {Boolean} forceAll true to force "all" query
18384              * @param {Boolean} cancel true to cancel the query
18385              * @param {Object} e The query event object
18386              */
18387         'beforequery': true,
18388          /**
18389          * @event add
18390          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18391              * @param {Roo.form.ComboBox} combo This combo box
18392              */
18393         'add' : true,
18394         /**
18395          * @event edit
18396          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18397              * @param {Roo.form.ComboBox} combo This combo box
18398              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18399              */
18400         'edit' : true
18401         
18402         
18403     });
18404     if(this.transform){
18405         this.allowDomMove = false;
18406         var s = Roo.getDom(this.transform);
18407         if(!this.hiddenName){
18408             this.hiddenName = s.name;
18409         }
18410         if(!this.store){
18411             this.mode = 'local';
18412             var d = [], opts = s.options;
18413             for(var i = 0, len = opts.length;i < len; i++){
18414                 var o = opts[i];
18415                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18416                 if(o.selected) {
18417                     this.value = value;
18418                 }
18419                 d.push([value, o.text]);
18420             }
18421             this.store = new Roo.data.SimpleStore({
18422                 'id': 0,
18423                 fields: ['value', 'text'],
18424                 data : d
18425             });
18426             this.valueField = 'value';
18427             this.displayField = 'text';
18428         }
18429         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18430         if(!this.lazyRender){
18431             this.target = true;
18432             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18433             s.parentNode.removeChild(s); // remove it
18434             this.render(this.el.parentNode);
18435         }else{
18436             s.parentNode.removeChild(s); // remove it
18437         }
18438
18439     }
18440     if (this.store) {
18441         this.store = Roo.factory(this.store, Roo.data);
18442     }
18443     
18444     this.selectedIndex = -1;
18445     if(this.mode == 'local'){
18446         if(config.queryDelay === undefined){
18447             this.queryDelay = 10;
18448         }
18449         if(config.minChars === undefined){
18450             this.minChars = 0;
18451         }
18452     }
18453 };
18454
18455 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18456     /**
18457      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18458      */
18459     /**
18460      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18461      * rendering into an Roo.Editor, defaults to false)
18462      */
18463     /**
18464      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18465      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18466      */
18467     /**
18468      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18469      */
18470     /**
18471      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18472      * the dropdown list (defaults to undefined, with no header element)
18473      */
18474
18475      /**
18476      * @cfg {String/Roo.Template} tpl The template to use to render the output
18477      */
18478      
18479     // private
18480     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18481     /**
18482      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18483      */
18484     listWidth: undefined,
18485     /**
18486      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18487      * mode = 'remote' or 'text' if mode = 'local')
18488      */
18489     displayField: undefined,
18490     /**
18491      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18492      * mode = 'remote' or 'value' if mode = 'local'). 
18493      * Note: use of a valueField requires the user make a selection
18494      * in order for a value to be mapped.
18495      */
18496     valueField: undefined,
18497     
18498     
18499     /**
18500      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18501      * field's data value (defaults to the underlying DOM element's name)
18502      */
18503     hiddenName: undefined,
18504     /**
18505      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18506      */
18507     listClass: '',
18508     /**
18509      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18510      */
18511     selectedClass: 'x-combo-selected',
18512     /**
18513      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18514      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18515      * which displays a downward arrow icon).
18516      */
18517     triggerClass : 'x-form-arrow-trigger',
18518     /**
18519      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18520      */
18521     shadow:'sides',
18522     /**
18523      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18524      * anchor positions (defaults to 'tl-bl')
18525      */
18526     listAlign: 'tl-bl?',
18527     /**
18528      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18529      */
18530     maxHeight: 300,
18531     /**
18532      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18533      * query specified by the allQuery config option (defaults to 'query')
18534      */
18535     triggerAction: 'query',
18536     /**
18537      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18538      * (defaults to 4, does not apply if editable = false)
18539      */
18540     minChars : 4,
18541     /**
18542      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18543      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18544      */
18545     typeAhead: false,
18546     /**
18547      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18548      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18549      */
18550     queryDelay: 500,
18551     /**
18552      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18553      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18554      */
18555     pageSize: 0,
18556     /**
18557      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18558      * when editable = true (defaults to false)
18559      */
18560     selectOnFocus:false,
18561     /**
18562      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18563      */
18564     queryParam: 'query',
18565     /**
18566      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18567      * when mode = 'remote' (defaults to 'Loading...')
18568      */
18569     loadingText: 'Loading...',
18570     /**
18571      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18572      */
18573     resizable: false,
18574     /**
18575      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18576      */
18577     handleHeight : 8,
18578     /**
18579      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18580      * traditional select (defaults to true)
18581      */
18582     editable: true,
18583     /**
18584      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18585      */
18586     allQuery: '',
18587     /**
18588      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18589      */
18590     mode: 'remote',
18591     /**
18592      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18593      * listWidth has a higher value)
18594      */
18595     minListWidth : 70,
18596     /**
18597      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18598      * allow the user to set arbitrary text into the field (defaults to false)
18599      */
18600     forceSelection:false,
18601     /**
18602      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18603      * if typeAhead = true (defaults to 250)
18604      */
18605     typeAheadDelay : 250,
18606     /**
18607      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18608      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18609      */
18610     valueNotFoundText : undefined,
18611     /**
18612      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18613      */
18614     blockFocus : false,
18615     
18616     /**
18617      * @cfg {Boolean} disableClear Disable showing of clear button.
18618      */
18619     disableClear : false,
18620     /**
18621      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18622      */
18623     alwaysQuery : false,
18624     
18625     //private
18626     addicon : false,
18627     editicon: false,
18628     
18629     // element that contains real text value.. (when hidden is used..)
18630      
18631     // private
18632     onRender : function(ct, position)
18633     {
18634         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18635         
18636         if(this.hiddenName){
18637             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18638                     'before', true);
18639             this.hiddenField.value =
18640                 this.hiddenValue !== undefined ? this.hiddenValue :
18641                 this.value !== undefined ? this.value : '';
18642
18643             // prevent input submission
18644             this.el.dom.removeAttribute('name');
18645              
18646              
18647         }
18648         
18649         if(Roo.isGecko){
18650             this.el.dom.setAttribute('autocomplete', 'off');
18651         }
18652
18653         var cls = 'x-combo-list';
18654
18655         this.list = new Roo.Layer({
18656             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18657         });
18658
18659         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18660         this.list.setWidth(lw);
18661         this.list.swallowEvent('mousewheel');
18662         this.assetHeight = 0;
18663
18664         if(this.title){
18665             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18666             this.assetHeight += this.header.getHeight();
18667         }
18668
18669         this.innerList = this.list.createChild({cls:cls+'-inner'});
18670         this.innerList.on('mouseover', this.onViewOver, this);
18671         this.innerList.on('mousemove', this.onViewMove, this);
18672         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18673         
18674         if(this.allowBlank && !this.pageSize && !this.disableClear){
18675             this.footer = this.list.createChild({cls:cls+'-ft'});
18676             this.pageTb = new Roo.Toolbar(this.footer);
18677            
18678         }
18679         if(this.pageSize){
18680             this.footer = this.list.createChild({cls:cls+'-ft'});
18681             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18682                     {pageSize: this.pageSize});
18683             
18684         }
18685         
18686         if (this.pageTb && this.allowBlank && !this.disableClear) {
18687             var _this = this;
18688             this.pageTb.add(new Roo.Toolbar.Fill(), {
18689                 cls: 'x-btn-icon x-btn-clear',
18690                 text: '&#160;',
18691                 handler: function()
18692                 {
18693                     _this.collapse();
18694                     _this.clearValue();
18695                     _this.onSelect(false, -1);
18696                 }
18697             });
18698         }
18699         if (this.footer) {
18700             this.assetHeight += this.footer.getHeight();
18701         }
18702         
18703
18704         if(!this.tpl){
18705             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18706         }
18707
18708         this.view = new Roo.View(this.innerList, this.tpl, {
18709             singleSelect:true,
18710             store: this.store,
18711             selectedClass: this.selectedClass
18712         });
18713
18714         this.view.on('click', this.onViewClick, this);
18715
18716         this.store.on('beforeload', this.onBeforeLoad, this);
18717         this.store.on('load', this.onLoad, this);
18718         this.store.on('loadexception', this.onLoadException, this);
18719
18720         if(this.resizable){
18721             this.resizer = new Roo.Resizable(this.list,  {
18722                pinned:true, handles:'se'
18723             });
18724             this.resizer.on('resize', function(r, w, h){
18725                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18726                 this.listWidth = w;
18727                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18728                 this.restrictHeight();
18729             }, this);
18730             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18731         }
18732         if(!this.editable){
18733             this.editable = true;
18734             this.setEditable(false);
18735         }  
18736         
18737         
18738         if (typeof(this.events.add.listeners) != 'undefined') {
18739             
18740             this.addicon = this.wrap.createChild(
18741                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18742        
18743             this.addicon.on('click', function(e) {
18744                 this.fireEvent('add', this);
18745             }, this);
18746         }
18747         if (typeof(this.events.edit.listeners) != 'undefined') {
18748             
18749             this.editicon = this.wrap.createChild(
18750                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18751             if (this.addicon) {
18752                 this.editicon.setStyle('margin-left', '40px');
18753             }
18754             this.editicon.on('click', function(e) {
18755                 
18756                 // we fire even  if inothing is selected..
18757                 this.fireEvent('edit', this, this.lastData );
18758                 
18759             }, this);
18760         }
18761         
18762         
18763         
18764     },
18765
18766     // private
18767     initEvents : function(){
18768         Roo.form.ComboBox.superclass.initEvents.call(this);
18769
18770         this.keyNav = new Roo.KeyNav(this.el, {
18771             "up" : function(e){
18772                 this.inKeyMode = true;
18773                 this.selectPrev();
18774             },
18775
18776             "down" : function(e){
18777                 if(!this.isExpanded()){
18778                     this.onTriggerClick();
18779                 }else{
18780                     this.inKeyMode = true;
18781                     this.selectNext();
18782                 }
18783             },
18784
18785             "enter" : function(e){
18786                 this.onViewClick();
18787                 //return true;
18788             },
18789
18790             "esc" : function(e){
18791                 this.collapse();
18792             },
18793
18794             "tab" : function(e){
18795                 this.onViewClick(false);
18796                 this.fireEvent("specialkey", this, e);
18797                 return true;
18798             },
18799
18800             scope : this,
18801
18802             doRelay : function(foo, bar, hname){
18803                 if(hname == 'down' || this.scope.isExpanded()){
18804                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18805                 }
18806                 return true;
18807             },
18808
18809             forceKeyDown: true
18810         });
18811         this.queryDelay = Math.max(this.queryDelay || 10,
18812                 this.mode == 'local' ? 10 : 250);
18813         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18814         if(this.typeAhead){
18815             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18816         }
18817         if(this.editable !== false){
18818             this.el.on("keyup", this.onKeyUp, this);
18819         }
18820         if(this.forceSelection){
18821             this.on('blur', this.doForce, this);
18822         }
18823     },
18824
18825     onDestroy : function(){
18826         if(this.view){
18827             this.view.setStore(null);
18828             this.view.el.removeAllListeners();
18829             this.view.el.remove();
18830             this.view.purgeListeners();
18831         }
18832         if(this.list){
18833             this.list.destroy();
18834         }
18835         if(this.store){
18836             this.store.un('beforeload', this.onBeforeLoad, this);
18837             this.store.un('load', this.onLoad, this);
18838             this.store.un('loadexception', this.onLoadException, this);
18839         }
18840         Roo.form.ComboBox.superclass.onDestroy.call(this);
18841     },
18842
18843     // private
18844     fireKey : function(e){
18845         if(e.isNavKeyPress() && !this.list.isVisible()){
18846             this.fireEvent("specialkey", this, e);
18847         }
18848     },
18849
18850     // private
18851     onResize: function(w, h){
18852         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18853         
18854         if(typeof w != 'number'){
18855             // we do not handle it!?!?
18856             return;
18857         }
18858         var tw = this.trigger.getWidth();
18859         tw += this.addicon ? this.addicon.getWidth() : 0;
18860         tw += this.editicon ? this.editicon.getWidth() : 0;
18861         var x = w - tw;
18862         this.el.setWidth( this.adjustWidth('input', x));
18863             
18864         this.trigger.setStyle('left', x+'px');
18865         
18866         if(this.list && this.listWidth === undefined){
18867             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18868             this.list.setWidth(lw);
18869             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18870         }
18871         
18872     
18873         
18874     },
18875
18876     /**
18877      * Allow or prevent the user from directly editing the field text.  If false is passed,
18878      * the user will only be able to select from the items defined in the dropdown list.  This method
18879      * is the runtime equivalent of setting the 'editable' config option at config time.
18880      * @param {Boolean} value True to allow the user to directly edit the field text
18881      */
18882     setEditable : function(value){
18883         if(value == this.editable){
18884             return;
18885         }
18886         this.editable = value;
18887         if(!value){
18888             this.el.dom.setAttribute('readOnly', true);
18889             this.el.on('mousedown', this.onTriggerClick,  this);
18890             this.el.addClass('x-combo-noedit');
18891         }else{
18892             this.el.dom.setAttribute('readOnly', false);
18893             this.el.un('mousedown', this.onTriggerClick,  this);
18894             this.el.removeClass('x-combo-noedit');
18895         }
18896     },
18897
18898     // private
18899     onBeforeLoad : function(){
18900         if(!this.hasFocus){
18901             return;
18902         }
18903         this.innerList.update(this.loadingText ?
18904                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18905         this.restrictHeight();
18906         this.selectedIndex = -1;
18907     },
18908
18909     // private
18910     onLoad : function(){
18911         if(!this.hasFocus){
18912             return;
18913         }
18914         if(this.store.getCount() > 0){
18915             this.expand();
18916             this.restrictHeight();
18917             if(this.lastQuery == this.allQuery){
18918                 if(this.editable){
18919                     this.el.dom.select();
18920                 }
18921                 if(!this.selectByValue(this.value, true)){
18922                     this.select(0, true);
18923                 }
18924             }else{
18925                 this.selectNext();
18926                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18927                     this.taTask.delay(this.typeAheadDelay);
18928                 }
18929             }
18930         }else{
18931             this.onEmptyResults();
18932         }
18933         //this.el.focus();
18934     },
18935     // private
18936     onLoadException : function()
18937     {
18938         this.collapse();
18939         Roo.log(this.store.reader.jsonData);
18940         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18941             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18942         }
18943         
18944         
18945     },
18946     // private
18947     onTypeAhead : function(){
18948         if(this.store.getCount() > 0){
18949             var r = this.store.getAt(0);
18950             var newValue = r.data[this.displayField];
18951             var len = newValue.length;
18952             var selStart = this.getRawValue().length;
18953             if(selStart != len){
18954                 this.setRawValue(newValue);
18955                 this.selectText(selStart, newValue.length);
18956             }
18957         }
18958     },
18959
18960     // private
18961     onSelect : function(record, index){
18962         if(this.fireEvent('beforeselect', this, record, index) !== false){
18963             this.setFromData(index > -1 ? record.data : false);
18964             this.collapse();
18965             this.fireEvent('select', this, record, index);
18966         }
18967     },
18968
18969     /**
18970      * Returns the currently selected field value or empty string if no value is set.
18971      * @return {String} value The selected value
18972      */
18973     getValue : function(){
18974         if(this.valueField){
18975             return typeof this.value != 'undefined' ? this.value : '';
18976         }
18977         return Roo.form.ComboBox.superclass.getValue.call(this);
18978     },
18979
18980     /**
18981      * Clears any text/value currently set in the field
18982      */
18983     clearValue : function(){
18984         if(this.hiddenField){
18985             this.hiddenField.value = '';
18986         }
18987         this.value = '';
18988         this.setRawValue('');
18989         this.lastSelectionText = '';
18990         
18991     },
18992
18993     /**
18994      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18995      * will be displayed in the field.  If the value does not match the data value of an existing item,
18996      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18997      * Otherwise the field will be blank (although the value will still be set).
18998      * @param {String} value The value to match
18999      */
19000     setValue : function(v){
19001         var text = v;
19002         if(this.valueField){
19003             var r = this.findRecord(this.valueField, v);
19004             if(r){
19005                 text = r.data[this.displayField];
19006             }else if(this.valueNotFoundText !== undefined){
19007                 text = this.valueNotFoundText;
19008             }
19009         }
19010         this.lastSelectionText = text;
19011         if(this.hiddenField){
19012             this.hiddenField.value = v;
19013         }
19014         Roo.form.ComboBox.superclass.setValue.call(this, text);
19015         this.value = v;
19016     },
19017     /**
19018      * @property {Object} the last set data for the element
19019      */
19020     
19021     lastData : false,
19022     /**
19023      * Sets the value of the field based on a object which is related to the record format for the store.
19024      * @param {Object} value the value to set as. or false on reset?
19025      */
19026     setFromData : function(o){
19027         var dv = ''; // display value
19028         var vv = ''; // value value..
19029         this.lastData = o;
19030         if (this.displayField) {
19031             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19032         } else {
19033             // this is an error condition!!!
19034             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19035         }
19036         
19037         if(this.valueField){
19038             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19039         }
19040         if(this.hiddenField){
19041             this.hiddenField.value = vv;
19042             
19043             this.lastSelectionText = dv;
19044             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19045             this.value = vv;
19046             return;
19047         }
19048         // no hidden field.. - we store the value in 'value', but still display
19049         // display field!!!!
19050         this.lastSelectionText = dv;
19051         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19052         this.value = vv;
19053         
19054         
19055     },
19056     // private
19057     reset : function(){
19058         // overridden so that last data is reset..
19059         this.setValue(this.resetValue);
19060         this.originalValue = this.getValue();
19061         this.clearInvalid();
19062         this.lastData = false;
19063         if (this.view) {
19064             this.view.clearSelections();
19065         }
19066     },
19067     // private
19068     findRecord : function(prop, value){
19069         var record;
19070         if(this.store.getCount() > 0){
19071             this.store.each(function(r){
19072                 if(r.data[prop] == value){
19073                     record = r;
19074                     return false;
19075                 }
19076                 return true;
19077             });
19078         }
19079         return record;
19080     },
19081     
19082     getName: function()
19083     {
19084         // returns hidden if it's set..
19085         if (!this.rendered) {return ''};
19086         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19087         
19088     },
19089     // private
19090     onViewMove : function(e, t){
19091         this.inKeyMode = false;
19092     },
19093
19094     // private
19095     onViewOver : function(e, t){
19096         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19097             return;
19098         }
19099         var item = this.view.findItemFromChild(t);
19100         if(item){
19101             var index = this.view.indexOf(item);
19102             this.select(index, false);
19103         }
19104     },
19105
19106     // private
19107     onViewClick : function(doFocus)
19108     {
19109         var index = this.view.getSelectedIndexes()[0];
19110         var r = this.store.getAt(index);
19111         if(r){
19112             this.onSelect(r, index);
19113         }
19114         if(doFocus !== false && !this.blockFocus){
19115             this.el.focus();
19116         }
19117     },
19118
19119     // private
19120     restrictHeight : function(){
19121         this.innerList.dom.style.height = '';
19122         var inner = this.innerList.dom;
19123         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19124         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19125         this.list.beginUpdate();
19126         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19127         this.list.alignTo(this.el, this.listAlign);
19128         this.list.endUpdate();
19129     },
19130
19131     // private
19132     onEmptyResults : function(){
19133         this.collapse();
19134     },
19135
19136     /**
19137      * Returns true if the dropdown list is expanded, else false.
19138      */
19139     isExpanded : function(){
19140         return this.list.isVisible();
19141     },
19142
19143     /**
19144      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19145      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19146      * @param {String} value The data value of the item to select
19147      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19148      * selected item if it is not currently in view (defaults to true)
19149      * @return {Boolean} True if the value matched an item in the list, else false
19150      */
19151     selectByValue : function(v, scrollIntoView){
19152         if(v !== undefined && v !== null){
19153             var r = this.findRecord(this.valueField || this.displayField, v);
19154             if(r){
19155                 this.select(this.store.indexOf(r), scrollIntoView);
19156                 return true;
19157             }
19158         }
19159         return false;
19160     },
19161
19162     /**
19163      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19164      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19165      * @param {Number} index The zero-based index of the list item to select
19166      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19167      * selected item if it is not currently in view (defaults to true)
19168      */
19169     select : function(index, scrollIntoView){
19170         this.selectedIndex = index;
19171         this.view.select(index);
19172         if(scrollIntoView !== false){
19173             var el = this.view.getNode(index);
19174             if(el){
19175                 this.innerList.scrollChildIntoView(el, false);
19176             }
19177         }
19178     },
19179
19180     // private
19181     selectNext : function(){
19182         var ct = this.store.getCount();
19183         if(ct > 0){
19184             if(this.selectedIndex == -1){
19185                 this.select(0);
19186             }else if(this.selectedIndex < ct-1){
19187                 this.select(this.selectedIndex+1);
19188             }
19189         }
19190     },
19191
19192     // private
19193     selectPrev : function(){
19194         var ct = this.store.getCount();
19195         if(ct > 0){
19196             if(this.selectedIndex == -1){
19197                 this.select(0);
19198             }else if(this.selectedIndex != 0){
19199                 this.select(this.selectedIndex-1);
19200             }
19201         }
19202     },
19203
19204     // private
19205     onKeyUp : function(e){
19206         if(this.editable !== false && !e.isSpecialKey()){
19207             this.lastKey = e.getKey();
19208             this.dqTask.delay(this.queryDelay);
19209         }
19210     },
19211
19212     // private
19213     validateBlur : function(){
19214         return !this.list || !this.list.isVisible();   
19215     },
19216
19217     // private
19218     initQuery : function(){
19219         this.doQuery(this.getRawValue());
19220     },
19221
19222     // private
19223     doForce : function(){
19224         if(this.el.dom.value.length > 0){
19225             this.el.dom.value =
19226                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19227              
19228         }
19229     },
19230
19231     /**
19232      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19233      * query allowing the query action to be canceled if needed.
19234      * @param {String} query The SQL query to execute
19235      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19236      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19237      * saved in the current store (defaults to false)
19238      */
19239     doQuery : function(q, forceAll){
19240         if(q === undefined || q === null){
19241             q = '';
19242         }
19243         var qe = {
19244             query: q,
19245             forceAll: forceAll,
19246             combo: this,
19247             cancel:false
19248         };
19249         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19250             return false;
19251         }
19252         q = qe.query;
19253         forceAll = qe.forceAll;
19254         if(forceAll === true || (q.length >= this.minChars)){
19255             if(this.lastQuery != q || this.alwaysQuery){
19256                 this.lastQuery = q;
19257                 if(this.mode == 'local'){
19258                     this.selectedIndex = -1;
19259                     if(forceAll){
19260                         this.store.clearFilter();
19261                     }else{
19262                         this.store.filter(this.displayField, q);
19263                     }
19264                     this.onLoad();
19265                 }else{
19266                     this.store.baseParams[this.queryParam] = q;
19267                     this.store.load({
19268                         params: this.getParams(q)
19269                     });
19270                     this.expand();
19271                 }
19272             }else{
19273                 this.selectedIndex = -1;
19274                 this.onLoad();   
19275             }
19276         }
19277     },
19278
19279     // private
19280     getParams : function(q){
19281         var p = {};
19282         //p[this.queryParam] = q;
19283         if(this.pageSize){
19284             p.start = 0;
19285             p.limit = this.pageSize;
19286         }
19287         return p;
19288     },
19289
19290     /**
19291      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19292      */
19293     collapse : function(){
19294         if(!this.isExpanded()){
19295             return;
19296         }
19297         this.list.hide();
19298         Roo.get(document).un('mousedown', this.collapseIf, this);
19299         Roo.get(document).un('mousewheel', this.collapseIf, this);
19300         if (!this.editable) {
19301             Roo.get(document).un('keydown', this.listKeyPress, this);
19302         }
19303         this.fireEvent('collapse', this);
19304     },
19305
19306     // private
19307     collapseIf : function(e){
19308         if(!e.within(this.wrap) && !e.within(this.list)){
19309             this.collapse();
19310         }
19311     },
19312
19313     /**
19314      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19315      */
19316     expand : function(){
19317         if(this.isExpanded() || !this.hasFocus){
19318             return;
19319         }
19320         this.list.alignTo(this.el, this.listAlign);
19321         this.list.show();
19322         Roo.get(document).on('mousedown', this.collapseIf, this);
19323         Roo.get(document).on('mousewheel', this.collapseIf, this);
19324         if (!this.editable) {
19325             Roo.get(document).on('keydown', this.listKeyPress, this);
19326         }
19327         
19328         this.fireEvent('expand', this);
19329     },
19330
19331     // private
19332     // Implements the default empty TriggerField.onTriggerClick function
19333     onTriggerClick : function(){
19334         if(this.disabled){
19335             return;
19336         }
19337         if(this.isExpanded()){
19338             this.collapse();
19339             if (!this.blockFocus) {
19340                 this.el.focus();
19341             }
19342             
19343         }else {
19344             this.hasFocus = true;
19345             if(this.triggerAction == 'all') {
19346                 this.doQuery(this.allQuery, true);
19347             } else {
19348                 this.doQuery(this.getRawValue());
19349             }
19350             if (!this.blockFocus) {
19351                 this.el.focus();
19352             }
19353         }
19354     },
19355     listKeyPress : function(e)
19356     {
19357         //Roo.log('listkeypress');
19358         // scroll to first matching element based on key pres..
19359         if (e.isSpecialKey()) {
19360             return false;
19361         }
19362         var k = String.fromCharCode(e.getKey()).toUpperCase();
19363         //Roo.log(k);
19364         var match  = false;
19365         var csel = this.view.getSelectedNodes();
19366         var cselitem = false;
19367         if (csel.length) {
19368             var ix = this.view.indexOf(csel[0]);
19369             cselitem  = this.store.getAt(ix);
19370             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19371                 cselitem = false;
19372             }
19373             
19374         }
19375         
19376         this.store.each(function(v) { 
19377             if (cselitem) {
19378                 // start at existing selection.
19379                 if (cselitem.id == v.id) {
19380                     cselitem = false;
19381                 }
19382                 return;
19383             }
19384                 
19385             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19386                 match = this.store.indexOf(v);
19387                 return false;
19388             }
19389         }, this);
19390         
19391         if (match === false) {
19392             return true; // no more action?
19393         }
19394         // scroll to?
19395         this.view.select(match);
19396         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19397         sn.scrollIntoView(sn.dom.parentNode, false);
19398     } 
19399
19400     /** 
19401     * @cfg {Boolean} grow 
19402     * @hide 
19403     */
19404     /** 
19405     * @cfg {Number} growMin 
19406     * @hide 
19407     */
19408     /** 
19409     * @cfg {Number} growMax 
19410     * @hide 
19411     */
19412     /**
19413      * @hide
19414      * @method autoSize
19415      */
19416 });/*
19417  * Copyright(c) 2010-2012, Roo J Solutions Limited
19418  *
19419  * Licence LGPL
19420  *
19421  */
19422
19423 /**
19424  * @class Roo.form.ComboBoxArray
19425  * @extends Roo.form.TextField
19426  * A facebook style adder... for lists of email / people / countries  etc...
19427  * pick multiple items from a combo box, and shows each one.
19428  *
19429  *  Fred [x]  Brian [x]  [Pick another |v]
19430  *
19431  *
19432  *  For this to work: it needs various extra information
19433  *    - normal combo problay has
19434  *      name, hiddenName
19435  *    + displayField, valueField
19436  *
19437  *    For our purpose...
19438  *
19439  *
19440  *   If we change from 'extends' to wrapping...
19441  *   
19442  *  
19443  *
19444  
19445  
19446  * @constructor
19447  * Create a new ComboBoxArray.
19448  * @param {Object} config Configuration options
19449  */
19450  
19451
19452 Roo.form.ComboBoxArray = function(config)
19453 {
19454     this.addEvents({
19455         /**
19456          * @event beforeremove
19457          * Fires before remove the value from the list
19458              * @param {Roo.form.ComboBoxArray} _self This combo box array
19459              * @param {Roo.form.ComboBoxArray.Item} item removed item
19460              */
19461         'beforeremove' : true,
19462         /**
19463          * @event remove
19464          * Fires when remove the value from the list
19465              * @param {Roo.form.ComboBoxArray} _self This combo box array
19466              * @param {Roo.form.ComboBoxArray.Item} item removed item
19467              */
19468         'remove' : true
19469         
19470         
19471     });
19472     
19473     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19474     
19475     this.items = new Roo.util.MixedCollection(false);
19476     
19477     // construct the child combo...
19478     
19479     
19480     
19481     
19482    
19483     
19484 }
19485
19486  
19487 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19488
19489     /**
19490      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19491      */
19492     
19493     lastData : false,
19494     
19495     // behavies liek a hiddne field
19496     inputType:      'hidden',
19497     /**
19498      * @cfg {Number} width The width of the box that displays the selected element
19499      */ 
19500     width:          300,
19501
19502     
19503     
19504     /**
19505      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19506      */
19507     name : false,
19508     /**
19509      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19510      */
19511     hiddenName : false,
19512       /**
19513      * @cfg {String} seperator    The value seperator normally ',' 
19514      */
19515     seperator : ',',
19516     
19517     // private the array of items that are displayed..
19518     items  : false,
19519     // private - the hidden field el.
19520     hiddenEl : false,
19521     // private - the filed el..
19522     el : false,
19523     
19524     //validateValue : function() { return true; }, // all values are ok!
19525     //onAddClick: function() { },
19526     
19527     onRender : function(ct, position) 
19528     {
19529         
19530         // create the standard hidden element
19531         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19532         
19533         
19534         // give fake names to child combo;
19535         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19536         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19537         
19538         this.combo = Roo.factory(this.combo, Roo.form);
19539         this.combo.onRender(ct, position);
19540         if (typeof(this.combo.width) != 'undefined') {
19541             this.combo.onResize(this.combo.width,0);
19542         }
19543         
19544         this.combo.initEvents();
19545         
19546         // assigned so form know we need to do this..
19547         this.store          = this.combo.store;
19548         this.valueField     = this.combo.valueField;
19549         this.displayField   = this.combo.displayField ;
19550         
19551         
19552         this.combo.wrap.addClass('x-cbarray-grp');
19553         
19554         var cbwrap = this.combo.wrap.createChild(
19555             {tag: 'div', cls: 'x-cbarray-cb'},
19556             this.combo.el.dom
19557         );
19558         
19559              
19560         this.hiddenEl = this.combo.wrap.createChild({
19561             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19562         });
19563         this.el = this.combo.wrap.createChild({
19564             tag: 'input',  type:'hidden' , name: this.name, value : ''
19565         });
19566          //   this.el.dom.removeAttribute("name");
19567         
19568         
19569         this.outerWrap = this.combo.wrap;
19570         this.wrap = cbwrap;
19571         
19572         this.outerWrap.setWidth(this.width);
19573         this.outerWrap.dom.removeChild(this.el.dom);
19574         
19575         this.wrap.dom.appendChild(this.el.dom);
19576         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19577         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19578         
19579         this.combo.trigger.setStyle('position','relative');
19580         this.combo.trigger.setStyle('left', '0px');
19581         this.combo.trigger.setStyle('top', '2px');
19582         
19583         this.combo.el.setStyle('vertical-align', 'text-bottom');
19584         
19585         //this.trigger.setStyle('vertical-align', 'top');
19586         
19587         // this should use the code from combo really... on('add' ....)
19588         if (this.adder) {
19589             
19590         
19591             this.adder = this.outerWrap.createChild(
19592                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19593             var _t = this;
19594             this.adder.on('click', function(e) {
19595                 _t.fireEvent('adderclick', this, e);
19596             }, _t);
19597         }
19598         //var _t = this;
19599         //this.adder.on('click', this.onAddClick, _t);
19600         
19601         
19602         this.combo.on('select', function(cb, rec, ix) {
19603             this.addItem(rec.data);
19604             
19605             cb.setValue('');
19606             cb.el.dom.value = '';
19607             //cb.lastData = rec.data;
19608             // add to list
19609             
19610         }, this);
19611         
19612         
19613     },
19614     
19615     
19616     getName: function()
19617     {
19618         // returns hidden if it's set..
19619         if (!this.rendered) {return ''};
19620         return  this.hiddenName ? this.hiddenName : this.name;
19621         
19622     },
19623     
19624     
19625     onResize: function(w, h){
19626         
19627         return;
19628         // not sure if this is needed..
19629         //this.combo.onResize(w,h);
19630         
19631         if(typeof w != 'number'){
19632             // we do not handle it!?!?
19633             return;
19634         }
19635         var tw = this.combo.trigger.getWidth();
19636         tw += this.addicon ? this.addicon.getWidth() : 0;
19637         tw += this.editicon ? this.editicon.getWidth() : 0;
19638         var x = w - tw;
19639         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19640             
19641         this.combo.trigger.setStyle('left', '0px');
19642         
19643         if(this.list && this.listWidth === undefined){
19644             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19645             this.list.setWidth(lw);
19646             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19647         }
19648         
19649     
19650         
19651     },
19652     
19653     addItem: function(rec)
19654     {
19655         var valueField = this.combo.valueField;
19656         var displayField = this.combo.displayField;
19657         
19658         if (this.items.indexOfKey(rec[valueField]) > -1) {
19659             //console.log("GOT " + rec.data.id);
19660             return;
19661         }
19662         
19663         var x = new Roo.form.ComboBoxArray.Item({
19664             //id : rec[this.idField],
19665             data : rec,
19666             displayField : displayField ,
19667             tipField : displayField ,
19668             cb : this
19669         });
19670         // use the 
19671         this.items.add(rec[valueField],x);
19672         // add it before the element..
19673         this.updateHiddenEl();
19674         x.render(this.outerWrap, this.wrap.dom);
19675         // add the image handler..
19676     },
19677     
19678     updateHiddenEl : function()
19679     {
19680         this.validate();
19681         if (!this.hiddenEl) {
19682             return;
19683         }
19684         var ar = [];
19685         var idField = this.combo.valueField;
19686         
19687         this.items.each(function(f) {
19688             ar.push(f.data[idField]);
19689         });
19690         this.hiddenEl.dom.value = ar.join(this.seperator);
19691         this.validate();
19692     },
19693     
19694     reset : function()
19695     {
19696         this.items.clear();
19697         
19698         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19699            el.remove();
19700         });
19701         
19702         this.el.dom.value = '';
19703         if (this.hiddenEl) {
19704             this.hiddenEl.dom.value = '';
19705         }
19706         
19707     },
19708     getValue: function()
19709     {
19710         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19711     },
19712     setValue: function(v) // not a valid action - must use addItems..
19713     {
19714         
19715         this.reset();
19716          
19717         if (this.store.isLocal && (typeof(v) == 'string')) {
19718             // then we can use the store to find the values..
19719             // comma seperated at present.. this needs to allow JSON based encoding..
19720             this.hiddenEl.value  = v;
19721             var v_ar = [];
19722             Roo.each(v.split(this.seperator), function(k) {
19723                 Roo.log("CHECK " + this.valueField + ',' + k);
19724                 var li = this.store.query(this.valueField, k);
19725                 if (!li.length) {
19726                     return;
19727                 }
19728                 var add = {};
19729                 add[this.valueField] = k;
19730                 add[this.displayField] = li.item(0).data[this.displayField];
19731                 
19732                 this.addItem(add);
19733             }, this) 
19734              
19735         }
19736         if (typeof(v) == 'object' ) {
19737             // then let's assume it's an array of objects..
19738             Roo.each(v, function(l) {
19739                 var add = l;
19740                 if (typeof(l) == 'string') {
19741                     add = {};
19742                     add[this.valueField] = l;
19743                     add[this.displayField] = l
19744                 }
19745                 this.addItem(add);
19746             }, this);
19747              
19748         }
19749         
19750         
19751     },
19752     setFromData: function(v)
19753     {
19754         // this recieves an object, if setValues is called.
19755         this.reset();
19756         this.el.dom.value = v[this.displayField];
19757         this.hiddenEl.dom.value = v[this.valueField];
19758         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19759             return;
19760         }
19761         var kv = v[this.valueField];
19762         var dv = v[this.displayField];
19763         kv = typeof(kv) != 'string' ? '' : kv;
19764         dv = typeof(dv) != 'string' ? '' : dv;
19765         
19766         
19767         var keys = kv.split(this.seperator);
19768         var display = dv.split(this.seperator);
19769         for (var i = 0 ; i < keys.length; i++) {
19770             add = {};
19771             add[this.valueField] = keys[i];
19772             add[this.displayField] = display[i];
19773             this.addItem(add);
19774         }
19775       
19776         
19777     },
19778     
19779     /**
19780      * Validates the combox array value
19781      * @return {Boolean} True if the value is valid, else false
19782      */
19783     validate : function(){
19784         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19785             this.clearInvalid();
19786             return true;
19787         }
19788         return false;
19789     },
19790     
19791     validateValue : function(value){
19792         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19793         
19794     },
19795     
19796     /*@
19797      * overide
19798      * 
19799      */
19800     isDirty : function() {
19801         if(this.disabled) {
19802             return false;
19803         }
19804         
19805         try {
19806             var d = Roo.decode(String(this.originalValue));
19807         } catch (e) {
19808             return String(this.getValue()) !== String(this.originalValue);
19809         }
19810         
19811         var originalValue = [];
19812         
19813         for (var i = 0; i < d.length; i++){
19814             originalValue.push(d[i][this.valueField]);
19815         }
19816         
19817         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19818         
19819     }
19820     
19821 });
19822
19823
19824
19825 /**
19826  * @class Roo.form.ComboBoxArray.Item
19827  * @extends Roo.BoxComponent
19828  * A selected item in the list
19829  *  Fred [x]  Brian [x]  [Pick another |v]
19830  * 
19831  * @constructor
19832  * Create a new item.
19833  * @param {Object} config Configuration options
19834  */
19835  
19836 Roo.form.ComboBoxArray.Item = function(config) {
19837     config.id = Roo.id();
19838     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19839 }
19840
19841 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19842     data : {},
19843     cb: false,
19844     displayField : false,
19845     tipField : false,
19846     
19847     
19848     defaultAutoCreate : {
19849         tag: 'div',
19850         cls: 'x-cbarray-item',
19851         cn : [ 
19852             { tag: 'div' },
19853             {
19854                 tag: 'img',
19855                 width:16,
19856                 height : 16,
19857                 src : Roo.BLANK_IMAGE_URL ,
19858                 align: 'center'
19859             }
19860         ]
19861         
19862     },
19863     
19864  
19865     onRender : function(ct, position)
19866     {
19867         Roo.form.Field.superclass.onRender.call(this, ct, position);
19868         
19869         if(!this.el){
19870             var cfg = this.getAutoCreate();
19871             this.el = ct.createChild(cfg, position);
19872         }
19873         
19874         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19875         
19876         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19877             this.cb.renderer(this.data) :
19878             String.format('{0}',this.data[this.displayField]);
19879         
19880             
19881         this.el.child('div').dom.setAttribute('qtip',
19882                         String.format('{0}',this.data[this.tipField])
19883         );
19884         
19885         this.el.child('img').on('click', this.remove, this);
19886         
19887     },
19888    
19889     remove : function()
19890     {
19891         if(this.cb.disabled){
19892             return;
19893         }
19894         
19895         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19896             this.cb.items.remove(this);
19897             this.el.child('img').un('click', this.remove, this);
19898             this.el.remove();
19899             this.cb.updateHiddenEl();
19900
19901             this.cb.fireEvent('remove', this.cb, this);
19902         }
19903         
19904     }
19905 });/*
19906  * RooJS Library 1.1.1
19907  * Copyright(c) 2008-2011  Alan Knowles
19908  *
19909  * License - LGPL
19910  */
19911  
19912
19913 /**
19914  * @class Roo.form.ComboNested
19915  * @extends Roo.form.ComboBox
19916  * A combobox for that allows selection of nested items in a list,
19917  * eg.
19918  *
19919  *  Book
19920  *    -> red
19921  *    -> green
19922  *  Table
19923  *    -> square
19924  *      ->red
19925  *      ->green
19926  *    -> rectangle
19927  *      ->green
19928  *      
19929  * 
19930  * @constructor
19931  * Create a new ComboNested
19932  * @param {Object} config Configuration options
19933  */
19934 Roo.form.ComboNested = function(config){
19935     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19936     // should verify some data...
19937     // like
19938     // hiddenName = required..
19939     // displayField = required
19940     // valudField == required
19941     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19942     var _t = this;
19943     Roo.each(req, function(e) {
19944         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19945             throw "Roo.form.ComboNested : missing value for: " + e;
19946         }
19947     });
19948      
19949     
19950 };
19951
19952 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19953    
19954     /*
19955      * @config {Number} max Number of columns to show
19956      */
19957     
19958     maxColumns : 3,
19959    
19960     list : null, // the outermost div..
19961     innerLists : null, // the
19962     views : null,
19963     stores : null,
19964     // private
19965     loadingChildren : false,
19966     
19967     onRender : function(ct, position)
19968     {
19969         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19970         
19971         if(this.hiddenName){
19972             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19973                     'before', true);
19974             this.hiddenField.value =
19975                 this.hiddenValue !== undefined ? this.hiddenValue :
19976                 this.value !== undefined ? this.value : '';
19977
19978             // prevent input submission
19979             this.el.dom.removeAttribute('name');
19980              
19981              
19982         }
19983         
19984         if(Roo.isGecko){
19985             this.el.dom.setAttribute('autocomplete', 'off');
19986         }
19987
19988         var cls = 'x-combo-list';
19989
19990         this.list = new Roo.Layer({
19991             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19992         });
19993
19994         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19995         this.list.setWidth(lw);
19996         this.list.swallowEvent('mousewheel');
19997         this.assetHeight = 0;
19998
19999         if(this.title){
20000             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20001             this.assetHeight += this.header.getHeight();
20002         }
20003         this.innerLists = [];
20004         this.views = [];
20005         this.stores = [];
20006         for (var i =0 ; i < this.maxColumns; i++) {
20007             this.onRenderList( cls, i);
20008         }
20009         
20010         // always needs footer, as we are going to have an 'OK' button.
20011         this.footer = this.list.createChild({cls:cls+'-ft'});
20012         this.pageTb = new Roo.Toolbar(this.footer);  
20013         var _this = this;
20014         this.pageTb.add(  {
20015             
20016             text: 'Done',
20017             handler: function()
20018             {
20019                 _this.collapse();
20020             }
20021         });
20022         
20023         if ( this.allowBlank && !this.disableClear) {
20024             
20025             this.pageTb.add(new Roo.Toolbar.Fill(), {
20026                 cls: 'x-btn-icon x-btn-clear',
20027                 text: '&#160;',
20028                 handler: function()
20029                 {
20030                     _this.collapse();
20031                     _this.clearValue();
20032                     _this.onSelect(false, -1);
20033                 }
20034             });
20035         }
20036         if (this.footer) {
20037             this.assetHeight += this.footer.getHeight();
20038         }
20039         
20040     },
20041     onRenderList : function (  cls, i)
20042     {
20043         
20044         var lw = Math.floor(
20045                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20046         );
20047         
20048         this.list.setWidth(lw); // default to '1'
20049
20050         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20051         //il.on('mouseover', this.onViewOver, this, { list:  i });
20052         //il.on('mousemove', this.onViewMove, this, { list:  i });
20053         il.setWidth(lw);
20054         il.setStyle({ 'overflow-x' : 'hidden'});
20055
20056         if(!this.tpl){
20057             this.tpl = new Roo.Template({
20058                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20059                 isEmpty: function (value, allValues) {
20060                     //Roo.log(value);
20061                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20062                     return dl ? 'has-children' : 'no-children'
20063                 }
20064             });
20065         }
20066         
20067         var store  = this.store;
20068         if (i > 0) {
20069             store  = new Roo.data.SimpleStore({
20070                 //fields : this.store.reader.meta.fields,
20071                 reader : this.store.reader,
20072                 data : [ ]
20073             });
20074         }
20075         this.stores[i]  = store;
20076                   
20077         var view = this.views[i] = new Roo.View(
20078             il,
20079             this.tpl,
20080             {
20081                 singleSelect:true,
20082                 store: store,
20083                 selectedClass: this.selectedClass
20084             }
20085         );
20086         view.getEl().setWidth(lw);
20087         view.getEl().setStyle({
20088             position: i < 1 ? 'relative' : 'absolute',
20089             top: 0,
20090             left: (i * lw ) + 'px',
20091             display : i > 0 ? 'none' : 'block'
20092         });
20093         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20094         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20095         //view.on('click', this.onViewClick, this, { list : i });
20096
20097         store.on('beforeload', this.onBeforeLoad, this);
20098         store.on('load',  this.onLoad, this, { list  : i});
20099         store.on('loadexception', this.onLoadException, this);
20100
20101         // hide the other vies..
20102         
20103         
20104         
20105     },
20106       
20107     restrictHeight : function()
20108     {
20109         var mh = 0;
20110         Roo.each(this.innerLists, function(il,i) {
20111             var el = this.views[i].getEl();
20112             el.dom.style.height = '';
20113             var inner = el.dom;
20114             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20115             // only adjust heights on other ones..
20116             mh = Math.max(h, mh);
20117             if (i < 1) {
20118                 
20119                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20120                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20121                
20122             }
20123             
20124             
20125         }, this);
20126         
20127         this.list.beginUpdate();
20128         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20129         this.list.alignTo(this.el, this.listAlign);
20130         this.list.endUpdate();
20131         
20132     },
20133      
20134     
20135     // -- store handlers..
20136     // private
20137     onBeforeLoad : function()
20138     {
20139         if(!this.hasFocus){
20140             return;
20141         }
20142         this.innerLists[0].update(this.loadingText ?
20143                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20144         this.restrictHeight();
20145         this.selectedIndex = -1;
20146     },
20147     // private
20148     onLoad : function(a,b,c,d)
20149     {
20150         if (!this.loadingChildren) {
20151             // then we are loading the top level. - hide the children
20152             for (var i = 1;i < this.views.length; i++) {
20153                 this.views[i].getEl().setStyle({ display : 'none' });
20154             }
20155             var lw = Math.floor(
20156                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20157             );
20158         
20159              this.list.setWidth(lw); // default to '1'
20160
20161             
20162         }
20163         if(!this.hasFocus){
20164             return;
20165         }
20166         
20167         if(this.store.getCount() > 0) {
20168             this.expand();
20169             this.restrictHeight();   
20170         } else {
20171             this.onEmptyResults();
20172         }
20173         
20174         if (!this.loadingChildren) {
20175             this.selectActive();
20176         }
20177         /*
20178         this.stores[1].loadData([]);
20179         this.stores[2].loadData([]);
20180         this.views
20181         */    
20182     
20183         //this.el.focus();
20184     },
20185     
20186     
20187     // private
20188     onLoadException : function()
20189     {
20190         this.collapse();
20191         Roo.log(this.store.reader.jsonData);
20192         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20193             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20194         }
20195         
20196         
20197     },
20198     // no cleaning of leading spaces on blur here.
20199     cleanLeadingSpace : function(e) { },
20200     
20201
20202     onSelectChange : function (view, sels, opts )
20203     {
20204         var ix = view.getSelectedIndexes();
20205          
20206         if (opts.list > this.maxColumns - 2) {
20207             if (view.store.getCount()<  1) {
20208                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20209
20210             } else  {
20211                 if (ix.length) {
20212                     // used to clear ?? but if we are loading unselected 
20213                     this.setFromData(view.store.getAt(ix[0]).data);
20214                 }
20215                 
20216             }
20217             
20218             return;
20219         }
20220         
20221         if (!ix.length) {
20222             // this get's fired when trigger opens..
20223            // this.setFromData({});
20224             var str = this.stores[opts.list+1];
20225             str.data.clear(); // removeall wihtout the fire events..
20226             return;
20227         }
20228         
20229         var rec = view.store.getAt(ix[0]);
20230          
20231         this.setFromData(rec.data);
20232         this.fireEvent('select', this, rec, ix[0]);
20233         
20234         var lw = Math.floor(
20235              (
20236                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20237              ) / this.maxColumns
20238         );
20239         this.loadingChildren = true;
20240         this.stores[opts.list+1].loadDataFromChildren( rec );
20241         this.loadingChildren = false;
20242         var dl = this.stores[opts.list+1]. getTotalCount();
20243         
20244         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20245         
20246         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20247         for (var i = opts.list+2; i < this.views.length;i++) {
20248             this.views[i].getEl().setStyle({ display : 'none' });
20249         }
20250         
20251         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20252         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20253         
20254         if (this.isLoading) {
20255            // this.selectActive(opts.list);
20256         }
20257          
20258     },
20259     
20260     
20261     
20262     
20263     onDoubleClick : function()
20264     {
20265         this.collapse(); //??
20266     },
20267     
20268      
20269     
20270     
20271     
20272     // private
20273     recordToStack : function(store, prop, value, stack)
20274     {
20275         var cstore = new Roo.data.SimpleStore({
20276             //fields : this.store.reader.meta.fields, // we need array reader.. for
20277             reader : this.store.reader,
20278             data : [ ]
20279         });
20280         var _this = this;
20281         var record  = false;
20282         var srec = false;
20283         if(store.getCount() < 1){
20284             return false;
20285         }
20286         store.each(function(r){
20287             if(r.data[prop] == value){
20288                 record = r;
20289             srec = r;
20290                 return false;
20291             }
20292             if (r.data.cn && r.data.cn.length) {
20293                 cstore.loadDataFromChildren( r);
20294                 var cret = _this.recordToStack(cstore, prop, value, stack);
20295                 if (cret !== false) {
20296                     record = cret;
20297                     srec = r;
20298                     return false;
20299                 }
20300             }
20301              
20302             return true;
20303         });
20304         if (record == false) {
20305             return false
20306         }
20307         stack.unshift(srec);
20308         return record;
20309     },
20310     
20311     /*
20312      * find the stack of stores that match our value.
20313      *
20314      * 
20315      */
20316     
20317     selectActive : function ()
20318     {
20319         // if store is not loaded, then we will need to wait for that to happen first.
20320         var stack = [];
20321         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20322         for (var i = 0; i < stack.length; i++ ) {
20323             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20324         }
20325         
20326     }
20327         
20328          
20329     
20330     
20331     
20332     
20333 });/*
20334  * Based on:
20335  * Ext JS Library 1.1.1
20336  * Copyright(c) 2006-2007, Ext JS, LLC.
20337  *
20338  * Originally Released Under LGPL - original licence link has changed is not relivant.
20339  *
20340  * Fork - LGPL
20341  * <script type="text/javascript">
20342  */
20343 /**
20344  * @class Roo.form.Checkbox
20345  * @extends Roo.form.Field
20346  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20347  * @constructor
20348  * Creates a new Checkbox
20349  * @param {Object} config Configuration options
20350  */
20351 Roo.form.Checkbox = function(config){
20352     Roo.form.Checkbox.superclass.constructor.call(this, config);
20353     this.addEvents({
20354         /**
20355          * @event check
20356          * Fires when the checkbox is checked or unchecked.
20357              * @param {Roo.form.Checkbox} this This checkbox
20358              * @param {Boolean} checked The new checked value
20359              */
20360         check : true
20361     });
20362 };
20363
20364 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20365     /**
20366      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20367      */
20368     focusClass : undefined,
20369     /**
20370      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20371      */
20372     fieldClass: "x-form-field",
20373     /**
20374      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20375      */
20376     checked: false,
20377     /**
20378      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20379      * {tag: "input", type: "checkbox", autocomplete: "off"})
20380      */
20381     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20382     /**
20383      * @cfg {String} boxLabel The text that appears beside the checkbox
20384      */
20385     boxLabel : "",
20386     /**
20387      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20388      */  
20389     inputValue : '1',
20390     /**
20391      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20392      */
20393      valueOff: '0', // value when not checked..
20394
20395     actionMode : 'viewEl', 
20396     //
20397     // private
20398     itemCls : 'x-menu-check-item x-form-item',
20399     groupClass : 'x-menu-group-item',
20400     inputType : 'hidden',
20401     
20402     
20403     inSetChecked: false, // check that we are not calling self...
20404     
20405     inputElement: false, // real input element?
20406     basedOn: false, // ????
20407     
20408     isFormField: true, // not sure where this is needed!!!!
20409
20410     onResize : function(){
20411         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20412         if(!this.boxLabel){
20413             this.el.alignTo(this.wrap, 'c-c');
20414         }
20415     },
20416
20417     initEvents : function(){
20418         Roo.form.Checkbox.superclass.initEvents.call(this);
20419         this.el.on("click", this.onClick,  this);
20420         this.el.on("change", this.onClick,  this);
20421     },
20422
20423
20424     getResizeEl : function(){
20425         return this.wrap;
20426     },
20427
20428     getPositionEl : function(){
20429         return this.wrap;
20430     },
20431
20432     // private
20433     onRender : function(ct, position){
20434         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20435         /*
20436         if(this.inputValue !== undefined){
20437             this.el.dom.value = this.inputValue;
20438         }
20439         */
20440         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20441         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20442         var viewEl = this.wrap.createChild({ 
20443             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20444         this.viewEl = viewEl;   
20445         this.wrap.on('click', this.onClick,  this); 
20446         
20447         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20448         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20449         
20450         
20451         
20452         if(this.boxLabel){
20453             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20454         //    viewEl.on('click', this.onClick,  this); 
20455         }
20456         //if(this.checked){
20457             this.setChecked(this.checked);
20458         //}else{
20459             //this.checked = this.el.dom;
20460         //}
20461
20462     },
20463
20464     // private
20465     initValue : Roo.emptyFn,
20466
20467     /**
20468      * Returns the checked state of the checkbox.
20469      * @return {Boolean} True if checked, else false
20470      */
20471     getValue : function(){
20472         if(this.el){
20473             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20474         }
20475         return this.valueOff;
20476         
20477     },
20478
20479         // private
20480     onClick : function(){ 
20481         if (this.disabled) {
20482             return;
20483         }
20484         this.setChecked(!this.checked);
20485
20486         //if(this.el.dom.checked != this.checked){
20487         //    this.setValue(this.el.dom.checked);
20488        // }
20489     },
20490
20491     /**
20492      * Sets the checked state of the checkbox.
20493      * On is always based on a string comparison between inputValue and the param.
20494      * @param {Boolean/String} value - the value to set 
20495      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20496      */
20497     setValue : function(v,suppressEvent){
20498         
20499         
20500         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20501         //if(this.el && this.el.dom){
20502         //    this.el.dom.checked = this.checked;
20503         //    this.el.dom.defaultChecked = this.checked;
20504         //}
20505         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20506         //this.fireEvent("check", this, this.checked);
20507     },
20508     // private..
20509     setChecked : function(state,suppressEvent)
20510     {
20511         if (this.inSetChecked) {
20512             this.checked = state;
20513             return;
20514         }
20515         
20516     
20517         if(this.wrap){
20518             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20519         }
20520         this.checked = state;
20521         if(suppressEvent !== true){
20522             this.fireEvent('check', this, state);
20523         }
20524         this.inSetChecked = true;
20525         this.el.dom.value = state ? this.inputValue : this.valueOff;
20526         this.inSetChecked = false;
20527         
20528     },
20529     // handle setting of hidden value by some other method!!?!?
20530     setFromHidden: function()
20531     {
20532         if(!this.el){
20533             return;
20534         }
20535         //console.log("SET FROM HIDDEN");
20536         //alert('setFrom hidden');
20537         this.setValue(this.el.dom.value);
20538     },
20539     
20540     onDestroy : function()
20541     {
20542         if(this.viewEl){
20543             Roo.get(this.viewEl).remove();
20544         }
20545          
20546         Roo.form.Checkbox.superclass.onDestroy.call(this);
20547     },
20548     
20549     setBoxLabel : function(str)
20550     {
20551         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20552     }
20553
20554 });/*
20555  * Based on:
20556  * Ext JS Library 1.1.1
20557  * Copyright(c) 2006-2007, Ext JS, LLC.
20558  *
20559  * Originally Released Under LGPL - original licence link has changed is not relivant.
20560  *
20561  * Fork - LGPL
20562  * <script type="text/javascript">
20563  */
20564  
20565 /**
20566  * @class Roo.form.Radio
20567  * @extends Roo.form.Checkbox
20568  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20569  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20570  * @constructor
20571  * Creates a new Radio
20572  * @param {Object} config Configuration options
20573  */
20574 Roo.form.Radio = function(){
20575     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20576 };
20577 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20578     inputType: 'radio',
20579
20580     /**
20581      * If this radio is part of a group, it will return the selected value
20582      * @return {String}
20583      */
20584     getGroupValue : function(){
20585         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20586     },
20587     
20588     
20589     onRender : function(ct, position){
20590         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20591         
20592         if(this.inputValue !== undefined){
20593             this.el.dom.value = this.inputValue;
20594         }
20595          
20596         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20597         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20598         //var viewEl = this.wrap.createChild({ 
20599         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20600         //this.viewEl = viewEl;   
20601         //this.wrap.on('click', this.onClick,  this); 
20602         
20603         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20604         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20605         
20606         
20607         
20608         if(this.boxLabel){
20609             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20610         //    viewEl.on('click', this.onClick,  this); 
20611         }
20612          if(this.checked){
20613             this.el.dom.checked =   'checked' ;
20614         }
20615          
20616     } 
20617     
20618     
20619 });Roo.rtf = {}; // namespace
20620 Roo.rtf.Hex = function(hex)
20621 {
20622     this.hexstr = hex;
20623 };
20624 Roo.rtf.Paragraph = function(opts)
20625 {
20626     this.content = []; ///??? is that used?
20627 };Roo.rtf.Span = function(opts)
20628 {
20629     this.value = opts.value;
20630 };
20631
20632 Roo.rtf.Group = function(parent)
20633 {
20634     // we dont want to acutally store parent - it will make debug a nightmare..
20635     this.content = [];
20636     this.cn  = [];
20637      
20638        
20639     
20640 };
20641
20642 Roo.rtf.Group.prototype = {
20643     ignorable : false,
20644     content: false,
20645     cn: false,
20646     addContent : function(node) {
20647         // could set styles...
20648         this.content.push(node);
20649     },
20650     addChild : function(cn)
20651     {
20652         this.cn.push(cn);
20653     },
20654     // only for images really...
20655     toDataURL : function()
20656     {
20657         var mimetype = false;
20658         switch(true) {
20659             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20660                 mimetype = "image/png";
20661                 break;
20662              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20663                 mimetype = "image/jpeg";
20664                 break;
20665             default :
20666                 return 'about:blank'; // ?? error?
20667         }
20668         
20669         
20670         var hexstring = this.content[this.content.length-1].value;
20671         
20672         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20673             return String.fromCharCode(parseInt(a, 16));
20674         }).join(""));
20675     }
20676     
20677 };
20678 // this looks like it's normally the {rtf{ .... }}
20679 Roo.rtf.Document = function()
20680 {
20681     // we dont want to acutally store parent - it will make debug a nightmare..
20682     this.rtlch  = [];
20683     this.content = [];
20684     this.cn = [];
20685     
20686 };
20687 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20688     addChild : function(cn)
20689     {
20690         this.cn.push(cn);
20691         switch(cn.type) {
20692             case 'rtlch': // most content seems to be inside this??
20693             case 'listtext':
20694             case 'shpinst':
20695                 this.rtlch.push(cn);
20696                 return;
20697             default:
20698                 this[cn.type] = cn;
20699         }
20700         
20701     },
20702     
20703     getElementsByType : function(type)
20704     {
20705         var ret =  [];
20706         this._getElementsByType(type, ret, this.cn, 'rtf');
20707         return ret;
20708     },
20709     _getElementsByType : function (type, ret, search_array, path)
20710     {
20711         search_array.forEach(function(n,i) {
20712             if (n.type == type) {
20713                 n.path = path + '/' + n.type + ':' + i;
20714                 ret.push(n);
20715             }
20716             if (n.cn.length > 0) {
20717                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20718             }
20719         },this);
20720     }
20721     
20722 });
20723  
20724 Roo.rtf.Ctrl = function(opts)
20725 {
20726     this.value = opts.value;
20727     this.param = opts.param;
20728 };
20729 /**
20730  *
20731  *
20732  * based on this https://github.com/iarna/rtf-parser
20733  * it's really only designed to extract pict from pasted RTF 
20734  *
20735  * usage:
20736  *
20737  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20738  *  
20739  *
20740  */
20741
20742  
20743
20744
20745
20746 Roo.rtf.Parser = function(text) {
20747     //super({objectMode: true})
20748     this.text = '';
20749     this.parserState = this.parseText;
20750     
20751     // these are for interpeter...
20752     this.doc = {};
20753     ///this.parserState = this.parseTop
20754     this.groupStack = [];
20755     this.hexStore = [];
20756     this.doc = false;
20757     
20758     this.groups = []; // where we put the return.
20759     
20760     for (var ii = 0; ii < text.length; ++ii) {
20761         ++this.cpos;
20762         
20763         if (text[ii] === '\n') {
20764             ++this.row;
20765             this.col = 1;
20766         } else {
20767             ++this.col;
20768         }
20769         this.parserState(text[ii]);
20770     }
20771     
20772     
20773     
20774 };
20775 Roo.rtf.Parser.prototype = {
20776     text : '', // string being parsed..
20777     controlWord : '',
20778     controlWordParam :  '',
20779     hexChar : '',
20780     doc : false,
20781     group: false,
20782     groupStack : false,
20783     hexStore : false,
20784     
20785     
20786     cpos : 0, 
20787     row : 1, // reportin?
20788     col : 1, //
20789
20790      
20791     push : function (el)
20792     {
20793         var m = 'cmd'+ el.type;
20794         if (typeof(this[m]) == 'undefined') {
20795             Roo.log('invalid cmd:' + el.type);
20796             return;
20797         }
20798         this[m](el);
20799         //Roo.log(el);
20800     },
20801     flushHexStore : function()
20802     {
20803         if (this.hexStore.length < 1) {
20804             return;
20805         }
20806         var hexstr = this.hexStore.map(
20807             function(cmd) {
20808                 return cmd.value;
20809         }).join('');
20810         
20811         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20812               
20813             
20814         this.hexStore.splice(0)
20815         
20816     },
20817     
20818     cmdgroupstart : function()
20819     {
20820         this.flushHexStore();
20821         if (this.group) {
20822             this.groupStack.push(this.group);
20823         }
20824          // parent..
20825         if (this.doc === false) {
20826             this.group = this.doc = new Roo.rtf.Document();
20827             return;
20828             
20829         }
20830         this.group = new Roo.rtf.Group(this.group);
20831     },
20832     cmdignorable : function()
20833     {
20834         this.flushHexStore();
20835         this.group.ignorable = true;
20836     },
20837     cmdendparagraph : function()
20838     {
20839         this.flushHexStore();
20840         this.group.addContent(new Roo.rtf.Paragraph());
20841     },
20842     cmdgroupend : function ()
20843     {
20844         this.flushHexStore();
20845         var endingGroup = this.group;
20846         
20847         
20848         this.group = this.groupStack.pop();
20849         if (this.group) {
20850             this.group.addChild(endingGroup);
20851         }
20852         
20853         
20854         
20855         var doc = this.group || this.doc;
20856         //if (endingGroup instanceof FontTable) {
20857         //  doc.fonts = endingGroup.table
20858         //} else if (endingGroup instanceof ColorTable) {
20859         //  doc.colors = endingGroup.table
20860         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20861         if (endingGroup.ignorable === false) {
20862             //code
20863             this.groups.push(endingGroup);
20864            // Roo.log( endingGroup );
20865         }
20866             //Roo.each(endingGroup.content, function(item)) {
20867             //    doc.addContent(item);
20868             //}
20869             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20870         //}
20871     },
20872     cmdtext : function (cmd)
20873     {
20874         this.flushHexStore();
20875         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20876             //this.group = this.doc
20877             return;  // we really don't care about stray text...
20878         }
20879         this.group.addContent(new Roo.rtf.Span(cmd));
20880     },
20881     cmdcontrolword : function (cmd)
20882     {
20883         this.flushHexStore();
20884         if (!this.group.type) {
20885             this.group.type = cmd.value;
20886             return;
20887         }
20888         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20889         // we actually don't care about ctrl words...
20890         return ;
20891         /*
20892         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20893         if (this[method]) {
20894             this[method](cmd.param)
20895         } else {
20896             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20897         }
20898         */
20899     },
20900     cmdhexchar : function(cmd) {
20901         this.hexStore.push(cmd);
20902     },
20903     cmderror : function(cmd) {
20904         throw cmd.value;
20905     },
20906     
20907     /*
20908       _flush (done) {
20909         if (this.text !== '\u0000') this.emitText()
20910         done()
20911       }
20912       */
20913       
20914       
20915     parseText : function(c)
20916     {
20917         if (c === '\\') {
20918             this.parserState = this.parseEscapes;
20919         } else if (c === '{') {
20920             this.emitStartGroup();
20921         } else if (c === '}') {
20922             this.emitEndGroup();
20923         } else if (c === '\x0A' || c === '\x0D') {
20924             // cr/lf are noise chars
20925         } else {
20926             this.text += c;
20927         }
20928     },
20929     
20930     parseEscapes: function (c)
20931     {
20932         if (c === '\\' || c === '{' || c === '}') {
20933             this.text += c;
20934             this.parserState = this.parseText;
20935         } else {
20936             this.parserState = this.parseControlSymbol;
20937             this.parseControlSymbol(c);
20938         }
20939     },
20940     parseControlSymbol: function(c)
20941     {
20942         if (c === '~') {
20943             this.text += '\u00a0'; // nbsp
20944             this.parserState = this.parseText
20945         } else if (c === '-') {
20946              this.text += '\u00ad'; // soft hyphen
20947         } else if (c === '_') {
20948             this.text += '\u2011'; // non-breaking hyphen
20949         } else if (c === '*') {
20950             this.emitIgnorable();
20951             this.parserState = this.parseText;
20952         } else if (c === "'") {
20953             this.parserState = this.parseHexChar;
20954         } else if (c === '|') { // formula cacter
20955             this.emitFormula();
20956             this.parserState = this.parseText;
20957         } else if (c === ':') { // subentry in an index entry
20958             this.emitIndexSubEntry();
20959             this.parserState = this.parseText;
20960         } else if (c === '\x0a') {
20961             this.emitEndParagraph();
20962             this.parserState = this.parseText;
20963         } else if (c === '\x0d') {
20964             this.emitEndParagraph();
20965             this.parserState = this.parseText;
20966         } else {
20967             this.parserState = this.parseControlWord;
20968             this.parseControlWord(c);
20969         }
20970     },
20971     parseHexChar: function (c)
20972     {
20973         if (/^[A-Fa-f0-9]$/.test(c)) {
20974             this.hexChar += c;
20975             if (this.hexChar.length >= 2) {
20976               this.emitHexChar();
20977               this.parserState = this.parseText;
20978             }
20979             return;
20980         }
20981         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20982         this.parserState = this.parseText;
20983         
20984     },
20985     parseControlWord : function(c)
20986     {
20987         if (c === ' ') {
20988             this.emitControlWord();
20989             this.parserState = this.parseText;
20990         } else if (/^[-\d]$/.test(c)) {
20991             this.parserState = this.parseControlWordParam;
20992             this.controlWordParam += c;
20993         } else if (/^[A-Za-z]$/.test(c)) {
20994           this.controlWord += c;
20995         } else {
20996           this.emitControlWord();
20997           this.parserState = this.parseText;
20998           this.parseText(c);
20999         }
21000     },
21001     parseControlWordParam : function (c) {
21002         if (/^\d$/.test(c)) {
21003           this.controlWordParam += c;
21004         } else if (c === ' ') {
21005           this.emitControlWord();
21006           this.parserState = this.parseText;
21007         } else {
21008           this.emitControlWord();
21009           this.parserState = this.parseText;
21010           this.parseText(c);
21011         }
21012     },
21013     
21014     
21015     
21016     
21017     emitText : function () {
21018         if (this.text === '') {
21019             return;
21020         }
21021         this.push({
21022             type: 'text',
21023             value: this.text,
21024             pos: this.cpos,
21025             row: this.row,
21026             col: this.col
21027         });
21028         this.text = ''
21029     },
21030     emitControlWord : function ()
21031     {
21032         this.emitText();
21033         if (this.controlWord === '') {
21034             // do we want to track this - it seems just to cause problems.
21035             //this.emitError('empty control word');
21036         } else {
21037             this.push({
21038                   type: 'controlword',
21039                   value: this.controlWord,
21040                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21041                   pos: this.cpos,
21042                   row: this.row,
21043                   col: this.col
21044             });
21045         }
21046         this.controlWord = '';
21047         this.controlWordParam = '';
21048     },
21049     emitStartGroup : function ()
21050     {
21051         this.emitText();
21052         this.push({
21053             type: 'groupstart',
21054             pos: this.cpos,
21055             row: this.row,
21056             col: this.col
21057         });
21058     },
21059     emitEndGroup : function ()
21060     {
21061         this.emitText();
21062         this.push({
21063             type: 'groupend',
21064             pos: this.cpos,
21065             row: this.row,
21066             col: this.col
21067         });
21068     },
21069     emitIgnorable : function ()
21070     {
21071         this.emitText();
21072         this.push({
21073             type: 'ignorable',
21074             pos: this.cpos,
21075             row: this.row,
21076             col: this.col
21077         });
21078     },
21079     emitHexChar : function ()
21080     {
21081         this.emitText();
21082         this.push({
21083             type: 'hexchar',
21084             value: this.hexChar,
21085             pos: this.cpos,
21086             row: this.row,
21087             col: this.col
21088         });
21089         this.hexChar = ''
21090     },
21091     emitError : function (message)
21092     {
21093       this.emitText();
21094       this.push({
21095             type: 'error',
21096             value: message,
21097             row: this.row,
21098             col: this.col,
21099             char: this.cpos //,
21100             //stack: new Error().stack
21101         });
21102     },
21103     emitEndParagraph : function () {
21104         this.emitText();
21105         this.push({
21106             type: 'endparagraph',
21107             pos: this.cpos,
21108             row: this.row,
21109             col: this.col
21110         });
21111     }
21112      
21113 } ;
21114 Roo.htmleditor = {};
21115  
21116 /**
21117  * @class Roo.htmleditor.Filter
21118  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21119  * @cfg {DomElement} node The node to iterate and filter
21120  * @cfg {boolean|String|Array} tag Tags to replace 
21121  * @constructor
21122  * Create a new Filter.
21123  * @param {Object} config Configuration options
21124  */
21125
21126
21127
21128 Roo.htmleditor.Filter = function(cfg) {
21129     Roo.apply(this.cfg);
21130     // this does not actually call walk as it's really just a abstract class
21131 }
21132
21133
21134 Roo.htmleditor.Filter.prototype = {
21135     
21136     node: false,
21137     
21138     tag: false,
21139
21140     // overrride to do replace comments.
21141     replaceComment : false,
21142     
21143     // overrride to do replace or do stuff with tags..
21144     replaceTag : false,
21145     
21146     walk : function(dom)
21147     {
21148         Roo.each( Array.from(dom.childNodes), function( e ) {
21149             switch(true) {
21150                 
21151                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21152                     this.replaceComment(e);
21153                     return;
21154                 
21155                 case e.nodeType != 1: //not a node.
21156                     return;
21157                 
21158                 case this.tag === true: // everything
21159                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21160                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21161                     if (this.replaceTag && false === this.replaceTag(e)) {
21162                         return;
21163                     }
21164                     if (e.hasChildNodes()) {
21165                         this.walk(e);
21166                     }
21167                     return;
21168                 
21169                 default:    // tags .. that do not match.
21170                     if (e.hasChildNodes()) {
21171                         this.walk(e);
21172                     }
21173             }
21174             
21175         }, this);
21176         
21177     }
21178 }; 
21179
21180 /**
21181  * @class Roo.htmleditor.FilterAttributes
21182  * clean attributes and  styles including http:// etc.. in attribute
21183  * @constructor
21184 * Run a new Attribute Filter
21185 * @param {Object} config Configuration options
21186  */
21187 Roo.htmleditor.FilterAttributes = function(cfg)
21188 {
21189     Roo.apply(this, cfg);
21190     this.attrib_black = this.attrib_black || [];
21191     this.attrib_white = this.attrib_white || [];
21192
21193     this.attrib_clean = this.attrib_clean || [];
21194     this.style_white = this.style_white || [];
21195     this.style_black = this.style_black || [];
21196     this.walk(cfg.node);
21197 }
21198
21199 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21200 {
21201     tag: true, // all tags
21202     
21203     attrib_black : false, // array
21204     attrib_clean : false,
21205     attrib_white : false,
21206
21207     style_white : false,
21208     style_black : false,
21209      
21210      
21211     replaceTag : function(node)
21212     {
21213         if (!node.attributes || !node.attributes.length) {
21214             return true;
21215         }
21216         
21217         for (var i = node.attributes.length-1; i > -1 ; i--) {
21218             var a = node.attributes[i];
21219             //console.log(a);
21220             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21221                 node.removeAttribute(a.name);
21222                 continue;
21223             }
21224             
21225             
21226             
21227             if (a.name.toLowerCase().substr(0,2)=='on')  {
21228                 node.removeAttribute(a.name);
21229                 continue;
21230             }
21231             
21232             
21233             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21234                 node.removeAttribute(a.name);
21235                 continue;
21236             }
21237             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21238                 this.cleanAttr(node,a.name,a.value); // fixme..
21239                 continue;
21240             }
21241             if (a.name == 'style') {
21242                 this.cleanStyle(node,a.name,a.value);
21243                 continue;
21244             }
21245             /// clean up MS crap..
21246             // tecnically this should be a list of valid class'es..
21247             
21248             
21249             if (a.name == 'class') {
21250                 if (a.value.match(/^Mso/)) {
21251                     node.removeAttribute('class');
21252                 }
21253                 
21254                 if (a.value.match(/^body$/)) {
21255                     node.removeAttribute('class');
21256                 }
21257                 continue;
21258             }
21259             
21260             
21261             // style cleanup!?
21262             // class cleanup?
21263             
21264         }
21265         return true; // clean children
21266     },
21267         
21268     cleanAttr: function(node, n,v)
21269     {
21270         
21271         if (v.match(/^\./) || v.match(/^\//)) {
21272             return;
21273         }
21274         if (v.match(/^(http|https):\/\//)
21275             || v.match(/^mailto:/) 
21276             || v.match(/^ftp:/)
21277             || v.match(/^data:/)
21278             ) {
21279             return;
21280         }
21281         if (v.match(/^#/)) {
21282             return;
21283         }
21284         if (v.match(/^\{/)) { // allow template editing.
21285             return;
21286         }
21287 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21288         node.removeAttribute(n);
21289         
21290     },
21291     cleanStyle : function(node,  n,v)
21292     {
21293         if (v.match(/expression/)) { //XSS?? should we even bother..
21294             node.removeAttribute(n);
21295             return;
21296         }
21297         
21298         var parts = v.split(/;/);
21299         var clean = [];
21300         
21301         Roo.each(parts, function(p) {
21302             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21303             if (!p.length) {
21304                 return true;
21305             }
21306             var l = p.split(':').shift().replace(/\s+/g,'');
21307             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21308             
21309             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21310                 return true;
21311             }
21312             //Roo.log()
21313             // only allow 'c whitelisted system attributes'
21314             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21315                 return true;
21316             }
21317             
21318             
21319             clean.push(p);
21320             return true;
21321         },this);
21322         if (clean.length) { 
21323             node.setAttribute(n, clean.join(';'));
21324         } else {
21325             node.removeAttribute(n);
21326         }
21327         
21328     }
21329         
21330         
21331         
21332     
21333 });/**
21334  * @class Roo.htmleditor.FilterBlack
21335  * remove blacklisted elements.
21336  * @constructor
21337  * Run a new Blacklisted Filter
21338  * @param {Object} config Configuration options
21339  */
21340
21341 Roo.htmleditor.FilterBlack = function(cfg)
21342 {
21343     Roo.apply(this, cfg);
21344     this.walk(cfg.node);
21345 }
21346
21347 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21348 {
21349     tag : true, // all elements.
21350    
21351     replaceTag : function(n)
21352     {
21353         n.parentNode.removeChild(n);
21354     }
21355 });
21356 /**
21357  * @class Roo.htmleditor.FilterComment
21358  * remove comments.
21359  * @constructor
21360 * Run a new Comments Filter
21361 * @param {Object} config Configuration options
21362  */
21363 Roo.htmleditor.FilterComment = function(cfg)
21364 {
21365     this.walk(cfg.node);
21366 }
21367
21368 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21369 {
21370   
21371     replaceComment : function(n)
21372     {
21373         n.parentNode.removeChild(n);
21374     }
21375 });/**
21376  * @class Roo.htmleditor.FilterKeepChildren
21377  * remove tags but keep children
21378  * @constructor
21379  * Run a new Keep Children Filter
21380  * @param {Object} config Configuration options
21381  */
21382
21383 Roo.htmleditor.FilterKeepChildren = function(cfg)
21384 {
21385     Roo.apply(this, cfg);
21386     if (this.tag === false) {
21387         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21388     }
21389     this.walk(cfg.node);
21390 }
21391
21392 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21393 {
21394     
21395   
21396     replaceTag : function(node)
21397     {
21398         // walk children...
21399         //Roo.log(node);
21400         var ar = Array.from(node.childNodes);
21401         //remove first..
21402         for (var i = 0; i < ar.length; i++) {
21403             if (ar[i].nodeType == 1) {
21404                 if (
21405                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21406                     || // array and it matches
21407                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21408                 ) {
21409                     this.replaceTag(ar[i]); // child is blacklisted as well...
21410                     continue;
21411                 }
21412             }
21413         }  
21414         ar = Array.from(node.childNodes);
21415         for (var i = 0; i < ar.length; i++) {
21416          
21417             node.removeChild(ar[i]);
21418             // what if we need to walk these???
21419             node.parentNode.insertBefore(ar[i], node);
21420             if (this.tag !== false) {
21421                 this.walk(ar[i]);
21422                 
21423             }
21424         }
21425         node.parentNode.removeChild(node);
21426         return false; // don't walk children
21427         
21428         
21429     }
21430 });/**
21431  * @class Roo.htmleditor.FilterParagraph
21432  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21433  * like on 'push' to remove the <p> tags and replace them with line breaks.
21434  * @constructor
21435  * Run a new Paragraph Filter
21436  * @param {Object} config Configuration options
21437  */
21438
21439 Roo.htmleditor.FilterParagraph = function(cfg)
21440 {
21441     // no need to apply config.
21442     this.walk(cfg.node);
21443 }
21444
21445 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21446 {
21447     
21448      
21449     tag : 'P',
21450     
21451      
21452     replaceTag : function(node)
21453     {
21454         
21455         if (node.childNodes.length == 1 &&
21456             node.childNodes[0].nodeType == 3 &&
21457             node.childNodes[0].textContent.trim().length < 1
21458             ) {
21459             // remove and replace with '<BR>';
21460             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21461             return false; // no need to walk..
21462         }
21463         var ar = Array.from(node.childNodes);
21464         for (var i = 0; i < ar.length; i++) {
21465             node.removeChild(ar[i]);
21466             // what if we need to walk these???
21467             node.parentNode.insertBefore(ar[i], node);
21468         }
21469         // now what about this?
21470         // <p> &nbsp; </p>
21471         
21472         // double BR.
21473         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21474         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21475         node.parentNode.removeChild(node);
21476         
21477         return false;
21478
21479     }
21480     
21481 });/**
21482  * @class Roo.htmleditor.FilterSpan
21483  * filter span's with no attributes out..
21484  * @constructor
21485  * Run a new Span Filter
21486  * @param {Object} config Configuration options
21487  */
21488
21489 Roo.htmleditor.FilterSpan = function(cfg)
21490 {
21491     // no need to apply config.
21492     this.walk(cfg.node);
21493 }
21494
21495 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21496 {
21497      
21498     tag : 'SPAN',
21499      
21500  
21501     replaceTag : function(node)
21502     {
21503         if (node.attributes && node.attributes.length > 0) {
21504             return true; // walk if there are any.
21505         }
21506         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21507         return false;
21508      
21509     }
21510     
21511 });/**
21512  * @class Roo.htmleditor.FilterTableWidth
21513   try and remove table width data - as that frequently messes up other stuff.
21514  * 
21515  *      was cleanTableWidths.
21516  *
21517  * Quite often pasting from word etc.. results in tables with column and widths.
21518  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21519  *
21520  * @constructor
21521  * Run a new Table Filter
21522  * @param {Object} config Configuration options
21523  */
21524
21525 Roo.htmleditor.FilterTableWidth = function(cfg)
21526 {
21527     // no need to apply config.
21528     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21529     this.walk(cfg.node);
21530 }
21531
21532 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21533 {
21534      
21535      
21536     
21537     replaceTag: function(node) {
21538         
21539         
21540       
21541         if (node.hasAttribute('width')) {
21542             node.removeAttribute('width');
21543         }
21544         
21545          
21546         if (node.hasAttribute("style")) {
21547             // pretty basic...
21548             
21549             var styles = node.getAttribute("style").split(";");
21550             var nstyle = [];
21551             Roo.each(styles, function(s) {
21552                 if (!s.match(/:/)) {
21553                     return;
21554                 }
21555                 var kv = s.split(":");
21556                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21557                     return;
21558                 }
21559                 // what ever is left... we allow.
21560                 nstyle.push(s);
21561             });
21562             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21563             if (!nstyle.length) {
21564                 node.removeAttribute('style');
21565             }
21566         }
21567         
21568         return true; // continue doing children..
21569     }
21570 });/**
21571  * @class Roo.htmleditor.FilterWord
21572  * try and clean up all the mess that Word generates.
21573  * 
21574  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21575  
21576  * @constructor
21577  * Run a new Span Filter
21578  * @param {Object} config Configuration options
21579  */
21580
21581 Roo.htmleditor.FilterWord = function(cfg)
21582 {
21583     // no need to apply config.
21584     this.replaceDocBullets(cfg.node);
21585     
21586    // this.walk(cfg.node);
21587     
21588     
21589 }
21590
21591 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21592 {
21593     tag: true,
21594      
21595     
21596     /**
21597      * Clean up MS wordisms...
21598      */
21599     replaceTag : function(node)
21600     {
21601          
21602         // no idea what this does - span with text, replaceds with just text.
21603         if(
21604                 node.nodeName == 'SPAN' &&
21605                 !node.hasAttributes() &&
21606                 node.childNodes.length == 1 &&
21607                 node.firstChild.nodeName == "#text"  
21608         ) {
21609             var textNode = node.firstChild;
21610             node.removeChild(textNode);
21611             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21612                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21613             }
21614             node.parentNode.insertBefore(textNode, node);
21615             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21616                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21617             }
21618             
21619             node.parentNode.removeChild(node);
21620             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21621         }
21622         
21623    
21624         
21625         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21626             node.parentNode.removeChild(node);
21627             return false; // dont do chidlren
21628         }
21629         //Roo.log(node.tagName);
21630         // remove - but keep children..
21631         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21632             //Roo.log('-- removed');
21633             while (node.childNodes.length) {
21634                 var cn = node.childNodes[0];
21635                 node.removeChild(cn);
21636                 node.parentNode.insertBefore(cn, node);
21637                 // move node to parent - and clean it..
21638                 if (cn.nodeType == 1) {
21639                     this.replaceTag(cn);
21640                 }
21641                 
21642             }
21643             node.parentNode.removeChild(node);
21644             /// no need to iterate chidlren = it's got none..
21645             //this.iterateChildren(node, this.cleanWord);
21646             return false; // no need to iterate children.
21647         }
21648         // clean styles
21649         if (node.className.length) {
21650             
21651             var cn = node.className.split(/\W+/);
21652             var cna = [];
21653             Roo.each(cn, function(cls) {
21654                 if (cls.match(/Mso[a-zA-Z]+/)) {
21655                     return;
21656                 }
21657                 cna.push(cls);
21658             });
21659             node.className = cna.length ? cna.join(' ') : '';
21660             if (!cna.length) {
21661                 node.removeAttribute("class");
21662             }
21663         }
21664         
21665         if (node.hasAttribute("lang")) {
21666             node.removeAttribute("lang");
21667         }
21668         
21669         if (node.hasAttribute("style")) {
21670             
21671             var styles = node.getAttribute("style").split(";");
21672             var nstyle = [];
21673             Roo.each(styles, function(s) {
21674                 if (!s.match(/:/)) {
21675                     return;
21676                 }
21677                 var kv = s.split(":");
21678                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21679                     return;
21680                 }
21681                 // what ever is left... we allow.
21682                 nstyle.push(s);
21683             });
21684             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21685             if (!nstyle.length) {
21686                 node.removeAttribute('style');
21687             }
21688         }
21689         return true; // do children
21690         
21691         
21692         
21693     },
21694     
21695     styleToObject: function(node)
21696     {
21697         var styles = (node.getAttribute("style") || '').split(";");
21698         var ret = {};
21699         Roo.each(styles, function(s) {
21700             if (!s.match(/:/)) {
21701                 return;
21702             }
21703             var kv = s.split(":");
21704              
21705             // what ever is left... we allow.
21706             ret[kv[0]] = kv[1];
21707         });
21708         return ret;
21709     },
21710     
21711     
21712     replaceDocBullets : function(doc)
21713     {
21714         // this is a bit odd - but it appears some indents use ql-indent-1
21715         
21716         var listpara = doc.getElementsByClassName('ql-indent-1');
21717         while(listpara.length) {
21718             this.replaceDocBullet(listpara.item(0));
21719         }
21720         
21721         var listpara = doc.getElementsByClassName('MsoListParagraph');
21722         while(listpara.length) {
21723             this.replaceDocBullet(listpara.item(0));
21724         }
21725     },
21726     
21727     replaceDocBullet : function(p)
21728     {
21729         // gather all the siblings.
21730         var ns = p,
21731             parent = p.parentNode,
21732             doc = parent.ownerDocument,
21733             items = []; 
21734         while (ns) {
21735             if (ns.nodeType != 1) {
21736                 ns = ns.nextSibling;
21737                 continue;
21738             }
21739             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
21740                 break;
21741             }
21742             items.push(ns);
21743             ns = ns.nextSibling;
21744             
21745         }
21746         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
21747         parent.insertBefore(ul, p);
21748         var lvl = 0;
21749         var stack = [ ul ];
21750         var last_li = false;
21751         items.forEach(function(n) {
21752             //Roo.log("got innertHMLT=" + n.innerHTML);
21753             
21754             var spans = n.getElementsByTagName('span');
21755             if (!spans.length) {
21756                 //Roo.log("No spans found");
21757
21758                 parent.removeChild(n);
21759                 return; // skip it...
21760             }
21761            
21762                 
21763             
21764             var style = {};
21765             for(var i = 0; i < spans.length; i++) {
21766             
21767                 style = this.styleToObject(spans[i]);
21768                 if (typeof(style['mso-list']) == 'undefined') {
21769                     continue;
21770                 }
21771                 
21772                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
21773                 break;
21774             }
21775             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
21776             style = this.styleToObject(n); // mo-list is from the parent node.
21777             if (typeof(style['mso-list']) == 'undefined') {
21778                 //Roo.log("parent is missing level");
21779                 parent.removeChild(n);
21780                 return;
21781             }
21782             
21783             var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
21784             if (nlvl > lvl) {
21785                 //new indent
21786                 var nul = doc.createElement('ul'); // what about number lists...
21787                 last_li.appendChild(nul);
21788                 stack[nlvl] = nul;
21789             }
21790             lvl = nlvl;
21791             
21792             var nli = stack[nlvl].appendChild(doc.createElement('li'));
21793             last_li = nli;
21794             nli.innerHTML = n.innerHTML;
21795             //Roo.log("innerHTML = " + n.innerHTML);
21796             parent.removeChild(n);
21797             
21798             // copy children of p into nli
21799             /*while(n.firstChild) {
21800                 var fc = n.firstChild;
21801                 n.removeChild(fc);
21802                 nli.appendChild(fc);
21803             }*/
21804              
21805             
21806         },this);
21807         
21808         
21809         
21810         
21811     }
21812     
21813     
21814     
21815 });
21816 /**
21817  * @class Roo.htmleditor.FilterStyleToTag
21818  * part of the word stuff... - certain 'styles' should be converted to tags.
21819  * eg.
21820  *   font-weight: bold -> bold
21821  *   ?? super / subscrit etc..
21822  * 
21823  * @constructor
21824 * Run a new style to tag filter.
21825 * @param {Object} config Configuration options
21826  */
21827 Roo.htmleditor.FilterStyleToTag = function(cfg)
21828 {
21829     
21830     this.tags = {
21831         B  : [ 'fontWeight' , 'bold'],
21832         I :  [ 'fontStyle' , 'italic'],
21833         //pre :  [ 'font-style' , 'italic'],
21834         // h1.. h6 ?? font-size?
21835         SUP : [ 'verticalAlign' , 'super' ],
21836         SUB : [ 'verticalAlign' , 'sub' ]
21837         
21838         
21839     };
21840     
21841     Roo.apply(this, cfg);
21842      
21843     
21844     this.walk(cfg.node);
21845     
21846     
21847     
21848 }
21849
21850
21851 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21852 {
21853     tag: true, // all tags
21854     
21855     tags : false,
21856     
21857     
21858     replaceTag : function(node)
21859     {
21860         
21861         
21862         if (node.getAttribute("style") === null) {
21863             return true;
21864         }
21865         var inject = [];
21866         for (var k in this.tags) {
21867             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21868                 inject.push(k);
21869                 node.style.removeProperty(this.tags[k][0]);
21870             }
21871         }
21872         if (!inject.length) {
21873             return true; 
21874         }
21875         var cn = Array.from(node.childNodes);
21876         var nn = node;
21877         Roo.each(inject, function(t) {
21878             var nc = node.ownerDocument.createElement(t);
21879             nn.appendChild(nc);
21880             nn = nc;
21881         });
21882         for(var i = 0;i < cn.length;cn++) {
21883             node.removeChild(cn[i]);
21884             nn.appendChild(cn[i]);
21885         }
21886         return true /// iterate thru
21887     }
21888     
21889 })/**
21890  * @class Roo.htmleditor.FilterLongBr
21891  * BR/BR/BR - keep a maximum of 2...
21892  * @constructor
21893  * Run a new Long BR Filter
21894  * @param {Object} config Configuration options
21895  */
21896
21897 Roo.htmleditor.FilterLongBr = function(cfg)
21898 {
21899     // no need to apply config.
21900     this.walk(cfg.node);
21901 }
21902
21903 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21904 {
21905     
21906      
21907     tag : 'BR',
21908     
21909      
21910     replaceTag : function(node)
21911     {
21912         
21913         var ps = node.nextSibling;
21914         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21915             ps = ps.nextSibling;
21916         }
21917         
21918         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21919             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21920             return false;
21921         }
21922         
21923         if (!ps || ps.nodeType != 1) {
21924             return false;
21925         }
21926         
21927         if (!ps || ps.tagName != 'BR') {
21928            
21929             return false;
21930         }
21931         
21932         
21933         
21934         
21935         
21936         if (!node.previousSibling) {
21937             return false;
21938         }
21939         var ps = node.previousSibling;
21940         
21941         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21942             ps = ps.previousSibling;
21943         }
21944         if (!ps || ps.nodeType != 1) {
21945             return false;
21946         }
21947         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21948         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21949             return false;
21950         }
21951         
21952         node.parentNode.removeChild(node); // remove me...
21953         
21954         return false; // no need to do children
21955
21956     }
21957     
21958 }); 
21959
21960 /**
21961  * @class Roo.htmleditor.FilterBlock
21962  * removes id / data-block and contenteditable that are associated with blocks
21963  * usage should be done on a cloned copy of the dom
21964  * @constructor
21965 * Run a new Attribute Filter { node : xxxx }}
21966 * @param {Object} config Configuration options
21967  */
21968 Roo.htmleditor.FilterBlock = function(cfg)
21969 {
21970     Roo.apply(this, cfg);
21971     var qa = cfg.node.querySelectorAll;
21972     this.removeAttributes('data-block');
21973     this.removeAttributes('contenteditable');
21974     this.removeAttributes('id');
21975     
21976 }
21977
21978 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21979 {
21980     node: true, // all tags
21981      
21982      
21983     removeAttributes : function(attr)
21984     {
21985         var ar = this.node.querySelectorAll('*[' + attr + ']');
21986         for (var i =0;i<ar.length;i++) {
21987             ar[i].removeAttribute(attr);
21988         }
21989     }
21990         
21991         
21992         
21993     
21994 });
21995 /***
21996  * This is based loosely on tinymce 
21997  * @class Roo.htmleditor.TidySerializer
21998  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21999  * @constructor
22000  * @method Serializer
22001  * @param {Object} settings Name/value settings object.
22002  */
22003
22004
22005 Roo.htmleditor.TidySerializer = function(settings)
22006 {
22007     Roo.apply(this, settings);
22008     
22009     this.writer = new Roo.htmleditor.TidyWriter(settings);
22010     
22011     
22012
22013 };
22014 Roo.htmleditor.TidySerializer.prototype = {
22015     
22016     /**
22017      * @param {boolean} inner do the inner of the node.
22018      */
22019     inner : false,
22020     
22021     writer : false,
22022     
22023     /**
22024     * Serializes the specified node into a string.
22025     *
22026     * @example
22027     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
22028     * @method serialize
22029     * @param {DomElement} node Node instance to serialize.
22030     * @return {String} String with HTML based on DOM tree.
22031     */
22032     serialize : function(node) {
22033         
22034         // = settings.validate;
22035         var writer = this.writer;
22036         var self  = this;
22037         this.handlers = {
22038             // #text
22039             3: function(node) {
22040                 
22041                 writer.text(node.nodeValue, node);
22042             },
22043             // #comment
22044             8: function(node) {
22045                 writer.comment(node.nodeValue);
22046             },
22047             // Processing instruction
22048             7: function(node) {
22049                 writer.pi(node.name, node.nodeValue);
22050             },
22051             // Doctype
22052             10: function(node) {
22053                 writer.doctype(node.nodeValue);
22054             },
22055             // CDATA
22056             4: function(node) {
22057                 writer.cdata(node.nodeValue);
22058             },
22059             // Document fragment
22060             11: function(node) {
22061                 node = node.firstChild;
22062                 if (!node) {
22063                     return;
22064                 }
22065                 while(node) {
22066                     self.walk(node);
22067                     node = node.nextSibling
22068                 }
22069             }
22070         };
22071         writer.reset();
22072         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
22073         return writer.getContent();
22074     },
22075
22076     walk: function(node)
22077     {
22078         var attrName, attrValue, sortedAttrs, i, l, elementRule,
22079             handler = this.handlers[node.nodeType];
22080             
22081         if (handler) {
22082             handler(node);
22083             return;
22084         }
22085     
22086         var name = node.nodeName;
22087         var isEmpty = node.childNodes.length < 1;
22088       
22089         var writer = this.writer;
22090         var attrs = node.attributes;
22091         // Sort attributes
22092         
22093         writer.start(node.nodeName, attrs, isEmpty, node);
22094         if (isEmpty) {
22095             return;
22096         }
22097         node = node.firstChild;
22098         if (!node) {
22099             writer.end(name);
22100             return;
22101         }
22102         while (node) {
22103             this.walk(node);
22104             node = node.nextSibling;
22105         }
22106         writer.end(name);
22107         
22108     
22109     }
22110     // Serialize element and treat all non elements as fragments
22111    
22112 }; 
22113
22114 /***
22115  * This is based loosely on tinymce 
22116  * @class Roo.htmleditor.TidyWriter
22117  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22118  *
22119  * Known issues?
22120  * - not tested much with 'PRE' formated elements.
22121  * 
22122  *
22123  *
22124  */
22125
22126 Roo.htmleditor.TidyWriter = function(settings)
22127 {
22128     
22129     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
22130     Roo.apply(this, settings);
22131     this.html = [];
22132     this.state = [];
22133      
22134     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
22135   
22136 }
22137 Roo.htmleditor.TidyWriter.prototype = {
22138
22139  
22140     state : false,
22141     
22142     indent :  '  ',
22143     
22144     // part of state...
22145     indentstr : '',
22146     in_pre: false,
22147     in_inline : false,
22148     last_inline : false,
22149     encode : false,
22150      
22151     
22152             /**
22153     * Writes the a start element such as <p id="a">.
22154     *
22155     * @method start
22156     * @param {String} name Name of the element.
22157     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22158     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22159     */
22160     start: function(name, attrs, empty, node)
22161     {
22162         var i, l, attr, value;
22163         
22164         // there are some situations where adding line break && indentation will not work. will not work.
22165         // <span / b / i ... formating?
22166         
22167         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22168         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22169         
22170         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22171         
22172         var add_lb = name == 'BR' ? false : in_inline;
22173         
22174         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22175             i_inline = false;
22176         }
22177
22178         var indentstr =  this.indentstr;
22179         
22180         // e_inline = elements that can be inline, but still allow \n before and after?
22181         // only 'BR' ??? any others?
22182         
22183         // ADD LINE BEFORE tage
22184         if (!this.in_pre) {
22185             if (in_inline) {
22186                 //code
22187                 if (name == 'BR') {
22188                     this.addLine();
22189                 } else if (this.lastElementEndsWS()) {
22190                     this.addLine();
22191                 } else{
22192                     // otherwise - no new line. (and dont indent.)
22193                     indentstr = '';
22194                 }
22195                 
22196             } else {
22197                 this.addLine();
22198             }
22199         } else {
22200             indentstr = '';
22201         }
22202         
22203         this.html.push(indentstr + '<', name.toLowerCase());
22204         
22205         if (attrs) {
22206             for (i = 0, l = attrs.length; i < l; i++) {
22207                 attr = attrs[i];
22208                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22209             }
22210         }
22211      
22212         if (empty) {
22213             if (is_short) {
22214                 this.html[this.html.length] = '/>';
22215             } else {
22216                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22217             }
22218             var e_inline = name == 'BR' ? false : this.in_inline;
22219             
22220             if (!e_inline && !this.in_pre) {
22221                 this.addLine();
22222             }
22223             return;
22224         
22225         }
22226         // not empty..
22227         this.html[this.html.length] = '>';
22228         
22229         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22230         /*
22231         if (!in_inline && !in_pre) {
22232             var cn = node.firstChild;
22233             while(cn) {
22234                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22235                     in_inline = true
22236                     break;
22237                 }
22238                 cn = cn.nextSibling;
22239             }
22240              
22241         }
22242         */
22243         
22244         
22245         this.pushState({
22246             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22247             in_pre : in_pre,
22248             in_inline :  in_inline
22249         });
22250         // add a line after if we are not in a
22251         
22252         if (!in_inline && !in_pre) {
22253             this.addLine();
22254         }
22255         
22256             
22257          
22258         
22259     },
22260     
22261     lastElementEndsWS : function()
22262     {
22263         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22264         if (value === false) {
22265             return true;
22266         }
22267         return value.match(/\s+$/);
22268         
22269     },
22270     
22271     /**
22272      * Writes the a end element such as </p>.
22273      *
22274      * @method end
22275      * @param {String} name Name of the element.
22276      */
22277     end: function(name) {
22278         var value;
22279         this.popState();
22280         var indentstr = '';
22281         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22282         
22283         if (!this.in_pre && !in_inline) {
22284             this.addLine();
22285             indentstr  = this.indentstr;
22286         }
22287         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22288         this.last_inline = in_inline;
22289         
22290         // pop the indent state..
22291     },
22292     /**
22293      * Writes a text node.
22294      *
22295      * In pre - we should not mess with the contents.
22296      * 
22297      *
22298      * @method text
22299      * @param {String} text String to write out.
22300      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22301      */
22302     text: function(in_text, node)
22303     {
22304         // if not in whitespace critical
22305         if (in_text.length < 1) {
22306             return;
22307         }
22308         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
22309         
22310         if (this.in_pre) {
22311             this.html[this.html.length] =  text;
22312             return;   
22313         }
22314         
22315         if (this.in_inline) {
22316             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22317             if (text != ' ') {
22318                 text = text.replace(/\s+/,' ');  // all white space to single white space
22319                 
22320                     
22321                 // if next tag is '<BR>', then we can trim right..
22322                 if (node.nextSibling &&
22323                     node.nextSibling.nodeType == 1 &&
22324                     node.nextSibling.nodeName == 'BR' )
22325                 {
22326                     text = text.replace(/\s+$/g,'');
22327                 }
22328                 // if previous tag was a BR, we can also trim..
22329                 if (node.previousSibling &&
22330                     node.previousSibling.nodeType == 1 &&
22331                     node.previousSibling.nodeName == 'BR' )
22332                 {
22333                     text = this.indentstr +  text.replace(/^\s+/g,'');
22334                 }
22335                 if (text.match(/\n/)) {
22336                     text = text.replace(
22337                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22338                     );
22339                     // remoeve the last whitespace / line break.
22340                     text = text.replace(/\n\s+$/,'');
22341                 }
22342                 // repace long lines
22343                 
22344             }
22345              
22346             this.html[this.html.length] =  text;
22347             return;   
22348         }
22349         // see if previous element was a inline element.
22350         var indentstr = this.indentstr;
22351    
22352         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22353         
22354         // should trim left?
22355         if (node.previousSibling &&
22356             node.previousSibling.nodeType == 1 &&
22357             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22358         {
22359             indentstr = '';
22360             
22361         } else {
22362             this.addLine();
22363             text = text.replace(/^\s+/,''); // trim left
22364           
22365         }
22366         // should trim right?
22367         if (node.nextSibling &&
22368             node.nextSibling.nodeType == 1 &&
22369             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22370         {
22371           // noop
22372             
22373         }  else {
22374             text = text.replace(/\s+$/,''); // trim right
22375         }
22376          
22377               
22378         
22379         
22380         
22381         if (text.length < 1) {
22382             return;
22383         }
22384         if (!text.match(/\n/)) {
22385             this.html.push(indentstr + text);
22386             return;
22387         }
22388         
22389         text = this.indentstr + text.replace(
22390             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22391         );
22392         // remoeve the last whitespace / line break.
22393         text = text.replace(/\s+$/,''); 
22394         
22395         this.html.push(text);
22396         
22397         // split and indent..
22398         
22399         
22400     },
22401     /**
22402      * Writes a cdata node such as <![CDATA[data]]>.
22403      *
22404      * @method cdata
22405      * @param {String} text String to write out inside the cdata.
22406      */
22407     cdata: function(text) {
22408         this.html.push('<![CDATA[', text, ']]>');
22409     },
22410     /**
22411     * Writes a comment node such as <!-- Comment -->.
22412     *
22413     * @method cdata
22414     * @param {String} text String to write out inside the comment.
22415     */
22416    comment: function(text) {
22417        this.html.push('<!--', text, '-->');
22418    },
22419     /**
22420      * Writes a PI node such as <?xml attr="value" ?>.
22421      *
22422      * @method pi
22423      * @param {String} name Name of the pi.
22424      * @param {String} text String to write out inside the pi.
22425      */
22426     pi: function(name, text) {
22427         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22428         this.indent != '' && this.html.push('\n');
22429     },
22430     /**
22431      * Writes a doctype node such as <!DOCTYPE data>.
22432      *
22433      * @method doctype
22434      * @param {String} text String to write out inside the doctype.
22435      */
22436     doctype: function(text) {
22437         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22438     },
22439     /**
22440      * Resets the internal buffer if one wants to reuse the writer.
22441      *
22442      * @method reset
22443      */
22444     reset: function() {
22445         this.html.length = 0;
22446         this.state = [];
22447         this.pushState({
22448             indentstr : '',
22449             in_pre : false, 
22450             in_inline : false
22451         })
22452     },
22453     /**
22454      * Returns the contents that got serialized.
22455      *
22456      * @method getContent
22457      * @return {String} HTML contents that got written down.
22458      */
22459     getContent: function() {
22460         return this.html.join('').replace(/\n$/, '');
22461     },
22462     
22463     pushState : function(cfg)
22464     {
22465         this.state.push(cfg);
22466         Roo.apply(this, cfg);
22467     },
22468     
22469     popState : function()
22470     {
22471         if (this.state.length < 1) {
22472             return; // nothing to push
22473         }
22474         var cfg = {
22475             in_pre: false,
22476             indentstr : ''
22477         };
22478         this.state.pop();
22479         if (this.state.length > 0) {
22480             cfg = this.state[this.state.length-1]; 
22481         }
22482         Roo.apply(this, cfg);
22483     },
22484     
22485     addLine: function()
22486     {
22487         if (this.html.length < 1) {
22488             return;
22489         }
22490         
22491         
22492         var value = this.html[this.html.length - 1];
22493         if (value.length > 0 && '\n' !== value) {
22494             this.html.push('\n');
22495         }
22496     }
22497     
22498     
22499 //'pre script noscript style textarea video audio iframe object code'
22500 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22501 // inline 
22502 };
22503
22504 Roo.htmleditor.TidyWriter.inline_elements = [
22505         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22506         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22507 ];
22508 Roo.htmleditor.TidyWriter.shortend_elements = [
22509     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22510     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22511 ];
22512
22513 Roo.htmleditor.TidyWriter.whitespace_elements = [
22514     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22515 ];/***
22516  * This is based loosely on tinymce 
22517  * @class Roo.htmleditor.TidyEntities
22518  * @static
22519  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22520  *
22521  * Not 100% sure this is actually used or needed.
22522  */
22523
22524 Roo.htmleditor.TidyEntities = {
22525     
22526     /**
22527      * initialize data..
22528      */
22529     init : function (){
22530      
22531         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22532        
22533     },
22534
22535
22536     buildEntitiesLookup: function(items, radix) {
22537         var i, chr, entity, lookup = {};
22538         if (!items) {
22539             return {};
22540         }
22541         items = typeof(items) == 'string' ? items.split(',') : items;
22542         radix = radix || 10;
22543         // Build entities lookup table
22544         for (i = 0; i < items.length; i += 2) {
22545             chr = String.fromCharCode(parseInt(items[i], radix));
22546             // Only add non base entities
22547             if (!this.baseEntities[chr]) {
22548                 entity = '&' + items[i + 1] + ';';
22549                 lookup[chr] = entity;
22550                 lookup[entity] = chr;
22551             }
22552         }
22553         return lookup;
22554         
22555     },
22556     
22557     asciiMap : {
22558             128: '€',
22559             130: '‚',
22560             131: 'ƒ',
22561             132: '„',
22562             133: '…',
22563             134: '†',
22564             135: '‡',
22565             136: 'ˆ',
22566             137: '‰',
22567             138: 'Š',
22568             139: '‹',
22569             140: 'Œ',
22570             142: 'Ž',
22571             145: '‘',
22572             146: '’',
22573             147: '“',
22574             148: '”',
22575             149: '•',
22576             150: '–',
22577             151: '—',
22578             152: '˜',
22579             153: '™',
22580             154: 'š',
22581             155: '›',
22582             156: 'œ',
22583             158: 'ž',
22584             159: 'Ÿ'
22585     },
22586     // Raw entities
22587     baseEntities : {
22588         '"': '&quot;',
22589         // Needs to be escaped since the YUI compressor would otherwise break the code
22590         '\'': '&#39;',
22591         '<': '&lt;',
22592         '>': '&gt;',
22593         '&': '&amp;',
22594         '`': '&#96;'
22595     },
22596     // Reverse lookup table for raw entities
22597     reverseEntities : {
22598         '&lt;': '<',
22599         '&gt;': '>',
22600         '&amp;': '&',
22601         '&quot;': '"',
22602         '&apos;': '\''
22603     },
22604     
22605     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22606     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22607     rawCharsRegExp : /[<>&\"\']/g,
22608     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22609     namedEntities  : false,
22610     namedEntitiesData : [ 
22611         '50',
22612         'nbsp',
22613         '51',
22614         'iexcl',
22615         '52',
22616         'cent',
22617         '53',
22618         'pound',
22619         '54',
22620         'curren',
22621         '55',
22622         'yen',
22623         '56',
22624         'brvbar',
22625         '57',
22626         'sect',
22627         '58',
22628         'uml',
22629         '59',
22630         'copy',
22631         '5a',
22632         'ordf',
22633         '5b',
22634         'laquo',
22635         '5c',
22636         'not',
22637         '5d',
22638         'shy',
22639         '5e',
22640         'reg',
22641         '5f',
22642         'macr',
22643         '5g',
22644         'deg',
22645         '5h',
22646         'plusmn',
22647         '5i',
22648         'sup2',
22649         '5j',
22650         'sup3',
22651         '5k',
22652         'acute',
22653         '5l',
22654         'micro',
22655         '5m',
22656         'para',
22657         '5n',
22658         'middot',
22659         '5o',
22660         'cedil',
22661         '5p',
22662         'sup1',
22663         '5q',
22664         'ordm',
22665         '5r',
22666         'raquo',
22667         '5s',
22668         'frac14',
22669         '5t',
22670         'frac12',
22671         '5u',
22672         'frac34',
22673         '5v',
22674         'iquest',
22675         '60',
22676         'Agrave',
22677         '61',
22678         'Aacute',
22679         '62',
22680         'Acirc',
22681         '63',
22682         'Atilde',
22683         '64',
22684         'Auml',
22685         '65',
22686         'Aring',
22687         '66',
22688         'AElig',
22689         '67',
22690         'Ccedil',
22691         '68',
22692         'Egrave',
22693         '69',
22694         'Eacute',
22695         '6a',
22696         'Ecirc',
22697         '6b',
22698         'Euml',
22699         '6c',
22700         'Igrave',
22701         '6d',
22702         'Iacute',
22703         '6e',
22704         'Icirc',
22705         '6f',
22706         'Iuml',
22707         '6g',
22708         'ETH',
22709         '6h',
22710         'Ntilde',
22711         '6i',
22712         'Ograve',
22713         '6j',
22714         'Oacute',
22715         '6k',
22716         'Ocirc',
22717         '6l',
22718         'Otilde',
22719         '6m',
22720         'Ouml',
22721         '6n',
22722         'times',
22723         '6o',
22724         'Oslash',
22725         '6p',
22726         'Ugrave',
22727         '6q',
22728         'Uacute',
22729         '6r',
22730         'Ucirc',
22731         '6s',
22732         'Uuml',
22733         '6t',
22734         'Yacute',
22735         '6u',
22736         'THORN',
22737         '6v',
22738         'szlig',
22739         '70',
22740         'agrave',
22741         '71',
22742         'aacute',
22743         '72',
22744         'acirc',
22745         '73',
22746         'atilde',
22747         '74',
22748         'auml',
22749         '75',
22750         'aring',
22751         '76',
22752         'aelig',
22753         '77',
22754         'ccedil',
22755         '78',
22756         'egrave',
22757         '79',
22758         'eacute',
22759         '7a',
22760         'ecirc',
22761         '7b',
22762         'euml',
22763         '7c',
22764         'igrave',
22765         '7d',
22766         'iacute',
22767         '7e',
22768         'icirc',
22769         '7f',
22770         'iuml',
22771         '7g',
22772         'eth',
22773         '7h',
22774         'ntilde',
22775         '7i',
22776         'ograve',
22777         '7j',
22778         'oacute',
22779         '7k',
22780         'ocirc',
22781         '7l',
22782         'otilde',
22783         '7m',
22784         'ouml',
22785         '7n',
22786         'divide',
22787         '7o',
22788         'oslash',
22789         '7p',
22790         'ugrave',
22791         '7q',
22792         'uacute',
22793         '7r',
22794         'ucirc',
22795         '7s',
22796         'uuml',
22797         '7t',
22798         'yacute',
22799         '7u',
22800         'thorn',
22801         '7v',
22802         'yuml',
22803         'ci',
22804         'fnof',
22805         'sh',
22806         'Alpha',
22807         'si',
22808         'Beta',
22809         'sj',
22810         'Gamma',
22811         'sk',
22812         'Delta',
22813         'sl',
22814         'Epsilon',
22815         'sm',
22816         'Zeta',
22817         'sn',
22818         'Eta',
22819         'so',
22820         'Theta',
22821         'sp',
22822         'Iota',
22823         'sq',
22824         'Kappa',
22825         'sr',
22826         'Lambda',
22827         'ss',
22828         'Mu',
22829         'st',
22830         'Nu',
22831         'su',
22832         'Xi',
22833         'sv',
22834         'Omicron',
22835         't0',
22836         'Pi',
22837         't1',
22838         'Rho',
22839         't3',
22840         'Sigma',
22841         't4',
22842         'Tau',
22843         't5',
22844         'Upsilon',
22845         't6',
22846         'Phi',
22847         't7',
22848         'Chi',
22849         't8',
22850         'Psi',
22851         't9',
22852         'Omega',
22853         'th',
22854         'alpha',
22855         'ti',
22856         'beta',
22857         'tj',
22858         'gamma',
22859         'tk',
22860         'delta',
22861         'tl',
22862         'epsilon',
22863         'tm',
22864         'zeta',
22865         'tn',
22866         'eta',
22867         'to',
22868         'theta',
22869         'tp',
22870         'iota',
22871         'tq',
22872         'kappa',
22873         'tr',
22874         'lambda',
22875         'ts',
22876         'mu',
22877         'tt',
22878         'nu',
22879         'tu',
22880         'xi',
22881         'tv',
22882         'omicron',
22883         'u0',
22884         'pi',
22885         'u1',
22886         'rho',
22887         'u2',
22888         'sigmaf',
22889         'u3',
22890         'sigma',
22891         'u4',
22892         'tau',
22893         'u5',
22894         'upsilon',
22895         'u6',
22896         'phi',
22897         'u7',
22898         'chi',
22899         'u8',
22900         'psi',
22901         'u9',
22902         'omega',
22903         'uh',
22904         'thetasym',
22905         'ui',
22906         'upsih',
22907         'um',
22908         'piv',
22909         '812',
22910         'bull',
22911         '816',
22912         'hellip',
22913         '81i',
22914         'prime',
22915         '81j',
22916         'Prime',
22917         '81u',
22918         'oline',
22919         '824',
22920         'frasl',
22921         '88o',
22922         'weierp',
22923         '88h',
22924         'image',
22925         '88s',
22926         'real',
22927         '892',
22928         'trade',
22929         '89l',
22930         'alefsym',
22931         '8cg',
22932         'larr',
22933         '8ch',
22934         'uarr',
22935         '8ci',
22936         'rarr',
22937         '8cj',
22938         'darr',
22939         '8ck',
22940         'harr',
22941         '8dl',
22942         'crarr',
22943         '8eg',
22944         'lArr',
22945         '8eh',
22946         'uArr',
22947         '8ei',
22948         'rArr',
22949         '8ej',
22950         'dArr',
22951         '8ek',
22952         'hArr',
22953         '8g0',
22954         'forall',
22955         '8g2',
22956         'part',
22957         '8g3',
22958         'exist',
22959         '8g5',
22960         'empty',
22961         '8g7',
22962         'nabla',
22963         '8g8',
22964         'isin',
22965         '8g9',
22966         'notin',
22967         '8gb',
22968         'ni',
22969         '8gf',
22970         'prod',
22971         '8gh',
22972         'sum',
22973         '8gi',
22974         'minus',
22975         '8gn',
22976         'lowast',
22977         '8gq',
22978         'radic',
22979         '8gt',
22980         'prop',
22981         '8gu',
22982         'infin',
22983         '8h0',
22984         'ang',
22985         '8h7',
22986         'and',
22987         '8h8',
22988         'or',
22989         '8h9',
22990         'cap',
22991         '8ha',
22992         'cup',
22993         '8hb',
22994         'int',
22995         '8hk',
22996         'there4',
22997         '8hs',
22998         'sim',
22999         '8i5',
23000         'cong',
23001         '8i8',
23002         'asymp',
23003         '8j0',
23004         'ne',
23005         '8j1',
23006         'equiv',
23007         '8j4',
23008         'le',
23009         '8j5',
23010         'ge',
23011         '8k2',
23012         'sub',
23013         '8k3',
23014         'sup',
23015         '8k4',
23016         'nsub',
23017         '8k6',
23018         'sube',
23019         '8k7',
23020         'supe',
23021         '8kl',
23022         'oplus',
23023         '8kn',
23024         'otimes',
23025         '8l5',
23026         'perp',
23027         '8m5',
23028         'sdot',
23029         '8o8',
23030         'lceil',
23031         '8o9',
23032         'rceil',
23033         '8oa',
23034         'lfloor',
23035         '8ob',
23036         'rfloor',
23037         '8p9',
23038         'lang',
23039         '8pa',
23040         'rang',
23041         '9ea',
23042         'loz',
23043         '9j0',
23044         'spades',
23045         '9j3',
23046         'clubs',
23047         '9j5',
23048         'hearts',
23049         '9j6',
23050         'diams',
23051         'ai',
23052         'OElig',
23053         'aj',
23054         'oelig',
23055         'b0',
23056         'Scaron',
23057         'b1',
23058         'scaron',
23059         'bo',
23060         'Yuml',
23061         'm6',
23062         'circ',
23063         'ms',
23064         'tilde',
23065         '802',
23066         'ensp',
23067         '803',
23068         'emsp',
23069         '809',
23070         'thinsp',
23071         '80c',
23072         'zwnj',
23073         '80d',
23074         'zwj',
23075         '80e',
23076         'lrm',
23077         '80f',
23078         'rlm',
23079         '80j',
23080         'ndash',
23081         '80k',
23082         'mdash',
23083         '80o',
23084         'lsquo',
23085         '80p',
23086         'rsquo',
23087         '80q',
23088         'sbquo',
23089         '80s',
23090         'ldquo',
23091         '80t',
23092         'rdquo',
23093         '80u',
23094         'bdquo',
23095         '810',
23096         'dagger',
23097         '811',
23098         'Dagger',
23099         '81g',
23100         'permil',
23101         '81p',
23102         'lsaquo',
23103         '81q',
23104         'rsaquo',
23105         '85c',
23106         'euro'
23107     ],
23108
23109          
23110     /**
23111      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
23112      *
23113      * @method encodeRaw
23114      * @param {String} text Text to encode.
23115      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23116      * @return {String} Entity encoded text.
23117      */
23118     encodeRaw: function(text, attr)
23119     {
23120         var t = this;
23121         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23122             return t.baseEntities[chr] || chr;
23123         });
23124     },
23125     /**
23126      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
23127      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
23128      * and is exposed as the DOMUtils.encode function.
23129      *
23130      * @method encodeAllRaw
23131      * @param {String} text Text to encode.
23132      * @return {String} Entity encoded text.
23133      */
23134     encodeAllRaw: function(text) {
23135         var t = this;
23136         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
23137             return t.baseEntities[chr] || chr;
23138         });
23139     },
23140     /**
23141      * Encodes the specified string using numeric entities. The core entities will be
23142      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
23143      *
23144      * @method encodeNumeric
23145      * @param {String} text Text to encode.
23146      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23147      * @return {String} Entity encoded text.
23148      */
23149     encodeNumeric: function(text, attr) {
23150         var t = this;
23151         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23152             // Multi byte sequence convert it to a single entity
23153             if (chr.length > 1) {
23154                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23155             }
23156             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23157         });
23158     },
23159     /**
23160      * Encodes the specified string using named entities. The core entities will be encoded
23161      * as named ones but all non lower ascii characters will be encoded into named entities.
23162      *
23163      * @method encodeNamed
23164      * @param {String} text Text to encode.
23165      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23166      * @param {Object} entities Optional parameter with entities to use.
23167      * @return {String} Entity encoded text.
23168      */
23169     encodeNamed: function(text, attr, entities) {
23170         var t = this;
23171         entities = entities || this.namedEntities;
23172         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23173             return t.baseEntities[chr] || entities[chr] || chr;
23174         });
23175     },
23176     /**
23177      * Returns an encode function based on the name(s) and it's optional entities.
23178      *
23179      * @method getEncodeFunc
23180      * @param {String} name Comma separated list of encoders for example named,numeric.
23181      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23182      * @return {function} Encode function to be used.
23183      */
23184     getEncodeFunc: function(name, entities) {
23185         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23186         var t = this;
23187         function encodeNamedAndNumeric(text, attr) {
23188             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23189                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23190             });
23191         }
23192
23193         function encodeCustomNamed(text, attr) {
23194             return t.encodeNamed(text, attr, entities);
23195         }
23196         // Replace + with , to be compatible with previous TinyMCE versions
23197         name = this.makeMap(name.replace(/\+/g, ','));
23198         // Named and numeric encoder
23199         if (name.named && name.numeric) {
23200             return this.encodeNamedAndNumeric;
23201         }
23202         // Named encoder
23203         if (name.named) {
23204             // Custom names
23205             if (entities) {
23206                 return encodeCustomNamed;
23207             }
23208             return this.encodeNamed;
23209         }
23210         // Numeric
23211         if (name.numeric) {
23212             return this.encodeNumeric;
23213         }
23214         // Raw encoder
23215         return this.encodeRaw;
23216     },
23217     /**
23218      * Decodes the specified string, this will replace entities with raw UTF characters.
23219      *
23220      * @method decode
23221      * @param {String} text Text to entity decode.
23222      * @return {String} Entity decoded string.
23223      */
23224     decode: function(text)
23225     {
23226         var  t = this;
23227         return text.replace(this.entityRegExp, function(all, numeric) {
23228             if (numeric) {
23229                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23230                 // Support upper UTF
23231                 if (numeric > 65535) {
23232                     numeric -= 65536;
23233                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23234                 }
23235                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23236             }
23237             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23238         });
23239     },
23240     nativeDecode : function (text) {
23241         return text;
23242     },
23243     makeMap : function (items, delim, map) {
23244                 var i;
23245                 items = items || [];
23246                 delim = delim || ',';
23247                 if (typeof items == "string") {
23248                         items = items.split(delim);
23249                 }
23250                 map = map || {};
23251                 i = items.length;
23252                 while (i--) {
23253                         map[items[i]] = {};
23254                 }
23255                 return map;
23256         }
23257 };
23258     
23259     
23260     
23261 Roo.htmleditor.TidyEntities.init();
23262 /**
23263  * @class Roo.htmleditor.KeyEnter
23264  * Handle Enter press..
23265  * @cfg {Roo.HtmlEditorCore} core the editor.
23266  * @constructor
23267  * Create a new Filter.
23268  * @param {Object} config Configuration options
23269  */
23270
23271
23272
23273
23274
23275 Roo.htmleditor.KeyEnter = function(cfg) {
23276     Roo.apply(this, cfg);
23277     // this does not actually call walk as it's really just a abstract class
23278  
23279     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23280 }
23281
23282 //Roo.htmleditor.KeyEnter.i = 0;
23283
23284
23285 Roo.htmleditor.KeyEnter.prototype = {
23286     
23287     core : false,
23288     
23289     keypress : function(e)
23290     {
23291         if (e.charCode != 13 && e.charCode != 10) {
23292             Roo.log([e.charCode,e]);
23293             return true;
23294         }
23295         e.preventDefault();
23296         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23297         var doc = this.core.doc;
23298           //add a new line
23299        
23300     
23301         var sel = this.core.getSelection();
23302         var range = sel.getRangeAt(0);
23303         var n = range.commonAncestorContainer;
23304         var pc = range.closest([ 'ol', 'ul']);
23305         var pli = range.closest('li');
23306         if (!pc || e.ctrlKey) {
23307             sel.insertNode('br', 'after'); 
23308          
23309             this.core.undoManager.addEvent();
23310             this.core.fireEditorEvent(e);
23311             return false;
23312         }
23313         
23314         // deal with <li> insetion
23315         if (pli.innerText.trim() == '' &&
23316             pli.previousSibling &&
23317             pli.previousSibling.nodeName == 'LI' &&
23318             pli.previousSibling.innerText.trim() ==  '') {
23319             pli.parentNode.removeChild(pli.previousSibling);
23320             sel.cursorAfter(pc);
23321             this.core.undoManager.addEvent();
23322             this.core.fireEditorEvent(e);
23323             return false;
23324         }
23325     
23326         var li = doc.createElement('LI');
23327         li.innerHTML = '&nbsp;';
23328         if (!pli || !pli.firstSibling) {
23329             pc.appendChild(li);
23330         } else {
23331             pli.parentNode.insertBefore(li, pli.firstSibling);
23332         }
23333         sel.cursorText (li.firstChild);
23334       
23335         this.core.undoManager.addEvent();
23336         this.core.fireEditorEvent(e);
23337
23338         return false;
23339         
23340     
23341         
23342         
23343          
23344     }
23345 };
23346      
23347 /**
23348  * @class Roo.htmleditor.Block
23349  * Base class for html editor blocks - do not use it directly .. extend it..
23350  * @cfg {DomElement} node The node to apply stuff to.
23351  * @cfg {String} friendly_name the name that appears in the context bar about this block
23352  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23353  
23354  * @constructor
23355  * Create a new Filter.
23356  * @param {Object} config Configuration options
23357  */
23358
23359 Roo.htmleditor.Block  = function(cfg)
23360 {
23361     // do nothing .. should not be called really.
23362 }
23363 /**
23364  * factory method to get the block from an element (using cache if necessary)
23365  * @static
23366  * @param {HtmlElement} the dom element
23367  */
23368 Roo.htmleditor.Block.factory = function(node)
23369 {
23370     var cc = Roo.htmleditor.Block.cache;
23371     var id = Roo.get(node).id;
23372     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23373         Roo.htmleditor.Block.cache[id].readElement(node);
23374         return Roo.htmleditor.Block.cache[id];
23375     }
23376     var db  = node.getAttribute('data-block');
23377     if (!db) {
23378         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23379     }
23380     var cls = Roo.htmleditor['Block' + db];
23381     if (typeof(cls) == 'undefined') {
23382         //Roo.log(node.getAttribute('data-block'));
23383         Roo.log("OOps missing block : " + 'Block' + db);
23384         return false;
23385     }
23386     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23387     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23388 };
23389
23390 /**
23391  * initalize all Elements from content that are 'blockable'
23392  * @static
23393  * @param the body element
23394  */
23395 Roo.htmleditor.Block.initAll = function(body, type)
23396 {
23397     if (typeof(type) == 'undefined') {
23398         var ia = Roo.htmleditor.Block.initAll;
23399         ia(body,'table');
23400         ia(body,'td');
23401         ia(body,'figure');
23402         return;
23403     }
23404     Roo.each(Roo.get(body).query(type), function(e) {
23405         Roo.htmleditor.Block.factory(e);    
23406     },this);
23407 };
23408 // question goes here... do we need to clear out this cache sometimes?
23409 // or show we make it relivant to the htmleditor.
23410 Roo.htmleditor.Block.cache = {};
23411
23412 Roo.htmleditor.Block.prototype = {
23413     
23414     node : false,
23415     
23416      // used by context menu
23417     friendly_name : 'Based Block',
23418     
23419     // text for button to delete this element
23420     deleteTitle : false,
23421     
23422     context : false,
23423     /**
23424      * Update a node with values from this object
23425      * @param {DomElement} node
23426      */
23427     updateElement : function(node)
23428     {
23429         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23430     },
23431      /**
23432      * convert to plain HTML for calling insertAtCursor..
23433      */
23434     toHTML : function()
23435     {
23436         return Roo.DomHelper.markup(this.toObject());
23437     },
23438     /**
23439      * used by readEleemnt to extract data from a node
23440      * may need improving as it's pretty basic
23441      
23442      * @param {DomElement} node
23443      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23444      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23445      * @param {String} style the style property - eg. text-align
23446      */
23447     getVal : function(node, tag, attr, style)
23448     {
23449         var n = node;
23450         if (tag !== true && n.tagName != tag.toUpperCase()) {
23451             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23452             // but kiss for now.
23453             n = node.getElementsByTagName(tag).item(0);
23454         }
23455         if (!n) {
23456             return '';
23457         }
23458         if (attr === false) {
23459             return n;
23460         }
23461         if (attr == 'html') {
23462             return n.innerHTML;
23463         }
23464         if (attr == 'style') {
23465             return n.style[style]; 
23466         }
23467         
23468         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23469             
23470     },
23471     /**
23472      * create a DomHelper friendly object - for use with 
23473      * Roo.DomHelper.markup / overwrite / etc..
23474      * (override this)
23475      */
23476     toObject : function()
23477     {
23478         return {};
23479     },
23480       /**
23481      * Read a node that has a 'data-block' property - and extract the values from it.
23482      * @param {DomElement} node - the node
23483      */
23484     readElement : function(node)
23485     {
23486         
23487     } 
23488     
23489     
23490 };
23491
23492  
23493
23494 /**
23495  * @class Roo.htmleditor.BlockFigure
23496  * Block that has an image and a figcaption
23497  * @cfg {String} image_src the url for the image
23498  * @cfg {String} align (left|right) alignment for the block default left
23499  * @cfg {String} caption the text to appear below  (and in the alt tag)
23500  * @cfg {String} caption_display (block|none) display or not the caption
23501  * @cfg {String|number} image_width the width of the image number or %?
23502  * @cfg {String|number} image_height the height of the image number or %?
23503  * 
23504  * @constructor
23505  * Create a new Filter.
23506  * @param {Object} config Configuration options
23507  */
23508
23509 Roo.htmleditor.BlockFigure = function(cfg)
23510 {
23511     if (cfg.node) {
23512         this.readElement(cfg.node);
23513         this.updateElement(cfg.node);
23514     }
23515     Roo.apply(this, cfg);
23516 }
23517 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23518  
23519     
23520     // setable values.
23521     image_src: '',
23522     align: 'center',
23523     caption : '',
23524     caption_display : 'block',
23525     width : '100%',
23526     cls : '',
23527     href: '',
23528     video_url : '',
23529     
23530     // margin: '2%', not used
23531     
23532     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23533
23534     
23535     // used by context menu
23536     friendly_name : 'Image with caption',
23537     deleteTitle : "Delete Image and Caption",
23538     
23539     contextMenu : function(toolbar)
23540     {
23541         
23542         var block = function() {
23543             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23544         };
23545         
23546         
23547         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23548         
23549         var syncValue = toolbar.editorcore.syncValue;
23550         
23551         var fields = {};
23552         
23553         return [
23554              {
23555                 xtype : 'TextItem',
23556                 text : "Source: ",
23557                 xns : rooui.Toolbar  //Boostrap?
23558             },
23559             {
23560                 xtype : 'Button',
23561                 text: 'Change Image URL',
23562                  
23563                 listeners : {
23564                     click: function (btn, state)
23565                     {
23566                         var b = block();
23567                         
23568                         Roo.MessageBox.show({
23569                             title : "Image Source URL",
23570                             msg : "Enter the url for the image",
23571                             buttons: Roo.MessageBox.OKCANCEL,
23572                             fn: function(btn, val){
23573                                 if (btn != 'ok') {
23574                                     return;
23575                                 }
23576                                 b.image_src = val;
23577                                 b.updateElement();
23578                                 syncValue();
23579                                 toolbar.editorcore.onEditorEvent();
23580                             },
23581                             minWidth:250,
23582                             prompt:true,
23583                             //multiline: multiline,
23584                             modal : true,
23585                             value : b.image_src
23586                         });
23587                     }
23588                 },
23589                 xns : rooui.Toolbar
23590             },
23591          
23592             {
23593                 xtype : 'Button',
23594                 text: 'Change Link URL',
23595                  
23596                 listeners : {
23597                     click: function (btn, state)
23598                     {
23599                         var b = block();
23600                         
23601                         Roo.MessageBox.show({
23602                             title : "Link URL",
23603                             msg : "Enter the url for the link - leave blank to have no link",
23604                             buttons: Roo.MessageBox.OKCANCEL,
23605                             fn: function(btn, val){
23606                                 if (btn != 'ok') {
23607                                     return;
23608                                 }
23609                                 b.href = val;
23610                                 b.updateElement();
23611                                 syncValue();
23612                                 toolbar.editorcore.onEditorEvent();
23613                             },
23614                             minWidth:250,
23615                             prompt:true,
23616                             //multiline: multiline,
23617                             modal : true,
23618                             value : b.href
23619                         });
23620                     }
23621                 },
23622                 xns : rooui.Toolbar
23623             },
23624             {
23625                 xtype : 'Button',
23626                 text: 'Show Video URL',
23627                  
23628                 listeners : {
23629                     click: function (btn, state)
23630                     {
23631                         Roo.MessageBox.alert("Video URL",
23632                             block().video_url == '' ? 'This image is not linked ot a video' :
23633                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23634                     }
23635                 },
23636                 xns : rooui.Toolbar
23637             },
23638             
23639             
23640             {
23641                 xtype : 'TextItem',
23642                 text : "Width: ",
23643                 xns : rooui.Toolbar  //Boostrap?
23644             },
23645             {
23646                 xtype : 'ComboBox',
23647                 allowBlank : false,
23648                 displayField : 'val',
23649                 editable : true,
23650                 listWidth : 100,
23651                 triggerAction : 'all',
23652                 typeAhead : true,
23653                 valueField : 'val',
23654                 width : 70,
23655                 name : 'width',
23656                 listeners : {
23657                     select : function (combo, r, index)
23658                     {
23659                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23660                         var b = block();
23661                         b.width = r.get('val');
23662                         b.updateElement();
23663                         syncValue();
23664                         toolbar.editorcore.onEditorEvent();
23665                     }
23666                 },
23667                 xns : rooui.form,
23668                 store : {
23669                     xtype : 'SimpleStore',
23670                     data : [
23671                         ['50%'],
23672                         ['80%'],
23673                         ['100%']
23674                     ],
23675                     fields : [ 'val'],
23676                     xns : Roo.data
23677                 }
23678             },
23679             {
23680                 xtype : 'TextItem',
23681                 text : "Align: ",
23682                 xns : rooui.Toolbar  //Boostrap?
23683             },
23684             {
23685                 xtype : 'ComboBox',
23686                 allowBlank : false,
23687                 displayField : 'val',
23688                 editable : true,
23689                 listWidth : 100,
23690                 triggerAction : 'all',
23691                 typeAhead : true,
23692                 valueField : 'val',
23693                 width : 70,
23694                 name : 'align',
23695                 listeners : {
23696                     select : function (combo, r, index)
23697                     {
23698                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23699                         var b = block();
23700                         b.align = r.get('val');
23701                         b.updateElement();
23702                         syncValue();
23703                         toolbar.editorcore.onEditorEvent();
23704                     }
23705                 },
23706                 xns : rooui.form,
23707                 store : {
23708                     xtype : 'SimpleStore',
23709                     data : [
23710                         ['left'],
23711                         ['right'],
23712                         ['center']
23713                     ],
23714                     fields : [ 'val'],
23715                     xns : Roo.data
23716                 }
23717             },
23718             
23719             
23720             {
23721                 xtype : 'Button',
23722                 text: 'Hide Caption',
23723                 name : 'caption_display',
23724                 pressed : false,
23725                 enableToggle : true,
23726                 setValue : function(v) {
23727                     // this trigger toggle.
23728                      
23729                     this.setText(v ? "Hide Caption" : "Show Caption");
23730                     this.setPressed(v != 'block');
23731                 },
23732                 listeners : {
23733                     toggle: function (btn, state)
23734                     {
23735                         var b  = block();
23736                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23737                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23738                         b.updateElement();
23739                         syncValue();
23740                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23741                         toolbar.editorcore.onEditorEvent();
23742                     }
23743                 },
23744                 xns : rooui.Toolbar
23745             }
23746         ];
23747         
23748     },
23749     /**
23750      * create a DomHelper friendly object - for use with
23751      * Roo.DomHelper.markup / overwrite / etc..
23752      */
23753     toObject : function()
23754     {
23755         var d = document.createElement('div');
23756         d.innerHTML = this.caption;
23757         
23758         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
23759         
23760         var iw = this.align == 'center' ? this.width : '100%';
23761         var img =   {
23762             tag : 'img',
23763             contenteditable : 'false',
23764             src : this.image_src,
23765             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23766             style: {
23767                 width : iw,
23768                 maxWidth : iw + ' !important', // this is not getting rendered?
23769                 margin : m  
23770                 
23771             }
23772         };
23773         /*
23774         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23775                     '<a href="{2}">' + 
23776                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23777                     '</a>' + 
23778                 '</div>',
23779         */
23780                 
23781         if (this.href.length > 0) {
23782             img = {
23783                 tag : 'a',
23784                 href: this.href,
23785                 contenteditable : 'true',
23786                 cn : [
23787                     img
23788                 ]
23789             };
23790         }
23791         
23792         
23793         if (this.video_url.length > 0) {
23794             img = {
23795                 tag : 'div',
23796                 cls : this.cls,
23797                 frameborder : 0,
23798                 allowfullscreen : true,
23799                 width : 420,  // these are for video tricks - that we replace the outer
23800                 height : 315,
23801                 src : this.video_url,
23802                 cn : [
23803                     img
23804                 ]
23805             };
23806         }
23807         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
23808         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
23809         
23810   
23811         var ret =   {
23812             tag: 'figure',
23813             'data-block' : 'Figure',
23814             'data-width' : this.width, 
23815             contenteditable : 'false',
23816             
23817             style : {
23818                 display: 'block',
23819                 float :  this.align ,
23820                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
23821                 width : this.align == 'center' ? '100%' : this.width,
23822                 margin:  '0px',
23823                 padding: this.align == 'center' ? '0' : '0 10px' ,
23824                 textAlign : this.align   // seems to work for email..
23825                 
23826             },
23827            
23828             
23829             align : this.align,
23830             cn : [
23831                 img,
23832               
23833                 {
23834                     tag: 'figcaption',
23835                     'data-display' : this.caption_display,
23836                     style : {
23837                         textAlign : 'left',
23838                         fontSize : '16px',
23839                         lineHeight : '24px',
23840                         display : this.caption_display,
23841                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
23842                         margin: m,
23843                         width: this.align == 'center' ?  this.width : '100%' 
23844                     
23845                          
23846                     },
23847                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23848                     cn : [
23849                         {
23850                             tag: 'div',
23851                             style  : {
23852                                 marginTop : '16px',
23853                                 textAlign : 'left'
23854                             },
23855                             align: 'left',
23856                             cn : [
23857                                 {
23858                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23859                                     tag : 'i',
23860                                     contenteditable : true,
23861                                     html : captionhtml
23862                                 }
23863                                 
23864                             ]
23865                         }
23866                         
23867                     ]
23868                     
23869                 }
23870             ]
23871         };
23872         return ret;
23873          
23874     },
23875     
23876     readElement : function(node)
23877     {
23878         // this should not really come from the link...
23879         this.video_url = this.getVal(node, 'div', 'src');
23880         this.cls = this.getVal(node, 'div', 'class');
23881         this.href = this.getVal(node, 'a', 'href');
23882         
23883         
23884         this.image_src = this.getVal(node, 'img', 'src');
23885          
23886         this.align = this.getVal(node, 'figure', 'align');
23887         var figcaption = this.getVal(node, 'figcaption', false);
23888         if (figcaption !== '') {
23889             this.caption = this.getVal(figcaption, 'i', 'html');
23890         }
23891         
23892
23893         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
23894         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23895         this.width = this.getVal(node, true, 'data-width');
23896         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23897         
23898     },
23899     removeNode : function()
23900     {
23901         return this.node;
23902     }
23903     
23904   
23905    
23906      
23907     
23908     
23909     
23910     
23911 })
23912
23913  
23914
23915 /**
23916  * @class Roo.htmleditor.BlockTable
23917  * Block that manages a table
23918  * 
23919  * @constructor
23920  * Create a new Filter.
23921  * @param {Object} config Configuration options
23922  */
23923
23924 Roo.htmleditor.BlockTable = function(cfg)
23925 {
23926     if (cfg.node) {
23927         this.readElement(cfg.node);
23928         this.updateElement(cfg.node);
23929     }
23930     Roo.apply(this, cfg);
23931     if (!cfg.node) {
23932         this.rows = [];
23933         for(var r = 0; r < this.no_row; r++) {
23934             this.rows[r] = [];
23935             for(var c = 0; c < this.no_col; c++) {
23936                 this.rows[r][c] = this.emptyCell();
23937             }
23938         }
23939     }
23940     
23941     
23942 }
23943 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23944  
23945     rows : false,
23946     no_col : 1,
23947     no_row : 1,
23948     
23949     
23950     width: '100%',
23951     
23952     // used by context menu
23953     friendly_name : 'Table',
23954     deleteTitle : 'Delete Table',
23955     // context menu is drawn once..
23956     
23957     contextMenu : function(toolbar)
23958     {
23959         
23960         var block = function() {
23961             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23962         };
23963         
23964         
23965         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23966         
23967         var syncValue = toolbar.editorcore.syncValue;
23968         
23969         var fields = {};
23970         
23971         return [
23972             {
23973                 xtype : 'TextItem',
23974                 text : "Width: ",
23975                 xns : rooui.Toolbar  //Boostrap?
23976             },
23977             {
23978                 xtype : 'ComboBox',
23979                 allowBlank : false,
23980                 displayField : 'val',
23981                 editable : true,
23982                 listWidth : 100,
23983                 triggerAction : 'all',
23984                 typeAhead : true,
23985                 valueField : 'val',
23986                 width : 100,
23987                 name : 'width',
23988                 listeners : {
23989                     select : function (combo, r, index)
23990                     {
23991                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23992                         var b = block();
23993                         b.width = r.get('val');
23994                         b.updateElement();
23995                         syncValue();
23996                         toolbar.editorcore.onEditorEvent();
23997                     }
23998                 },
23999                 xns : rooui.form,
24000                 store : {
24001                     xtype : 'SimpleStore',
24002                     data : [
24003                         ['100%'],
24004                         ['auto']
24005                     ],
24006                     fields : [ 'val'],
24007                     xns : Roo.data
24008                 }
24009             },
24010             // -------- Cols
24011             
24012             {
24013                 xtype : 'TextItem',
24014                 text : "Columns: ",
24015                 xns : rooui.Toolbar  //Boostrap?
24016             },
24017          
24018             {
24019                 xtype : 'Button',
24020                 text: '-',
24021                 listeners : {
24022                     click : function (_self, e)
24023                     {
24024                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24025                         block().removeColumn();
24026                         syncValue();
24027                         toolbar.editorcore.onEditorEvent();
24028                     }
24029                 },
24030                 xns : rooui.Toolbar
24031             },
24032             {
24033                 xtype : 'Button',
24034                 text: '+',
24035                 listeners : {
24036                     click : function (_self, e)
24037                     {
24038                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24039                         block().addColumn();
24040                         syncValue();
24041                         toolbar.editorcore.onEditorEvent();
24042                     }
24043                 },
24044                 xns : rooui.Toolbar
24045             },
24046             // -------- ROWS
24047             {
24048                 xtype : 'TextItem',
24049                 text : "Rows: ",
24050                 xns : rooui.Toolbar  //Boostrap?
24051             },
24052          
24053             {
24054                 xtype : 'Button',
24055                 text: '-',
24056                 listeners : {
24057                     click : function (_self, e)
24058                     {
24059                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24060                         block().removeRow();
24061                         syncValue();
24062                         toolbar.editorcore.onEditorEvent();
24063                     }
24064                 },
24065                 xns : rooui.Toolbar
24066             },
24067             {
24068                 xtype : 'Button',
24069                 text: '+',
24070                 listeners : {
24071                     click : function (_self, e)
24072                     {
24073                         block().addRow();
24074                         syncValue();
24075                         toolbar.editorcore.onEditorEvent();
24076                     }
24077                 },
24078                 xns : rooui.Toolbar
24079             },
24080             // -------- ROWS
24081             {
24082                 xtype : 'Button',
24083                 text: 'Reset Column Widths',
24084                 listeners : {
24085                     
24086                     click : function (_self, e)
24087                     {
24088                         block().resetWidths();
24089                         syncValue();
24090                         toolbar.editorcore.onEditorEvent();
24091                     }
24092                 },
24093                 xns : rooui.Toolbar
24094             } 
24095             
24096             
24097             
24098         ];
24099         
24100     },
24101     
24102     
24103   /**
24104      * create a DomHelper friendly object - for use with
24105      * Roo.DomHelper.markup / overwrite / etc..
24106      * ?? should it be called with option to hide all editing features?
24107      */
24108     toObject : function()
24109     {
24110         
24111         var ret = {
24112             tag : 'table',
24113             contenteditable : 'false', // this stops cell selection from picking the table.
24114             'data-block' : 'Table',
24115             style : {
24116                 width:  this.width,
24117                 border : 'solid 1px #000', // ??? hard coded?
24118                 'border-collapse' : 'collapse' 
24119             },
24120             cn : [
24121                 { tag : 'tbody' , cn : [] }
24122             ]
24123         };
24124         
24125         // do we have a head = not really 
24126         var ncols = 0;
24127         Roo.each(this.rows, function( row ) {
24128             var tr = {
24129                 tag: 'tr',
24130                 style : {
24131                     margin: '6px',
24132                     border : 'solid 1px #000',
24133                     textAlign : 'left' 
24134                 },
24135                 cn : [ ]
24136             };
24137             
24138             ret.cn[0].cn.push(tr);
24139             // does the row have any properties? ?? height?
24140             var nc = 0;
24141             Roo.each(row, function( cell ) {
24142                 
24143                 var td = {
24144                     tag : 'td',
24145                     contenteditable :  'true',
24146                     'data-block' : 'Td',
24147                     html : cell.html,
24148                     style : cell.style
24149                 };
24150                 if (cell.colspan > 1) {
24151                     td.colspan = cell.colspan ;
24152                     nc += cell.colspan;
24153                 } else {
24154                     nc++;
24155                 }
24156                 if (cell.rowspan > 1) {
24157                     td.rowspan = cell.rowspan ;
24158                 }
24159                 
24160                 
24161                 // widths ?
24162                 tr.cn.push(td);
24163                     
24164                 
24165             }, this);
24166             ncols = Math.max(nc, ncols);
24167             
24168             
24169         }, this);
24170         // add the header row..
24171         
24172         ncols++;
24173          
24174         
24175         return ret;
24176          
24177     },
24178     
24179     readElement : function(node)
24180     {
24181         node  = node ? node : this.node ;
24182         this.width = this.getVal(node, true, 'style', 'width') || '100%';
24183         
24184         this.rows = [];
24185         this.no_row = 0;
24186         var trs = Array.from(node.rows);
24187         trs.forEach(function(tr) {
24188             var row =  [];
24189             this.rows.push(row);
24190             
24191             this.no_row++;
24192             var no_column = 0;
24193             Array.from(tr.cells).forEach(function(td) {
24194                 
24195                 var add = {
24196                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24197                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24198                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24199                     html : td.innerHTML
24200                 };
24201                 no_column += add.colspan;
24202                      
24203                 
24204                 row.push(add);
24205                 
24206                 
24207             },this);
24208             this.no_col = Math.max(this.no_col, no_column);
24209             
24210             
24211         },this);
24212         
24213         
24214     },
24215     normalizeRows: function()
24216     {
24217         var ret= [];
24218         var rid = -1;
24219         this.rows.forEach(function(row) {
24220             rid++;
24221             ret[rid] = [];
24222             row = this.normalizeRow(row);
24223             var cid = 0;
24224             row.forEach(function(c) {
24225                 while (typeof(ret[rid][cid]) != 'undefined') {
24226                     cid++;
24227                 }
24228                 if (typeof(ret[rid]) == 'undefined') {
24229                     ret[rid] = [];
24230                 }
24231                 ret[rid][cid] = c;
24232                 c.row = rid;
24233                 c.col = cid;
24234                 if (c.rowspan < 2) {
24235                     return;
24236                 }
24237                 
24238                 for(var i = 1 ;i < c.rowspan; i++) {
24239                     if (typeof(ret[rid+i]) == 'undefined') {
24240                         ret[rid+i] = [];
24241                     }
24242                     ret[rid+i][cid] = c;
24243                 }
24244             });
24245         }, this);
24246         return ret;
24247     
24248     },
24249     
24250     normalizeRow: function(row)
24251     {
24252         var ret= [];
24253         row.forEach(function(c) {
24254             if (c.colspan < 2) {
24255                 ret.push(c);
24256                 return;
24257             }
24258             for(var i =0 ;i < c.colspan; i++) {
24259                 ret.push(c);
24260             }
24261         });
24262         return ret;
24263     
24264     },
24265     
24266     deleteColumn : function(sel)
24267     {
24268         if (!sel || sel.type != 'col') {
24269             return;
24270         }
24271         if (this.no_col < 2) {
24272             return;
24273         }
24274         
24275         this.rows.forEach(function(row) {
24276             var cols = this.normalizeRow(row);
24277             var col = cols[sel.col];
24278             if (col.colspan > 1) {
24279                 col.colspan --;
24280             } else {
24281                 row.remove(col);
24282             }
24283             
24284         }, this);
24285         this.no_col--;
24286         
24287     },
24288     removeColumn : function()
24289     {
24290         this.deleteColumn({
24291             type: 'col',
24292             col : this.no_col-1
24293         });
24294         this.updateElement();
24295     },
24296     
24297      
24298     addColumn : function()
24299     {
24300         
24301         this.rows.forEach(function(row) {
24302             row.push(this.emptyCell());
24303            
24304         }, this);
24305         this.updateElement();
24306     },
24307     
24308     deleteRow : function(sel)
24309     {
24310         if (!sel || sel.type != 'row') {
24311             return;
24312         }
24313         
24314         if (this.no_row < 2) {
24315             return;
24316         }
24317         
24318         var rows = this.normalizeRows();
24319         
24320         
24321         rows[sel.row].forEach(function(col) {
24322             if (col.rowspan > 1) {
24323                 col.rowspan--;
24324             } else {
24325                 col.remove = 1; // flage it as removed.
24326             }
24327             
24328         }, this);
24329         var newrows = [];
24330         this.rows.forEach(function(row) {
24331             newrow = [];
24332             row.forEach(function(c) {
24333                 if (typeof(c.remove) == 'undefined') {
24334                     newrow.push(c);
24335                 }
24336                 
24337             });
24338             if (newrow.length > 0) {
24339                 newrows.push(row);
24340             }
24341         });
24342         this.rows =  newrows;
24343         
24344         
24345         
24346         this.no_row--;
24347         this.updateElement();
24348         
24349     },
24350     removeRow : function()
24351     {
24352         this.deleteRow({
24353             type: 'row',
24354             row : this.no_row-1
24355         });
24356         
24357     },
24358     
24359      
24360     addRow : function()
24361     {
24362         
24363         var row = [];
24364         for (var i = 0; i < this.no_col; i++ ) {
24365             
24366             row.push(this.emptyCell());
24367            
24368         }
24369         this.rows.push(row);
24370         this.updateElement();
24371         
24372     },
24373      
24374     // the default cell object... at present...
24375     emptyCell : function() {
24376         return (new Roo.htmleditor.BlockTd({})).toObject();
24377         
24378      
24379     },
24380     
24381     removeNode : function()
24382     {
24383         return this.node;
24384     },
24385     
24386     
24387     
24388     resetWidths : function()
24389     {
24390         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24391             var nn = Roo.htmleditor.Block.factory(n);
24392             nn.width = '';
24393             nn.updateElement(n);
24394         });
24395     }
24396     
24397     
24398     
24399     
24400 })
24401
24402 /**
24403  *
24404  * editing a TD?
24405  *
24406  * since selections really work on the table cell, then editing really should work from there
24407  *
24408  * The original plan was to support merging etc... - but that may not be needed yet..
24409  *
24410  * So this simple version will support:
24411  *   add/remove cols
24412  *   adjust the width +/-
24413  *   reset the width...
24414  *   
24415  *
24416  */
24417
24418
24419  
24420
24421 /**
24422  * @class Roo.htmleditor.BlockTable
24423  * Block that manages a table
24424  * 
24425  * @constructor
24426  * Create a new Filter.
24427  * @param {Object} config Configuration options
24428  */
24429
24430 Roo.htmleditor.BlockTd = function(cfg)
24431 {
24432     if (cfg.node) {
24433         this.readElement(cfg.node);
24434         this.updateElement(cfg.node);
24435     }
24436     Roo.apply(this, cfg);
24437      
24438     
24439     
24440 }
24441 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24442  
24443     node : false,
24444     
24445     width: '',
24446     textAlign : 'left',
24447     valign : 'top',
24448     
24449     colspan : 1,
24450     rowspan : 1,
24451     
24452     
24453     // used by context menu
24454     friendly_name : 'Table Cell',
24455     deleteTitle : false, // use our customer delete
24456     
24457     // context menu is drawn once..
24458     
24459     contextMenu : function(toolbar)
24460     {
24461         
24462         var cell = function() {
24463             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24464         };
24465         
24466         var table = function() {
24467             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24468         };
24469         
24470         var lr = false;
24471         var saveSel = function()
24472         {
24473             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24474         }
24475         var restoreSel = function()
24476         {
24477             if (lr) {
24478                 (function() {
24479                     toolbar.editorcore.focus();
24480                     var cr = toolbar.editorcore.getSelection();
24481                     cr.removeAllRanges();
24482                     cr.addRange(lr);
24483                     toolbar.editorcore.onEditorEvent();
24484                 }).defer(10, this);
24485                 
24486                 
24487             }
24488         }
24489         
24490         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24491         
24492         var syncValue = toolbar.editorcore.syncValue;
24493         
24494         var fields = {};
24495         
24496         return [
24497             {
24498                 xtype : 'Button',
24499                 text : 'Edit Table',
24500                 listeners : {
24501                     click : function() {
24502                         var t = toolbar.tb.selectedNode.closest('table');
24503                         toolbar.editorcore.selectNode(t);
24504                         toolbar.editorcore.onEditorEvent();                        
24505                     }
24506                 }
24507                 
24508             },
24509               
24510            
24511              
24512             {
24513                 xtype : 'TextItem',
24514                 text : "Column Width: ",
24515                  xns : rooui.Toolbar 
24516                
24517             },
24518             {
24519                 xtype : 'Button',
24520                 text: '-',
24521                 listeners : {
24522                     click : function (_self, e)
24523                     {
24524                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24525                         cell().shrinkColumn();
24526                         syncValue();
24527                          toolbar.editorcore.onEditorEvent();
24528                     }
24529                 },
24530                 xns : rooui.Toolbar
24531             },
24532             {
24533                 xtype : 'Button',
24534                 text: '+',
24535                 listeners : {
24536                     click : function (_self, e)
24537                     {
24538                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24539                         cell().growColumn();
24540                         syncValue();
24541                         toolbar.editorcore.onEditorEvent();
24542                     }
24543                 },
24544                 xns : rooui.Toolbar
24545             },
24546             
24547             {
24548                 xtype : 'TextItem',
24549                 text : "Vertical Align: ",
24550                 xns : rooui.Toolbar  //Boostrap?
24551             },
24552             {
24553                 xtype : 'ComboBox',
24554                 allowBlank : false,
24555                 displayField : 'val',
24556                 editable : true,
24557                 listWidth : 100,
24558                 triggerAction : 'all',
24559                 typeAhead : true,
24560                 valueField : 'val',
24561                 width : 100,
24562                 name : 'valign',
24563                 listeners : {
24564                     select : function (combo, r, index)
24565                     {
24566                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24567                         var b = cell();
24568                         b.valign = r.get('val');
24569                         b.updateElement();
24570                         syncValue();
24571                         toolbar.editorcore.onEditorEvent();
24572                     }
24573                 },
24574                 xns : rooui.form,
24575                 store : {
24576                     xtype : 'SimpleStore',
24577                     data : [
24578                         ['top'],
24579                         ['middle'],
24580                         ['bottom'] // there are afew more... 
24581                     ],
24582                     fields : [ 'val'],
24583                     xns : Roo.data
24584                 }
24585             },
24586             
24587             {
24588                 xtype : 'TextItem',
24589                 text : "Merge Cells: ",
24590                  xns : rooui.Toolbar 
24591                
24592             },
24593             
24594             
24595             {
24596                 xtype : 'Button',
24597                 text: 'Right',
24598                 listeners : {
24599                     click : function (_self, e)
24600                     {
24601                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24602                         cell().mergeRight();
24603                         //block().growColumn();
24604                         syncValue();
24605                         toolbar.editorcore.onEditorEvent();
24606                     }
24607                 },
24608                 xns : rooui.Toolbar
24609             },
24610              
24611             {
24612                 xtype : 'Button',
24613                 text: 'Below',
24614                 listeners : {
24615                     click : function (_self, e)
24616                     {
24617                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24618                         cell().mergeBelow();
24619                         //block().growColumn();
24620                         syncValue();
24621                         toolbar.editorcore.onEditorEvent();
24622                     }
24623                 },
24624                 xns : rooui.Toolbar
24625             },
24626             {
24627                 xtype : 'TextItem',
24628                 text : "| ",
24629                  xns : rooui.Toolbar 
24630                
24631             },
24632             
24633             {
24634                 xtype : 'Button',
24635                 text: 'Split',
24636                 listeners : {
24637                     click : function (_self, e)
24638                     {
24639                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24640                         cell().split();
24641                         syncValue();
24642                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24643                         toolbar.editorcore.onEditorEvent();
24644                                              
24645                     }
24646                 },
24647                 xns : rooui.Toolbar
24648             },
24649             {
24650                 xtype : 'Fill',
24651                 xns : rooui.Toolbar 
24652                
24653             },
24654         
24655           
24656             {
24657                 xtype : 'Button',
24658                 text: 'Delete',
24659                  
24660                 xns : rooui.Toolbar,
24661                 menu : {
24662                     xtype : 'Menu',
24663                     xns : rooui.menu,
24664                     items : [
24665                         {
24666                             xtype : 'Item',
24667                             html: 'Column',
24668                             listeners : {
24669                                 click : function (_self, e)
24670                                 {
24671                                     var t = table();
24672                                     
24673                                     cell().deleteColumn();
24674                                     syncValue();
24675                                     toolbar.editorcore.selectNode(t.node);
24676                                     toolbar.editorcore.onEditorEvent();   
24677                                 }
24678                             },
24679                             xns : rooui.menu
24680                         },
24681                         {
24682                             xtype : 'Item',
24683                             html: 'Row',
24684                             listeners : {
24685                                 click : function (_self, e)
24686                                 {
24687                                     var t = table();
24688                                     cell().deleteRow();
24689                                     syncValue();
24690                                     
24691                                     toolbar.editorcore.selectNode(t.node);
24692                                     toolbar.editorcore.onEditorEvent();   
24693                                                          
24694                                 }
24695                             },
24696                             xns : rooui.menu
24697                         },
24698                        {
24699                             xtype : 'Separator',
24700                             xns : rooui.menu
24701                         },
24702                         {
24703                             xtype : 'Item',
24704                             html: 'Table',
24705                             listeners : {
24706                                 click : function (_self, e)
24707                                 {
24708                                     var t = table();
24709                                     var nn = t.node.nextSibling || t.node.previousSibling;
24710                                     t.node.parentNode.removeChild(t.node);
24711                                     if (nn) { 
24712                                         toolbar.editorcore.selectNode(nn, true);
24713                                     }
24714                                     toolbar.editorcore.onEditorEvent();   
24715                                                          
24716                                 }
24717                             },
24718                             xns : rooui.menu
24719                         }
24720                     ]
24721                 }
24722             }
24723             
24724             // align... << fixme
24725             
24726         ];
24727         
24728     },
24729     
24730     
24731   /**
24732      * create a DomHelper friendly object - for use with
24733      * Roo.DomHelper.markup / overwrite / etc..
24734      * ?? should it be called with option to hide all editing features?
24735      */
24736  /**
24737      * create a DomHelper friendly object - for use with
24738      * Roo.DomHelper.markup / overwrite / etc..
24739      * ?? should it be called with option to hide all editing features?
24740      */
24741     toObject : function()
24742     {
24743         
24744         var ret = {
24745             tag : 'td',
24746             contenteditable : 'true', // this stops cell selection from picking the table.
24747             'data-block' : 'Td',
24748             valign : this.valign,
24749             style : {  
24750                 'text-align' :  this.textAlign,
24751                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24752                 'border-collapse' : 'collapse',
24753                 padding : '6px', // 8 for desktop / 4 for mobile
24754                 'vertical-align': this.valign
24755             },
24756             html : this.html
24757         };
24758         if (this.width != '') {
24759             ret.width = this.width;
24760             ret.style.width = this.width;
24761         }
24762         
24763         
24764         if (this.colspan > 1) {
24765             ret.colspan = this.colspan ;
24766         } 
24767         if (this.rowspan > 1) {
24768             ret.rowspan = this.rowspan ;
24769         }
24770         
24771            
24772         
24773         return ret;
24774          
24775     },
24776     
24777     readElement : function(node)
24778     {
24779         node  = node ? node : this.node ;
24780         this.width = node.style.width;
24781         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24782         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24783         this.html = node.innerHTML;
24784         
24785         
24786     },
24787      
24788     // the default cell object... at present...
24789     emptyCell : function() {
24790         return {
24791             colspan :  1,
24792             rowspan :  1,
24793             textAlign : 'left',
24794             html : "&nbsp;" // is this going to be editable now?
24795         };
24796      
24797     },
24798     
24799     removeNode : function()
24800     {
24801         return this.node.closest('table');
24802          
24803     },
24804     
24805     cellData : false,
24806     
24807     colWidths : false,
24808     
24809     toTableArray  : function()
24810     {
24811         var ret = [];
24812         var tab = this.node.closest('tr').closest('table');
24813         Array.from(tab.rows).forEach(function(r, ri){
24814             ret[ri] = [];
24815         });
24816         var rn = 0;
24817         this.colWidths = [];
24818         var all_auto = true;
24819         Array.from(tab.rows).forEach(function(r, ri){
24820             
24821             var cn = 0;
24822             Array.from(r.cells).forEach(function(ce, ci){
24823                 var c =  {
24824                     cell : ce,
24825                     row : rn,
24826                     col: cn,
24827                     colspan : ce.colSpan,
24828                     rowspan : ce.rowSpan
24829                 };
24830                 if (ce.isEqualNode(this.node)) {
24831                     this.cellData = c;
24832                 }
24833                 // if we have been filled up by a row?
24834                 if (typeof(ret[rn][cn]) != 'undefined') {
24835                     while(typeof(ret[rn][cn]) != 'undefined') {
24836                         cn++;
24837                     }
24838                     c.col = cn;
24839                 }
24840                 
24841                 if (typeof(this.colWidths[cn]) == 'undefined') {
24842                     this.colWidths[cn] =   ce.style.width;
24843                     if (this.colWidths[cn] != '') {
24844                         all_auto = false;
24845                     }
24846                 }
24847                 
24848                 
24849                 if (c.colspan < 2 && c.rowspan < 2 ) {
24850                     ret[rn][cn] = c;
24851                     cn++;
24852                     return;
24853                 }
24854                 for(var j = 0; j < c.rowspan; j++) {
24855                     if (typeof(ret[rn+j]) == 'undefined') {
24856                         continue; // we have a problem..
24857                     }
24858                     ret[rn+j][cn] = c;
24859                     for(var i = 0; i < c.colspan; i++) {
24860                         ret[rn+j][cn+i] = c;
24861                     }
24862                 }
24863                 
24864                 cn += c.colspan;
24865             }, this);
24866             rn++;
24867         }, this);
24868         
24869         // initalize widths.?
24870         // either all widths or no widths..
24871         if (all_auto) {
24872             this.colWidths[0] = false; // no widths flag.
24873         }
24874         
24875         
24876         return ret;
24877         
24878     },
24879     
24880     
24881     
24882     
24883     mergeRight: function()
24884     {
24885          
24886         // get the contents of the next cell along..
24887         var tr = this.node.closest('tr');
24888         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24889         if (i >= tr.childNodes.length - 1) {
24890             return; // no cells on right to merge with.
24891         }
24892         var table = this.toTableArray();
24893         
24894         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24895             return; // nothing right?
24896         }
24897         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24898         // right cell - must be same rowspan and on the same row.
24899         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24900             return; // right hand side is not same rowspan.
24901         }
24902         
24903         
24904         
24905         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24906         tr.removeChild(rc.cell);
24907         this.colspan += rc.colspan;
24908         this.node.setAttribute('colspan', this.colspan);
24909
24910     },
24911     
24912     
24913     mergeBelow : function()
24914     {
24915         var table = this.toTableArray();
24916         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24917             return; // no row below
24918         }
24919         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24920             return; // nothing right?
24921         }
24922         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24923         
24924         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24925             return; // right hand side is not same rowspan.
24926         }
24927         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24928         rc.cell.parentNode.removeChild(rc.cell);
24929         this.rowspan += rc.rowspan;
24930         this.node.setAttribute('rowspan', this.rowspan);
24931     },
24932     
24933     split: function()
24934     {
24935         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24936             return;
24937         }
24938         var table = this.toTableArray();
24939         var cd = this.cellData;
24940         this.rowspan = 1;
24941         this.colspan = 1;
24942         
24943         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24944             
24945             
24946             
24947             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24948                 if (r == cd.row && c == cd.col) {
24949                     this.node.removeAttribute('rowspan');
24950                     this.node.removeAttribute('colspan');
24951                     continue;
24952                 }
24953                  
24954                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24955                 ntd.removeAttribute('id'); //
24956                 //ntd.style.width  = '';
24957                 ntd.innerHTML = '';
24958                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24959             }
24960             
24961         }
24962         this.redrawAllCells(table);
24963         
24964          
24965         
24966     },
24967     
24968     
24969     
24970     redrawAllCells: function(table)
24971     {
24972         
24973          
24974         var tab = this.node.closest('tr').closest('table');
24975         var ctr = tab.rows[0].parentNode;
24976         Array.from(tab.rows).forEach(function(r, ri){
24977             
24978             Array.from(r.cells).forEach(function(ce, ci){
24979                 ce.parentNode.removeChild(ce);
24980             });
24981             r.parentNode.removeChild(r);
24982         });
24983         for(var r = 0 ; r < table.length; r++) {
24984             var re = tab.rows[r];
24985             
24986             var re = tab.ownerDocument.createElement('tr');
24987             ctr.appendChild(re);
24988             for(var c = 0 ; c < table[r].length; c++) {
24989                 if (table[r][c].cell === false) {
24990                     continue;
24991                 }
24992                 
24993                 re.appendChild(table[r][c].cell);
24994                  
24995                 table[r][c].cell = false;
24996             }
24997         }
24998         
24999     },
25000     updateWidths : function(table)
25001     {
25002         for(var r = 0 ; r < table.length; r++) {
25003            
25004             for(var c = 0 ; c < table[r].length; c++) {
25005                 if (table[r][c].cell === false) {
25006                     continue;
25007                 }
25008                 
25009                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
25010                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25011                     el.width = Math.floor(this.colWidths[c])  +'%';
25012                     el.updateElement(el.node);
25013                 }
25014                 table[r][c].cell = false; // done
25015             }
25016         }
25017     },
25018     normalizeWidths : function(table)
25019     {
25020     
25021         if (this.colWidths[0] === false) {
25022             var nw = 100.0 / this.colWidths.length;
25023             this.colWidths.forEach(function(w,i) {
25024                 this.colWidths[i] = nw;
25025             },this);
25026             return;
25027         }
25028     
25029         var t = 0, missing = [];
25030         
25031         this.colWidths.forEach(function(w,i) {
25032             //if you mix % and
25033             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
25034             var add =  this.colWidths[i];
25035             if (add > 0) {
25036                 t+=add;
25037                 return;
25038             }
25039             missing.push(i);
25040             
25041             
25042         },this);
25043         var nc = this.colWidths.length;
25044         if (missing.length) {
25045             var mult = (nc - missing.length) / (1.0 * nc);
25046             var t = mult * t;
25047             var ew = (100 -t) / (1.0 * missing.length);
25048             this.colWidths.forEach(function(w,i) {
25049                 if (w > 0) {
25050                     this.colWidths[i] = w * mult;
25051                     return;
25052                 }
25053                 
25054                 this.colWidths[i] = ew;
25055             }, this);
25056             // have to make up numbers..
25057              
25058         }
25059         // now we should have all the widths..
25060         
25061     
25062     },
25063     
25064     shrinkColumn : function()
25065     {
25066         var table = this.toTableArray();
25067         this.normalizeWidths(table);
25068         var col = this.cellData.col;
25069         var nw = this.colWidths[col] * 0.8;
25070         if (nw < 5) {
25071             return;
25072         }
25073         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25074         this.colWidths.forEach(function(w,i) {
25075             if (i == col) {
25076                  this.colWidths[i] = nw;
25077                 return;
25078             }
25079             this.colWidths[i] += otherAdd
25080         }, this);
25081         this.updateWidths(table);
25082          
25083     },
25084     growColumn : function()
25085     {
25086         var table = this.toTableArray();
25087         this.normalizeWidths(table);
25088         var col = this.cellData.col;
25089         var nw = this.colWidths[col] * 1.2;
25090         if (nw > 90) {
25091             return;
25092         }
25093         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25094         this.colWidths.forEach(function(w,i) {
25095             if (i == col) {
25096                 this.colWidths[i] = nw;
25097                 return;
25098             }
25099             this.colWidths[i] -= otherSub
25100         }, this);
25101         this.updateWidths(table);
25102          
25103     },
25104     deleteRow : function()
25105     {
25106         // delete this rows 'tr'
25107         // if any of the cells in this row have a rowspan > 1 && row!= this row..
25108         // then reduce the rowspan.
25109         var table = this.toTableArray();
25110         // this.cellData.row;
25111         for (var i =0;i< table[this.cellData.row].length ; i++) {
25112             var c = table[this.cellData.row][i];
25113             if (c.row != this.cellData.row) {
25114                 
25115                 c.rowspan--;
25116                 c.cell.setAttribute('rowspan', c.rowspan);
25117                 continue;
25118             }
25119             if (c.rowspan > 1) {
25120                 c.rowspan--;
25121                 c.cell.setAttribute('rowspan', c.rowspan);
25122             }
25123         }
25124         table.splice(this.cellData.row,1);
25125         this.redrawAllCells(table);
25126         
25127     },
25128     deleteColumn : function()
25129     {
25130         var table = this.toTableArray();
25131         
25132         for (var i =0;i< table.length ; i++) {
25133             var c = table[i][this.cellData.col];
25134             if (c.col != this.cellData.col) {
25135                 table[i][this.cellData.col].colspan--;
25136             } else if (c.colspan > 1) {
25137                 c.colspan--;
25138                 c.cell.setAttribute('colspan', c.colspan);
25139             }
25140             table[i].splice(this.cellData.col,1);
25141         }
25142         
25143         this.redrawAllCells(table);
25144     }
25145     
25146     
25147     
25148     
25149 })
25150
25151 //<script type="text/javascript">
25152
25153 /*
25154  * Based  Ext JS Library 1.1.1
25155  * Copyright(c) 2006-2007, Ext JS, LLC.
25156  * LGPL
25157  *
25158  */
25159  
25160 /**
25161  * @class Roo.HtmlEditorCore
25162  * @extends Roo.Component
25163  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25164  *
25165  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25166  */
25167
25168 Roo.HtmlEditorCore = function(config){
25169     
25170     
25171     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25172     
25173     
25174     this.addEvents({
25175         /**
25176          * @event initialize
25177          * Fires when the editor is fully initialized (including the iframe)
25178          * @param {Roo.HtmlEditorCore} this
25179          */
25180         initialize: true,
25181         /**
25182          * @event activate
25183          * Fires when the editor is first receives the focus. Any insertion must wait
25184          * until after this event.
25185          * @param {Roo.HtmlEditorCore} this
25186          */
25187         activate: true,
25188          /**
25189          * @event beforesync
25190          * Fires before the textarea is updated with content from the editor iframe. Return false
25191          * to cancel the sync.
25192          * @param {Roo.HtmlEditorCore} this
25193          * @param {String} html
25194          */
25195         beforesync: true,
25196          /**
25197          * @event beforepush
25198          * Fires before the iframe editor is updated with content from the textarea. Return false
25199          * to cancel the push.
25200          * @param {Roo.HtmlEditorCore} this
25201          * @param {String} html
25202          */
25203         beforepush: true,
25204          /**
25205          * @event sync
25206          * Fires when the textarea is updated with content from the editor iframe.
25207          * @param {Roo.HtmlEditorCore} this
25208          * @param {String} html
25209          */
25210         sync: true,
25211          /**
25212          * @event push
25213          * Fires when the iframe editor is updated with content from the textarea.
25214          * @param {Roo.HtmlEditorCore} this
25215          * @param {String} html
25216          */
25217         push: true,
25218         
25219         /**
25220          * @event editorevent
25221          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25222          * @param {Roo.HtmlEditorCore} this
25223          */
25224         editorevent: true 
25225          
25226         
25227     });
25228     
25229     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25230     
25231     // defaults : white / black...
25232     this.applyBlacklists();
25233     
25234     
25235     
25236 };
25237
25238
25239 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25240
25241
25242      /**
25243      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25244      */
25245     
25246     owner : false,
25247     
25248      /**
25249      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25250      *                        Roo.resizable.
25251      */
25252     resizable : false,
25253      /**
25254      * @cfg {Number} height (in pixels)
25255      */   
25256     height: 300,
25257    /**
25258      * @cfg {Number} width (in pixels)
25259      */   
25260     width: 500,
25261      /**
25262      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25263      *         if you are doing an email editor, this probably needs disabling, it's designed
25264      */
25265     autoClean: true,
25266     
25267     /**
25268      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25269      */
25270     enableBlocks : true,
25271     /**
25272      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25273      * 
25274      */
25275     stylesheets: false,
25276      /**
25277      * @cfg {String} language default en - language of text (usefull for rtl languages)
25278      * 
25279      */
25280     language: 'en',
25281     
25282     /**
25283      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25284      *          - by default they are stripped - if you are editing email you may need this.
25285      */
25286     allowComments: false,
25287     // id of frame..
25288     frameId: false,
25289     
25290     // private properties
25291     validationEvent : false,
25292     deferHeight: true,
25293     initialized : false,
25294     activated : false,
25295     sourceEditMode : false,
25296     onFocus : Roo.emptyFn,
25297     iframePad:3,
25298     hideMode:'offsets',
25299     
25300     clearUp: true,
25301     
25302     // blacklist + whitelisted elements..
25303     black: false,
25304     white: false,
25305      
25306     bodyCls : '',
25307
25308     
25309     undoManager : false,
25310     /**
25311      * Protected method that will not generally be called directly. It
25312      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25313      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25314      */
25315     getDocMarkup : function(){
25316         // body styles..
25317         var st = '';
25318         
25319         // inherit styels from page...?? 
25320         if (this.stylesheets === false) {
25321             
25322             Roo.get(document.head).select('style').each(function(node) {
25323                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25324             });
25325             
25326             Roo.get(document.head).select('link').each(function(node) { 
25327                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25328             });
25329             
25330         } else if (!this.stylesheets.length) {
25331                 // simple..
25332                 st = '<style type="text/css">' +
25333                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25334                    '</style>';
25335         } else {
25336             for (var i in this.stylesheets) {
25337                 if (typeof(this.stylesheets[i]) != 'string') {
25338                     continue;
25339                 }
25340                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25341             }
25342             
25343         }
25344         
25345         st +=  '<style type="text/css">' +
25346             'IMG { cursor: pointer } ' +
25347         '</style>';
25348         
25349         st += '<meta name="google" content="notranslate">';
25350         
25351         var cls = 'notranslate roo-htmleditor-body';
25352         
25353         if(this.bodyCls.length){
25354             cls += ' ' + this.bodyCls;
25355         }
25356         
25357         return '<html  class="notranslate" translate="no"><head>' + st  +
25358             //<style type="text/css">' +
25359             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25360             //'</style>' +
25361             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25362     },
25363
25364     // private
25365     onRender : function(ct, position)
25366     {
25367         var _t = this;
25368         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25369         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25370         
25371         
25372         this.el.dom.style.border = '0 none';
25373         this.el.dom.setAttribute('tabIndex', -1);
25374         this.el.addClass('x-hidden hide');
25375         
25376         
25377         
25378         if(Roo.isIE){ // fix IE 1px bogus margin
25379             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25380         }
25381        
25382         
25383         this.frameId = Roo.id();
25384         
25385          
25386         
25387         var iframe = this.owner.wrap.createChild({
25388             tag: 'iframe',
25389             cls: 'form-control', // bootstrap..
25390             id: this.frameId,
25391             name: this.frameId,
25392             frameBorder : 'no',
25393             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25394         }, this.el
25395         );
25396         
25397         
25398         this.iframe = iframe.dom;
25399
25400         this.assignDocWin();
25401         
25402         this.doc.designMode = 'on';
25403        
25404         this.doc.open();
25405         this.doc.write(this.getDocMarkup());
25406         this.doc.close();
25407
25408         
25409         var task = { // must defer to wait for browser to be ready
25410             run : function(){
25411                 //console.log("run task?" + this.doc.readyState);
25412                 this.assignDocWin();
25413                 if(this.doc.body || this.doc.readyState == 'complete'){
25414                     try {
25415                         this.doc.designMode="on";
25416                         
25417                     } catch (e) {
25418                         return;
25419                     }
25420                     Roo.TaskMgr.stop(task);
25421                     this.initEditor.defer(10, this);
25422                 }
25423             },
25424             interval : 10,
25425             duration: 10000,
25426             scope: this
25427         };
25428         Roo.TaskMgr.start(task);
25429
25430     },
25431
25432     // private
25433     onResize : function(w, h)
25434     {
25435          Roo.log('resize: ' +w + ',' + h );
25436         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25437         if(!this.iframe){
25438             return;
25439         }
25440         if(typeof w == 'number'){
25441             
25442             this.iframe.style.width = w + 'px';
25443         }
25444         if(typeof h == 'number'){
25445             
25446             this.iframe.style.height = h + 'px';
25447             if(this.doc){
25448                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25449             }
25450         }
25451         
25452     },
25453
25454     /**
25455      * Toggles the editor between standard and source edit mode.
25456      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25457      */
25458     toggleSourceEdit : function(sourceEditMode){
25459         
25460         this.sourceEditMode = sourceEditMode === true;
25461         
25462         if(this.sourceEditMode){
25463  
25464             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25465             
25466         }else{
25467             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25468             //this.iframe.className = '';
25469             this.deferFocus();
25470         }
25471         //this.setSize(this.owner.wrap.getSize());
25472         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25473     },
25474
25475     
25476   
25477
25478     /**
25479      * Protected method that will not generally be called directly. If you need/want
25480      * custom HTML cleanup, this is the method you should override.
25481      * @param {String} html The HTML to be cleaned
25482      * return {String} The cleaned HTML
25483      */
25484     cleanHtml : function(html)
25485     {
25486         html = String(html);
25487         if(html.length > 5){
25488             if(Roo.isSafari){ // strip safari nonsense
25489                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25490             }
25491         }
25492         if(html == '&nbsp;'){
25493             html = '';
25494         }
25495         return html;
25496     },
25497
25498     /**
25499      * HTML Editor -> Textarea
25500      * Protected method that will not generally be called directly. Syncs the contents
25501      * of the editor iframe with the textarea.
25502      */
25503     syncValue : function()
25504     {
25505         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25506         if(this.initialized){
25507             
25508             if (this.undoManager) {
25509                 this.undoManager.addEvent();
25510             }
25511
25512             
25513             var bd = (this.doc.body || this.doc.documentElement);
25514            
25515             
25516             var sel = this.win.getSelection();
25517             
25518             var div = document.createElement('div');
25519             div.innerHTML = bd.innerHTML;
25520             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25521             if (gtx.length > 0) {
25522                 var rm = gtx.item(0).parentNode;
25523                 rm.parentNode.removeChild(rm);
25524             }
25525             
25526            
25527             if (this.enableBlocks) {
25528                 new Roo.htmleditor.FilterBlock({ node : div });
25529             }
25530             //?? tidy?
25531             var tidy = new Roo.htmleditor.TidySerializer({
25532                 inner:  true
25533             });
25534             var html  = tidy.serialize(div);
25535             
25536             
25537             if(Roo.isSafari){
25538                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25539                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25540                 if(m && m[1]){
25541                     html = '<div style="'+m[0]+'">' + html + '</div>';
25542                 }
25543             }
25544             html = this.cleanHtml(html);
25545             // fix up the special chars.. normaly like back quotes in word...
25546             // however we do not want to do this with chinese..
25547             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25548                 
25549                 var cc = match.charCodeAt();
25550
25551                 // Get the character value, handling surrogate pairs
25552                 if (match.length == 2) {
25553                     // It's a surrogate pair, calculate the Unicode code point
25554                     var high = match.charCodeAt(0) - 0xD800;
25555                     var low  = match.charCodeAt(1) - 0xDC00;
25556                     cc = (high * 0x400) + low + 0x10000;
25557                 }  else if (
25558                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25559                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25560                     (cc >= 0xf900 && cc < 0xfb00 )
25561                 ) {
25562                         return match;
25563                 }  
25564          
25565                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25566                 return "&#" + cc + ";";
25567                 
25568                 
25569             });
25570             
25571             
25572              
25573             if(this.owner.fireEvent('beforesync', this, html) !== false){
25574                 this.el.dom.value = html;
25575                 this.owner.fireEvent('sync', this, html);
25576             }
25577         }
25578     },
25579
25580     /**
25581      * TEXTAREA -> EDITABLE
25582      * Protected method that will not generally be called directly. Pushes the value of the textarea
25583      * into the iframe editor.
25584      */
25585     pushValue : function()
25586     {
25587         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25588         if(this.initialized){
25589             var v = this.el.dom.value.trim();
25590             
25591             
25592             if(this.owner.fireEvent('beforepush', this, v) !== false){
25593                 var d = (this.doc.body || this.doc.documentElement);
25594                 d.innerHTML = v;
25595                  
25596                 this.el.dom.value = d.innerHTML;
25597                 this.owner.fireEvent('push', this, v);
25598             }
25599             if (this.autoClean) {
25600                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25601                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25602             }
25603             if (this.enableBlocks) {
25604                 Roo.htmleditor.Block.initAll(this.doc.body);
25605             }
25606             
25607             this.updateLanguage();
25608             
25609             var lc = this.doc.body.lastChild;
25610             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25611                 // add an extra line at the end.
25612                 this.doc.body.appendChild(this.doc.createElement('br'));
25613             }
25614             
25615             
25616         }
25617     },
25618
25619     // private
25620     deferFocus : function(){
25621         this.focus.defer(10, this);
25622     },
25623
25624     // doc'ed in Field
25625     focus : function(){
25626         if(this.win && !this.sourceEditMode){
25627             this.win.focus();
25628         }else{
25629             this.el.focus();
25630         }
25631     },
25632     
25633     assignDocWin: function()
25634     {
25635         var iframe = this.iframe;
25636         
25637          if(Roo.isIE){
25638             this.doc = iframe.contentWindow.document;
25639             this.win = iframe.contentWindow;
25640         } else {
25641 //            if (!Roo.get(this.frameId)) {
25642 //                return;
25643 //            }
25644 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25645 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25646             
25647             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25648                 return;
25649             }
25650             
25651             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25652             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25653         }
25654     },
25655     
25656     // private
25657     initEditor : function(){
25658         //console.log("INIT EDITOR");
25659         this.assignDocWin();
25660         
25661         
25662         
25663         this.doc.designMode="on";
25664         this.doc.open();
25665         this.doc.write(this.getDocMarkup());
25666         this.doc.close();
25667         
25668         var dbody = (this.doc.body || this.doc.documentElement);
25669         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25670         // this copies styles from the containing element into thsi one..
25671         // not sure why we need all of this..
25672         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25673         
25674         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25675         //ss['background-attachment'] = 'fixed'; // w3c
25676         dbody.bgProperties = 'fixed'; // ie
25677         dbody.setAttribute("translate", "no");
25678         
25679         //Roo.DomHelper.applyStyles(dbody, ss);
25680         Roo.EventManager.on(this.doc, {
25681              
25682             'mouseup': this.onEditorEvent,
25683             'dblclick': this.onEditorEvent,
25684             'click': this.onEditorEvent,
25685             'keyup': this.onEditorEvent,
25686             
25687             buffer:100,
25688             scope: this
25689         });
25690         Roo.EventManager.on(this.doc, {
25691             'paste': this.onPasteEvent,
25692             scope : this
25693         });
25694         if(Roo.isGecko){
25695             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25696         }
25697         //??? needed???
25698         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25699             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25700         }
25701         this.initialized = true;
25702
25703         
25704         // initialize special key events - enter
25705         new Roo.htmleditor.KeyEnter({core : this});
25706         
25707          
25708         
25709         this.owner.fireEvent('initialize', this);
25710         this.pushValue();
25711     },
25712     // this is to prevent a href clicks resulting in a redirect?
25713    
25714     onPasteEvent : function(e,v)
25715     {
25716         // I think we better assume paste is going to be a dirty load of rubish from word..
25717         
25718         // even pasting into a 'email version' of this widget will have to clean up that mess.
25719         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25720         
25721         // check what type of paste - if it's an image, then handle it differently.
25722         if (cd.files && cd.files.length > 0) {
25723             // pasting images?
25724             var urlAPI = (window.createObjectURL && window) || 
25725                 (window.URL && URL.revokeObjectURL && URL) || 
25726                 (window.webkitURL && webkitURL);
25727     
25728             var url = urlAPI.createObjectURL( cd.files[0]);
25729             this.insertAtCursor('<img src=" + url + ">');
25730             return false;
25731         }
25732         if (cd.types.indexOf('text/html') < 0 ) {
25733             return false;
25734         }
25735         var images = [];
25736         var html = cd.getData('text/html'); // clipboard event
25737         if (cd.types.indexOf('text/rtf') > -1) {
25738             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25739             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25740         }
25741         //Roo.log(images);
25742         //Roo.log(imgs);
25743         // fixme..
25744         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
25745                        .map(function(g) { return g.toDataURL(); })
25746                        .filter(function(g) { return g != 'about:blank'; });
25747         
25748         
25749         html = this.cleanWordChars(html);
25750         
25751         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25752         
25753         
25754         var sn = this.getParentElement();
25755         // check if d contains a table, and prevent nesting??
25756         //Roo.log(d.getElementsByTagName('table'));
25757         //Roo.log(sn);
25758         //Roo.log(sn.closest('table'));
25759         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25760             e.preventDefault();
25761             this.insertAtCursor("You can not nest tables");
25762             //Roo.log("prevent?"); // fixme - 
25763             return false;
25764         }
25765         
25766         if (images.length > 0) {
25767             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25768                 img.setAttribute('src', images[i]);
25769             });
25770         }
25771         if (this.autoClean) {
25772             new Roo.htmleditor.FilterWord({ node : d });
25773             
25774             new Roo.htmleditor.FilterStyleToTag({ node : d });
25775             new Roo.htmleditor.FilterAttributes({
25776                 node : d,
25777                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
25778                 attrib_clean : ['href', 'src' ] 
25779             });
25780             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25781             // should be fonts..
25782             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25783             new Roo.htmleditor.FilterParagraph({ node : d });
25784             new Roo.htmleditor.FilterSpan({ node : d });
25785             new Roo.htmleditor.FilterLongBr({ node : d });
25786             new Roo.htmleditor.FilterComment({ node : d });
25787             
25788             
25789         }
25790         if (this.enableBlocks) {
25791                 
25792             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25793                 if (img.closest('figure')) { // assume!! that it's aready
25794                     return;
25795                 }
25796                 var fig  = new Roo.htmleditor.BlockFigure({
25797                     image_src  : img.src
25798                 });
25799                 fig.updateElement(img); // replace it..
25800                 
25801             });
25802         }
25803         
25804         
25805         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25806         if (this.enableBlocks) {
25807             Roo.htmleditor.Block.initAll(this.doc.body);
25808         }
25809          
25810         
25811         e.preventDefault();
25812         return false;
25813         // default behaveiour should be our local cleanup paste? (optional?)
25814         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25815         //this.owner.fireEvent('paste', e, v);
25816     },
25817     // private
25818     onDestroy : function(){
25819         
25820         
25821         
25822         if(this.rendered){
25823             
25824             //for (var i =0; i < this.toolbars.length;i++) {
25825             //    // fixme - ask toolbars for heights?
25826             //    this.toolbars[i].onDestroy();
25827            // }
25828             
25829             //this.wrap.dom.innerHTML = '';
25830             //this.wrap.remove();
25831         }
25832     },
25833
25834     // private
25835     onFirstFocus : function(){
25836         
25837         this.assignDocWin();
25838         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25839         
25840         this.activated = true;
25841          
25842     
25843         if(Roo.isGecko){ // prevent silly gecko errors
25844             this.win.focus();
25845             var s = this.win.getSelection();
25846             if(!s.focusNode || s.focusNode.nodeType != 3){
25847                 var r = s.getRangeAt(0);
25848                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25849                 r.collapse(true);
25850                 this.deferFocus();
25851             }
25852             try{
25853                 this.execCmd('useCSS', true);
25854                 this.execCmd('styleWithCSS', false);
25855             }catch(e){}
25856         }
25857         this.owner.fireEvent('activate', this);
25858     },
25859
25860     // private
25861     adjustFont: function(btn){
25862         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25863         //if(Roo.isSafari){ // safari
25864         //    adjust *= 2;
25865        // }
25866         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25867         if(Roo.isSafari){ // safari
25868             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25869             v =  (v < 10) ? 10 : v;
25870             v =  (v > 48) ? 48 : v;
25871             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25872             
25873         }
25874         
25875         
25876         v = Math.max(1, v+adjust);
25877         
25878         this.execCmd('FontSize', v  );
25879     },
25880
25881     onEditorEvent : function(e)
25882     {
25883          
25884         
25885         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25886             return; // we do not handle this.. (undo manager does..)
25887         }
25888         // in theory this detects if the last element is not a br, then we try and do that.
25889         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25890         if (e &&
25891             e.target.nodeName == 'BODY' &&
25892             e.type == "mouseup" &&
25893             this.doc.body.lastChild
25894            ) {
25895             var lc = this.doc.body.lastChild;
25896             // gtx-trans is google translate plugin adding crap.
25897             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25898                 lc = lc.previousSibling;
25899             }
25900             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25901             // if last element is <BR> - then dont do anything.
25902             
25903                 var ns = this.doc.createElement('br');
25904                 this.doc.body.appendChild(ns);
25905                 range = this.doc.createRange();
25906                 range.setStartAfter(ns);
25907                 range.collapse(true);
25908                 var sel = this.win.getSelection();
25909                 sel.removeAllRanges();
25910                 sel.addRange(range);
25911             }
25912         }
25913         
25914         
25915         
25916         this.fireEditorEvent(e);
25917       //  this.updateToolbar();
25918         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25919     },
25920     
25921     fireEditorEvent: function(e)
25922     {
25923         this.owner.fireEvent('editorevent', this, e);
25924     },
25925
25926     insertTag : function(tg)
25927     {
25928         // could be a bit smarter... -> wrap the current selected tRoo..
25929         if (tg.toLowerCase() == 'span' ||
25930             tg.toLowerCase() == 'code' ||
25931             tg.toLowerCase() == 'sup' ||
25932             tg.toLowerCase() == 'sub' 
25933             ) {
25934             
25935             range = this.createRange(this.getSelection());
25936             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25937             wrappingNode.appendChild(range.extractContents());
25938             range.insertNode(wrappingNode);
25939
25940             return;
25941             
25942             
25943             
25944         }
25945         this.execCmd("formatblock",   tg);
25946         this.undoManager.addEvent(); 
25947     },
25948     
25949     insertText : function(txt)
25950     {
25951         
25952         
25953         var range = this.createRange();
25954         range.deleteContents();
25955                //alert(Sender.getAttribute('label'));
25956                
25957         range.insertNode(this.doc.createTextNode(txt));
25958         this.undoManager.addEvent();
25959     } ,
25960     
25961      
25962
25963     /**
25964      * Executes a Midas editor command on the editor document and performs necessary focus and
25965      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25966      * @param {String} cmd The Midas command
25967      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25968      */
25969     relayCmd : function(cmd, value)
25970     {
25971         
25972         switch (cmd) {
25973             case 'justifyleft':
25974             case 'justifyright':
25975             case 'justifycenter':
25976                 // if we are in a cell, then we will adjust the
25977                 var n = this.getParentElement();
25978                 var td = n.closest('td');
25979                 if (td) {
25980                     var bl = Roo.htmleditor.Block.factory(td);
25981                     bl.textAlign = cmd.replace('justify','');
25982                     bl.updateElement();
25983                     this.owner.fireEvent('editorevent', this);
25984                     return;
25985                 }
25986                 this.execCmd('styleWithCSS', true); // 
25987                 break;
25988             case 'bold':
25989             case 'italic':
25990                 // if there is no selection, then we insert, and set the curson inside it..
25991                 this.execCmd('styleWithCSS', false); 
25992                 break;
25993                 
25994         
25995             default:
25996                 break;
25997         }
25998         
25999         
26000         this.win.focus();
26001         this.execCmd(cmd, value);
26002         this.owner.fireEvent('editorevent', this);
26003         //this.updateToolbar();
26004         this.owner.deferFocus();
26005     },
26006
26007     /**
26008      * Executes a Midas editor command directly on the editor document.
26009      * For visual commands, you should use {@link #relayCmd} instead.
26010      * <b>This should only be called after the editor is initialized.</b>
26011      * @param {String} cmd The Midas command
26012      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26013      */
26014     execCmd : function(cmd, value){
26015         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26016         this.syncValue();
26017     },
26018  
26019  
26020    
26021     /**
26022      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26023      * to insert tRoo.
26024      * @param {String} text | dom node.. 
26025      */
26026     insertAtCursor : function(text)
26027     {
26028         
26029         if(!this.activated){
26030             return;
26031         }
26032          
26033         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26034             this.win.focus();
26035             
26036             
26037             // from jquery ui (MIT licenced)
26038             var range, node;
26039             var win = this.win;
26040             
26041             if (win.getSelection && win.getSelection().getRangeAt) {
26042                 
26043                 // delete the existing?
26044                 
26045                 this.createRange(this.getSelection()).deleteContents();
26046                 range = win.getSelection().getRangeAt(0);
26047                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26048                 range.insertNode(node);
26049                 range = range.cloneRange();
26050                 range.collapse(false);
26051                  
26052                 win.getSelection().removeAllRanges();
26053                 win.getSelection().addRange(range);
26054                 
26055                 
26056                 
26057             } else if (win.document.selection && win.document.selection.createRange) {
26058                 // no firefox support
26059                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26060                 win.document.selection.createRange().pasteHTML(txt);
26061             
26062             } else {
26063                 // no firefox support
26064                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26065                 this.execCmd('InsertHTML', txt);
26066             } 
26067             this.syncValue();
26068             
26069             this.deferFocus();
26070         }
26071     },
26072  // private
26073     mozKeyPress : function(e){
26074         if(e.ctrlKey){
26075             var c = e.getCharCode(), cmd;
26076           
26077             if(c > 0){
26078                 c = String.fromCharCode(c).toLowerCase();
26079                 switch(c){
26080                     case 'b':
26081                         cmd = 'bold';
26082                         break;
26083                     case 'i':
26084                         cmd = 'italic';
26085                         break;
26086                     
26087                     case 'u':
26088                         cmd = 'underline';
26089                         break;
26090                     
26091                     //case 'v':
26092                       //  this.cleanUpPaste.defer(100, this);
26093                       //  return;
26094                         
26095                 }
26096                 if(cmd){
26097                     
26098                     this.relayCmd(cmd);
26099                     //this.win.focus();
26100                     //this.execCmd(cmd);
26101                     //this.deferFocus();
26102                     e.preventDefault();
26103                 }
26104                 
26105             }
26106         }
26107     },
26108
26109     // private
26110     fixKeys : function(){ // load time branching for fastest keydown performance
26111         
26112         
26113         if(Roo.isIE){
26114             return function(e){
26115                 var k = e.getKey(), r;
26116                 if(k == e.TAB){
26117                     e.stopEvent();
26118                     r = this.doc.selection.createRange();
26119                     if(r){
26120                         r.collapse(true);
26121                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26122                         this.deferFocus();
26123                     }
26124                     return;
26125                 }
26126                 /// this is handled by Roo.htmleditor.KeyEnter
26127                  /*
26128                 if(k == e.ENTER){
26129                     r = this.doc.selection.createRange();
26130                     if(r){
26131                         var target = r.parentElement();
26132                         if(!target || target.tagName.toLowerCase() != 'li'){
26133                             e.stopEvent();
26134                             r.pasteHTML('<br/>');
26135                             r.collapse(false);
26136                             r.select();
26137                         }
26138                     }
26139                 }
26140                 */
26141                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26142                 //    this.cleanUpPaste.defer(100, this);
26143                 //    return;
26144                 //}
26145                 
26146                 
26147             };
26148         }else if(Roo.isOpera){
26149             return function(e){
26150                 var k = e.getKey();
26151                 if(k == e.TAB){
26152                     e.stopEvent();
26153                     this.win.focus();
26154                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26155                     this.deferFocus();
26156                 }
26157                
26158                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26159                 //    this.cleanUpPaste.defer(100, this);
26160                  //   return;
26161                 //}
26162                 
26163             };
26164         }else if(Roo.isSafari){
26165             return function(e){
26166                 var k = e.getKey();
26167                 
26168                 if(k == e.TAB){
26169                     e.stopEvent();
26170                     this.execCmd('InsertText','\t');
26171                     this.deferFocus();
26172                     return;
26173                 }
26174                  this.mozKeyPress(e);
26175                 
26176                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26177                  //   this.cleanUpPaste.defer(100, this);
26178                  //   return;
26179                // }
26180                 
26181              };
26182         }
26183     }(),
26184     
26185     getAllAncestors: function()
26186     {
26187         var p = this.getSelectedNode();
26188         var a = [];
26189         if (!p) {
26190             a.push(p); // push blank onto stack..
26191             p = this.getParentElement();
26192         }
26193         
26194         
26195         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26196             a.push(p);
26197             p = p.parentNode;
26198         }
26199         a.push(this.doc.body);
26200         return a;
26201     },
26202     lastSel : false,
26203     lastSelNode : false,
26204     
26205     
26206     getSelection : function() 
26207     {
26208         this.assignDocWin();
26209         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26210     },
26211     /**
26212      * Select a dom node
26213      * @param {DomElement} node the node to select
26214      */
26215     selectNode : function(node, collapse)
26216     {
26217         var nodeRange = node.ownerDocument.createRange();
26218         try {
26219             nodeRange.selectNode(node);
26220         } catch (e) {
26221             nodeRange.selectNodeContents(node);
26222         }
26223         if (collapse === true) {
26224             nodeRange.collapse(true);
26225         }
26226         //
26227         var s = this.win.getSelection();
26228         s.removeAllRanges();
26229         s.addRange(nodeRange);
26230     },
26231     
26232     getSelectedNode: function() 
26233     {
26234         // this may only work on Gecko!!!
26235         
26236         // should we cache this!!!!
26237         
26238          
26239          
26240         var range = this.createRange(this.getSelection()).cloneRange();
26241         
26242         if (Roo.isIE) {
26243             var parent = range.parentElement();
26244             while (true) {
26245                 var testRange = range.duplicate();
26246                 testRange.moveToElementText(parent);
26247                 if (testRange.inRange(range)) {
26248                     break;
26249                 }
26250                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26251                     break;
26252                 }
26253                 parent = parent.parentElement;
26254             }
26255             return parent;
26256         }
26257         
26258         // is ancestor a text element.
26259         var ac =  range.commonAncestorContainer;
26260         if (ac.nodeType == 3) {
26261             ac = ac.parentNode;
26262         }
26263         
26264         var ar = ac.childNodes;
26265          
26266         var nodes = [];
26267         var other_nodes = [];
26268         var has_other_nodes = false;
26269         for (var i=0;i<ar.length;i++) {
26270             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26271                 continue;
26272             }
26273             // fullly contained node.
26274             
26275             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26276                 nodes.push(ar[i]);
26277                 continue;
26278             }
26279             
26280             // probably selected..
26281             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26282                 other_nodes.push(ar[i]);
26283                 continue;
26284             }
26285             // outer..
26286             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26287                 continue;
26288             }
26289             
26290             
26291             has_other_nodes = true;
26292         }
26293         if (!nodes.length && other_nodes.length) {
26294             nodes= other_nodes;
26295         }
26296         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26297             return false;
26298         }
26299         
26300         return nodes[0];
26301     },
26302     
26303     
26304     createRange: function(sel)
26305     {
26306         // this has strange effects when using with 
26307         // top toolbar - not sure if it's a great idea.
26308         //this.editor.contentWindow.focus();
26309         if (typeof sel != "undefined") {
26310             try {
26311                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26312             } catch(e) {
26313                 return this.doc.createRange();
26314             }
26315         } else {
26316             return this.doc.createRange();
26317         }
26318     },
26319     getParentElement: function()
26320     {
26321         
26322         this.assignDocWin();
26323         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26324         
26325         var range = this.createRange(sel);
26326          
26327         try {
26328             var p = range.commonAncestorContainer;
26329             while (p.nodeType == 3) { // text node
26330                 p = p.parentNode;
26331             }
26332             return p;
26333         } catch (e) {
26334             return null;
26335         }
26336     
26337     },
26338     /***
26339      *
26340      * Range intersection.. the hard stuff...
26341      *  '-1' = before
26342      *  '0' = hits..
26343      *  '1' = after.
26344      *         [ -- selected range --- ]
26345      *   [fail]                        [fail]
26346      *
26347      *    basically..
26348      *      if end is before start or  hits it. fail.
26349      *      if start is after end or hits it fail.
26350      *
26351      *   if either hits (but other is outside. - then it's not 
26352      *   
26353      *    
26354      **/
26355     
26356     
26357     // @see http://www.thismuchiknow.co.uk/?p=64.
26358     rangeIntersectsNode : function(range, node)
26359     {
26360         var nodeRange = node.ownerDocument.createRange();
26361         try {
26362             nodeRange.selectNode(node);
26363         } catch (e) {
26364             nodeRange.selectNodeContents(node);
26365         }
26366     
26367         var rangeStartRange = range.cloneRange();
26368         rangeStartRange.collapse(true);
26369     
26370         var rangeEndRange = range.cloneRange();
26371         rangeEndRange.collapse(false);
26372     
26373         var nodeStartRange = nodeRange.cloneRange();
26374         nodeStartRange.collapse(true);
26375     
26376         var nodeEndRange = nodeRange.cloneRange();
26377         nodeEndRange.collapse(false);
26378     
26379         return rangeStartRange.compareBoundaryPoints(
26380                  Range.START_TO_START, nodeEndRange) == -1 &&
26381                rangeEndRange.compareBoundaryPoints(
26382                  Range.START_TO_START, nodeStartRange) == 1;
26383         
26384          
26385     },
26386     rangeCompareNode : function(range, node)
26387     {
26388         var nodeRange = node.ownerDocument.createRange();
26389         try {
26390             nodeRange.selectNode(node);
26391         } catch (e) {
26392             nodeRange.selectNodeContents(node);
26393         }
26394         
26395         
26396         range.collapse(true);
26397     
26398         nodeRange.collapse(true);
26399      
26400         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26401         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26402          
26403         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26404         
26405         var nodeIsBefore   =  ss == 1;
26406         var nodeIsAfter    = ee == -1;
26407         
26408         if (nodeIsBefore && nodeIsAfter) {
26409             return 0; // outer
26410         }
26411         if (!nodeIsBefore && nodeIsAfter) {
26412             return 1; //right trailed.
26413         }
26414         
26415         if (nodeIsBefore && !nodeIsAfter) {
26416             return 2;  // left trailed.
26417         }
26418         // fully contined.
26419         return 3;
26420     },
26421  
26422     cleanWordChars : function(input) {// change the chars to hex code
26423         
26424        var swapCodes  = [ 
26425             [    8211, "&#8211;" ], 
26426             [    8212, "&#8212;" ], 
26427             [    8216,  "'" ],  
26428             [    8217, "'" ],  
26429             [    8220, '"' ],  
26430             [    8221, '"' ],  
26431             [    8226, "*" ],  
26432             [    8230, "..." ]
26433         ]; 
26434         var output = input;
26435         Roo.each(swapCodes, function(sw) { 
26436             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26437             
26438             output = output.replace(swapper, sw[1]);
26439         });
26440         
26441         return output;
26442     },
26443     
26444      
26445     
26446         
26447     
26448     cleanUpChild : function (node)
26449     {
26450         
26451         new Roo.htmleditor.FilterComment({node : node});
26452         new Roo.htmleditor.FilterAttributes({
26453                 node : node,
26454                 attrib_black : this.ablack,
26455                 attrib_clean : this.aclean,
26456                 style_white : this.cwhite,
26457                 style_black : this.cblack
26458         });
26459         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26460         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26461          
26462         
26463     },
26464     
26465     /**
26466      * Clean up MS wordisms...
26467      * @deprecated - use filter directly
26468      */
26469     cleanWord : function(node)
26470     {
26471         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26472         
26473     },
26474    
26475     
26476     /**
26477
26478      * @deprecated - use filters
26479      */
26480     cleanTableWidths : function(node)
26481     {
26482         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26483         
26484  
26485     },
26486     
26487      
26488         
26489     applyBlacklists : function()
26490     {
26491         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26492         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26493         
26494         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26495         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26496         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26497         
26498         this.white = [];
26499         this.black = [];
26500         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26501             if (b.indexOf(tag) > -1) {
26502                 return;
26503             }
26504             this.white.push(tag);
26505             
26506         }, this);
26507         
26508         Roo.each(w, function(tag) {
26509             if (b.indexOf(tag) > -1) {
26510                 return;
26511             }
26512             if (this.white.indexOf(tag) > -1) {
26513                 return;
26514             }
26515             this.white.push(tag);
26516             
26517         }, this);
26518         
26519         
26520         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26521             if (w.indexOf(tag) > -1) {
26522                 return;
26523             }
26524             this.black.push(tag);
26525             
26526         }, this);
26527         
26528         Roo.each(b, function(tag) {
26529             if (w.indexOf(tag) > -1) {
26530                 return;
26531             }
26532             if (this.black.indexOf(tag) > -1) {
26533                 return;
26534             }
26535             this.black.push(tag);
26536             
26537         }, this);
26538         
26539         
26540         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26541         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26542         
26543         this.cwhite = [];
26544         this.cblack = [];
26545         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26546             if (b.indexOf(tag) > -1) {
26547                 return;
26548             }
26549             this.cwhite.push(tag);
26550             
26551         }, this);
26552         
26553         Roo.each(w, function(tag) {
26554             if (b.indexOf(tag) > -1) {
26555                 return;
26556             }
26557             if (this.cwhite.indexOf(tag) > -1) {
26558                 return;
26559             }
26560             this.cwhite.push(tag);
26561             
26562         }, this);
26563         
26564         
26565         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26566             if (w.indexOf(tag) > -1) {
26567                 return;
26568             }
26569             this.cblack.push(tag);
26570             
26571         }, this);
26572         
26573         Roo.each(b, function(tag) {
26574             if (w.indexOf(tag) > -1) {
26575                 return;
26576             }
26577             if (this.cblack.indexOf(tag) > -1) {
26578                 return;
26579             }
26580             this.cblack.push(tag);
26581             
26582         }, this);
26583     },
26584     
26585     setStylesheets : function(stylesheets)
26586     {
26587         if(typeof(stylesheets) == 'string'){
26588             Roo.get(this.iframe.contentDocument.head).createChild({
26589                 tag : 'link',
26590                 rel : 'stylesheet',
26591                 type : 'text/css',
26592                 href : stylesheets
26593             });
26594             
26595             return;
26596         }
26597         var _this = this;
26598      
26599         Roo.each(stylesheets, function(s) {
26600             if(!s.length){
26601                 return;
26602             }
26603             
26604             Roo.get(_this.iframe.contentDocument.head).createChild({
26605                 tag : 'link',
26606                 rel : 'stylesheet',
26607                 type : 'text/css',
26608                 href : s
26609             });
26610         });
26611
26612         
26613     },
26614     
26615     
26616     updateLanguage : function()
26617     {
26618         if (!this.iframe || !this.iframe.contentDocument) {
26619             return;
26620         }
26621         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26622     },
26623     
26624     
26625     removeStylesheets : function()
26626     {
26627         var _this = this;
26628         
26629         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26630             s.remove();
26631         });
26632     },
26633     
26634     setStyle : function(style)
26635     {
26636         Roo.get(this.iframe.contentDocument.head).createChild({
26637             tag : 'style',
26638             type : 'text/css',
26639             html : style
26640         });
26641
26642         return;
26643     }
26644     
26645     // hide stuff that is not compatible
26646     /**
26647      * @event blur
26648      * @hide
26649      */
26650     /**
26651      * @event change
26652      * @hide
26653      */
26654     /**
26655      * @event focus
26656      * @hide
26657      */
26658     /**
26659      * @event specialkey
26660      * @hide
26661      */
26662     /**
26663      * @cfg {String} fieldClass @hide
26664      */
26665     /**
26666      * @cfg {String} focusClass @hide
26667      */
26668     /**
26669      * @cfg {String} autoCreate @hide
26670      */
26671     /**
26672      * @cfg {String} inputType @hide
26673      */
26674     /**
26675      * @cfg {String} invalidClass @hide
26676      */
26677     /**
26678      * @cfg {String} invalidText @hide
26679      */
26680     /**
26681      * @cfg {String} msgFx @hide
26682      */
26683     /**
26684      * @cfg {String} validateOnBlur @hide
26685      */
26686 });
26687
26688 Roo.HtmlEditorCore.white = [
26689         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26690         
26691        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26692        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26693        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26694        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26695        'TABLE',   'UL',         'XMP', 
26696        
26697        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26698       'THEAD',   'TR', 
26699      
26700       'DIR', 'MENU', 'OL', 'UL', 'DL',
26701        
26702       'EMBED',  'OBJECT'
26703 ];
26704
26705
26706 Roo.HtmlEditorCore.black = [
26707     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26708         'APPLET', // 
26709         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26710         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26711         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26712         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26713         //'FONT' // CLEAN LATER..
26714         'COLGROUP', 'COL'   // messy tables.
26715         
26716         
26717 ];
26718 Roo.HtmlEditorCore.clean = [ // ?? needed???
26719      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26720 ];
26721 Roo.HtmlEditorCore.tag_remove = [
26722     'FONT', 'TBODY'  
26723 ];
26724 // attributes..
26725
26726 Roo.HtmlEditorCore.ablack = [
26727     'on'
26728 ];
26729     
26730 Roo.HtmlEditorCore.aclean = [ 
26731     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26732 ];
26733
26734 // protocols..
26735 Roo.HtmlEditorCore.pwhite= [
26736         'http',  'https',  'mailto'
26737 ];
26738
26739 // white listed style attributes.
26740 Roo.HtmlEditorCore.cwhite= [
26741       //  'text-align', /// default is to allow most things..
26742       
26743          
26744 //        'font-size'//??
26745 ];
26746
26747 // black listed style attributes.
26748 Roo.HtmlEditorCore.cblack= [
26749       //  'font-size' -- this can be set by the project 
26750 ];
26751
26752
26753
26754
26755     //<script type="text/javascript">
26756
26757 /*
26758  * Ext JS Library 1.1.1
26759  * Copyright(c) 2006-2007, Ext JS, LLC.
26760  * Licence LGPL
26761  * 
26762  */
26763  
26764  
26765 Roo.form.HtmlEditor = function(config){
26766     
26767     
26768     
26769     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26770     
26771     if (!this.toolbars) {
26772         this.toolbars = [];
26773     }
26774     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26775     
26776     
26777 };
26778
26779 /**
26780  * @class Roo.form.HtmlEditor
26781  * @extends Roo.form.Field
26782  * Provides a lightweight HTML Editor component.
26783  *
26784  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26785  * 
26786  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26787  * supported by this editor.</b><br/><br/>
26788  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26789  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26790  */
26791 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26792     /**
26793      * @cfg {Boolean} clearUp
26794      */
26795     clearUp : true,
26796       /**
26797      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26798      */
26799     toolbars : false,
26800    
26801      /**
26802      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26803      *                        Roo.resizable.
26804      */
26805     resizable : false,
26806      /**
26807      * @cfg {Number} height (in pixels)
26808      */   
26809     height: 300,
26810    /**
26811      * @cfg {Number} width (in pixels)
26812      */   
26813     width: 500,
26814     
26815     /**
26816      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26817      * 
26818      */
26819     stylesheets: false,
26820     
26821     
26822      /**
26823      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26824      * 
26825      */
26826     cblack: false,
26827     /**
26828      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26829      * 
26830      */
26831     cwhite: false,
26832     
26833      /**
26834      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26835      * 
26836      */
26837     black: false,
26838     /**
26839      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26840      * 
26841      */
26842     white: false,
26843     /**
26844      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26845      */
26846     allowComments: false,
26847     /**
26848      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26849      */
26850     enableBlocks : true,
26851     
26852     /**
26853      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26854      *         if you are doing an email editor, this probably needs disabling, it's designed
26855      */
26856     autoClean: true,
26857     /**
26858      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26859      */
26860     bodyCls : '',
26861     /**
26862      * @cfg {String} language default en - language of text (usefull for rtl languages)
26863      * 
26864      */
26865     language: 'en',
26866     
26867      
26868     // id of frame..
26869     frameId: false,
26870     
26871     // private properties
26872     validationEvent : false,
26873     deferHeight: true,
26874     initialized : false,
26875     activated : false,
26876     
26877     onFocus : Roo.emptyFn,
26878     iframePad:3,
26879     hideMode:'offsets',
26880     
26881     actionMode : 'container', // defaults to hiding it...
26882     
26883     defaultAutoCreate : { // modified by initCompnoent..
26884         tag: "textarea",
26885         style:"width:500px;height:300px;",
26886         autocomplete: "new-password"
26887     },
26888
26889     // private
26890     initComponent : function(){
26891         this.addEvents({
26892             /**
26893              * @event initialize
26894              * Fires when the editor is fully initialized (including the iframe)
26895              * @param {HtmlEditor} this
26896              */
26897             initialize: true,
26898             /**
26899              * @event activate
26900              * Fires when the editor is first receives the focus. Any insertion must wait
26901              * until after this event.
26902              * @param {HtmlEditor} this
26903              */
26904             activate: true,
26905              /**
26906              * @event beforesync
26907              * Fires before the textarea is updated with content from the editor iframe. Return false
26908              * to cancel the sync.
26909              * @param {HtmlEditor} this
26910              * @param {String} html
26911              */
26912             beforesync: true,
26913              /**
26914              * @event beforepush
26915              * Fires before the iframe editor is updated with content from the textarea. Return false
26916              * to cancel the push.
26917              * @param {HtmlEditor} this
26918              * @param {String} html
26919              */
26920             beforepush: true,
26921              /**
26922              * @event sync
26923              * Fires when the textarea is updated with content from the editor iframe.
26924              * @param {HtmlEditor} this
26925              * @param {String} html
26926              */
26927             sync: true,
26928              /**
26929              * @event push
26930              * Fires when the iframe editor is updated with content from the textarea.
26931              * @param {HtmlEditor} this
26932              * @param {String} html
26933              */
26934             push: true,
26935              /**
26936              * @event editmodechange
26937              * Fires when the editor switches edit modes
26938              * @param {HtmlEditor} this
26939              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26940              */
26941             editmodechange: true,
26942             /**
26943              * @event editorevent
26944              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26945              * @param {HtmlEditor} this
26946              */
26947             editorevent: true,
26948             /**
26949              * @event firstfocus
26950              * Fires when on first focus - needed by toolbars..
26951              * @param {HtmlEditor} this
26952              */
26953             firstfocus: true,
26954             /**
26955              * @event autosave
26956              * Auto save the htmlEditor value as a file into Events
26957              * @param {HtmlEditor} this
26958              */
26959             autosave: true,
26960             /**
26961              * @event savedpreview
26962              * preview the saved version of htmlEditor
26963              * @param {HtmlEditor} this
26964              */
26965             savedpreview: true,
26966             
26967             /**
26968             * @event stylesheetsclick
26969             * Fires when press the Sytlesheets button
26970             * @param {Roo.HtmlEditorCore} this
26971             */
26972             stylesheetsclick: true,
26973             /**
26974             * @event paste
26975             * Fires when press user pastes into the editor
26976             * @param {Roo.HtmlEditorCore} this
26977             */
26978             paste: true 
26979         });
26980         this.defaultAutoCreate =  {
26981             tag: "textarea",
26982             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26983             autocomplete: "new-password"
26984         };
26985     },
26986
26987     /**
26988      * Protected method that will not generally be called directly. It
26989      * is called when the editor creates its toolbar. Override this method if you need to
26990      * add custom toolbar buttons.
26991      * @param {HtmlEditor} editor
26992      */
26993     createToolbar : function(editor){
26994         Roo.log("create toolbars");
26995         if (!editor.toolbars || !editor.toolbars.length) {
26996             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26997         }
26998         
26999         for (var i =0 ; i < editor.toolbars.length;i++) {
27000             editor.toolbars[i] = Roo.factory(
27001                     typeof(editor.toolbars[i]) == 'string' ?
27002                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
27003                 Roo.form.HtmlEditor);
27004             editor.toolbars[i].init(editor);
27005         }
27006          
27007         
27008     },
27009     /**
27010      * get the Context selected node
27011      * @returns {DomElement|boolean} selected node if active or false if none
27012      * 
27013      */
27014     getSelectedNode : function()
27015     {
27016         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
27017             return false;
27018         }
27019         return this.toolbars[1].tb.selectedNode;
27020     
27021     },
27022     // private
27023     onRender : function(ct, position)
27024     {
27025         var _t = this;
27026         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27027         
27028         this.wrap = this.el.wrap({
27029             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27030         });
27031         
27032         this.editorcore.onRender(ct, position);
27033          
27034         if (this.resizable) {
27035             this.resizeEl = new Roo.Resizable(this.wrap, {
27036                 pinned : true,
27037                 wrap: true,
27038                 dynamic : true,
27039                 minHeight : this.height,
27040                 height: this.height,
27041                 handles : this.resizable,
27042                 width: this.width,
27043                 listeners : {
27044                     resize : function(r, w, h) {
27045                         _t.onResize(w,h); // -something
27046                     }
27047                 }
27048             });
27049             
27050         }
27051         this.createToolbar(this);
27052        
27053         
27054         if(!this.width){
27055             this.setSize(this.wrap.getSize());
27056         }
27057         if (this.resizeEl) {
27058             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27059             // should trigger onReize..
27060         }
27061         
27062         this.keyNav = new Roo.KeyNav(this.el, {
27063             
27064             "tab" : function(e){
27065                 e.preventDefault();
27066                 
27067                 var value = this.getValue();
27068                 
27069                 var start = this.el.dom.selectionStart;
27070                 var end = this.el.dom.selectionEnd;
27071                 
27072                 if(!e.shiftKey){
27073                     
27074                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
27075                     this.el.dom.setSelectionRange(end + 1, end + 1);
27076                     return;
27077                 }
27078                 
27079                 var f = value.substring(0, start).split("\t");
27080                 
27081                 if(f.pop().length != 0){
27082                     return;
27083                 }
27084                 
27085                 this.setValue(f.join("\t") + value.substring(end));
27086                 this.el.dom.setSelectionRange(start - 1, start - 1);
27087                 
27088             },
27089             
27090             "home" : function(e){
27091                 e.preventDefault();
27092                 
27093                 var curr = this.el.dom.selectionStart;
27094                 var lines = this.getValue().split("\n");
27095                 
27096                 if(!lines.length){
27097                     return;
27098                 }
27099                 
27100                 if(e.ctrlKey){
27101                     this.el.dom.setSelectionRange(0, 0);
27102                     return;
27103                 }
27104                 
27105                 var pos = 0;
27106                 
27107                 for (var i = 0; i < lines.length;i++) {
27108                     pos += lines[i].length;
27109                     
27110                     if(i != 0){
27111                         pos += 1;
27112                     }
27113                     
27114                     if(pos < curr){
27115                         continue;
27116                     }
27117                     
27118                     pos -= lines[i].length;
27119                     
27120                     break;
27121                 }
27122                 
27123                 if(!e.shiftKey){
27124                     this.el.dom.setSelectionRange(pos, pos);
27125                     return;
27126                 }
27127                 
27128                 this.el.dom.selectionStart = pos;
27129                 this.el.dom.selectionEnd = curr;
27130             },
27131             
27132             "end" : function(e){
27133                 e.preventDefault();
27134                 
27135                 var curr = this.el.dom.selectionStart;
27136                 var lines = this.getValue().split("\n");
27137                 
27138                 if(!lines.length){
27139                     return;
27140                 }
27141                 
27142                 if(e.ctrlKey){
27143                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
27144                     return;
27145                 }
27146                 
27147                 var pos = 0;
27148                 
27149                 for (var i = 0; i < lines.length;i++) {
27150                     
27151                     pos += lines[i].length;
27152                     
27153                     if(i != 0){
27154                         pos += 1;
27155                     }
27156                     
27157                     if(pos < curr){
27158                         continue;
27159                     }
27160                     
27161                     break;
27162                 }
27163                 
27164                 if(!e.shiftKey){
27165                     this.el.dom.setSelectionRange(pos, pos);
27166                     return;
27167                 }
27168                 
27169                 this.el.dom.selectionStart = curr;
27170                 this.el.dom.selectionEnd = pos;
27171             },
27172
27173             scope : this,
27174
27175             doRelay : function(foo, bar, hname){
27176                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27177             },
27178
27179             forceKeyDown: true
27180         });
27181         
27182 //        if(this.autosave && this.w){
27183 //            this.autoSaveFn = setInterval(this.autosave, 1000);
27184 //        }
27185     },
27186
27187     // private
27188     onResize : function(w, h)
27189     {
27190         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27191         var ew = false;
27192         var eh = false;
27193         
27194         if(this.el ){
27195             if(typeof w == 'number'){
27196                 var aw = w - this.wrap.getFrameWidth('lr');
27197                 this.el.setWidth(this.adjustWidth('textarea', aw));
27198                 ew = aw;
27199             }
27200             if(typeof h == 'number'){
27201                 var tbh = 0;
27202                 for (var i =0; i < this.toolbars.length;i++) {
27203                     // fixme - ask toolbars for heights?
27204                     tbh += this.toolbars[i].tb.el.getHeight();
27205                     if (this.toolbars[i].footer) {
27206                         tbh += this.toolbars[i].footer.el.getHeight();
27207                     }
27208                 }
27209                 
27210                 
27211                 
27212                 
27213                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27214                 ah -= 5; // knock a few pixes off for look..
27215 //                Roo.log(ah);
27216                 this.el.setHeight(this.adjustWidth('textarea', ah));
27217                 var eh = ah;
27218             }
27219         }
27220         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27221         this.editorcore.onResize(ew,eh);
27222         
27223     },
27224
27225     /**
27226      * Toggles the editor between standard and source edit mode.
27227      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27228      */
27229     toggleSourceEdit : function(sourceEditMode)
27230     {
27231         this.editorcore.toggleSourceEdit(sourceEditMode);
27232         
27233         if(this.editorcore.sourceEditMode){
27234             Roo.log('editor - showing textarea');
27235             
27236 //            Roo.log('in');
27237 //            Roo.log(this.syncValue());
27238             this.editorcore.syncValue();
27239             this.el.removeClass('x-hidden');
27240             this.el.dom.removeAttribute('tabIndex');
27241             this.el.focus();
27242             this.el.dom.scrollTop = 0;
27243             
27244             
27245             for (var i = 0; i < this.toolbars.length; i++) {
27246                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27247                     this.toolbars[i].tb.hide();
27248                     this.toolbars[i].footer.hide();
27249                 }
27250             }
27251             
27252         }else{
27253             Roo.log('editor - hiding textarea');
27254 //            Roo.log('out')
27255 //            Roo.log(this.pushValue()); 
27256             this.editorcore.pushValue();
27257             
27258             this.el.addClass('x-hidden');
27259             this.el.dom.setAttribute('tabIndex', -1);
27260             
27261             for (var i = 0; i < this.toolbars.length; i++) {
27262                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27263                     this.toolbars[i].tb.show();
27264                     this.toolbars[i].footer.show();
27265                 }
27266             }
27267             
27268             //this.deferFocus();
27269         }
27270         
27271         this.setSize(this.wrap.getSize());
27272         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27273         
27274         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27275     },
27276  
27277     // private (for BoxComponent)
27278     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27279
27280     // private (for BoxComponent)
27281     getResizeEl : function(){
27282         return this.wrap;
27283     },
27284
27285     // private (for BoxComponent)
27286     getPositionEl : function(){
27287         return this.wrap;
27288     },
27289
27290     // private
27291     initEvents : function(){
27292         this.originalValue = this.getValue();
27293     },
27294
27295     /**
27296      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27297      * @method
27298      */
27299     markInvalid : Roo.emptyFn,
27300     /**
27301      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27302      * @method
27303      */
27304     clearInvalid : Roo.emptyFn,
27305
27306     setValue : function(v){
27307         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27308         this.editorcore.pushValue();
27309     },
27310
27311     /**
27312      * update the language in the body - really done by core
27313      * @param {String} language - eg. en / ar / zh-CN etc..
27314      */
27315     updateLanguage : function(lang)
27316     {
27317         this.language = lang;
27318         this.editorcore.language = lang;
27319         this.editorcore.updateLanguage();
27320      
27321     },
27322     // private
27323     deferFocus : function(){
27324         this.focus.defer(10, this);
27325     },
27326
27327     // doc'ed in Field
27328     focus : function(){
27329         this.editorcore.focus();
27330         
27331     },
27332       
27333
27334     // private
27335     onDestroy : function(){
27336         
27337         
27338         
27339         if(this.rendered){
27340             
27341             for (var i =0; i < this.toolbars.length;i++) {
27342                 // fixme - ask toolbars for heights?
27343                 this.toolbars[i].onDestroy();
27344             }
27345             
27346             this.wrap.dom.innerHTML = '';
27347             this.wrap.remove();
27348         }
27349     },
27350
27351     // private
27352     onFirstFocus : function(){
27353         //Roo.log("onFirstFocus");
27354         this.editorcore.onFirstFocus();
27355          for (var i =0; i < this.toolbars.length;i++) {
27356             this.toolbars[i].onFirstFocus();
27357         }
27358         
27359     },
27360     
27361     // private
27362     syncValue : function()
27363     {
27364         this.editorcore.syncValue();
27365     },
27366     
27367     pushValue : function()
27368     {
27369         this.editorcore.pushValue();
27370     },
27371     
27372     setStylesheets : function(stylesheets)
27373     {
27374         this.editorcore.setStylesheets(stylesheets);
27375     },
27376     
27377     removeStylesheets : function()
27378     {
27379         this.editorcore.removeStylesheets();
27380     }
27381      
27382     
27383     // hide stuff that is not compatible
27384     /**
27385      * @event blur
27386      * @hide
27387      */
27388     /**
27389      * @event change
27390      * @hide
27391      */
27392     /**
27393      * @event focus
27394      * @hide
27395      */
27396     /**
27397      * @event specialkey
27398      * @hide
27399      */
27400     /**
27401      * @cfg {String} fieldClass @hide
27402      */
27403     /**
27404      * @cfg {String} focusClass @hide
27405      */
27406     /**
27407      * @cfg {String} autoCreate @hide
27408      */
27409     /**
27410      * @cfg {String} inputType @hide
27411      */
27412     /**
27413      * @cfg {String} invalidClass @hide
27414      */
27415     /**
27416      * @cfg {String} invalidText @hide
27417      */
27418     /**
27419      * @cfg {String} msgFx @hide
27420      */
27421     /**
27422      * @cfg {String} validateOnBlur @hide
27423      */
27424 });
27425  
27426     /*
27427  * Based on
27428  * Ext JS Library 1.1.1
27429  * Copyright(c) 2006-2007, Ext JS, LLC.
27430  *  
27431  
27432  */
27433
27434 /**
27435  * @class Roo.form.HtmlEditor.ToolbarStandard
27436  * Basic Toolbar
27437
27438  * Usage:
27439  *
27440  new Roo.form.HtmlEditor({
27441     ....
27442     toolbars : [
27443         new Roo.form.HtmlEditorToolbar1({
27444             disable : { fonts: 1 , format: 1, ..., ... , ...],
27445             btns : [ .... ]
27446         })
27447     }
27448      
27449  * 
27450  * @cfg {Object} disable List of elements to disable..
27451  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27452  * 
27453  * 
27454  * NEEDS Extra CSS? 
27455  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27456  */
27457  
27458 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27459 {
27460     
27461     Roo.apply(this, config);
27462     
27463     // default disabled, based on 'good practice'..
27464     this.disable = this.disable || {};
27465     Roo.applyIf(this.disable, {
27466         fontSize : true,
27467         colors : true,
27468         specialElements : true
27469     });
27470     
27471     
27472     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27473     // dont call parent... till later.
27474 }
27475
27476 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27477     
27478     tb: false,
27479     
27480     rendered: false,
27481     
27482     editor : false,
27483     editorcore : false,
27484     /**
27485      * @cfg {Object} disable  List of toolbar elements to disable
27486          
27487      */
27488     disable : false,
27489     
27490     
27491      /**
27492      * @cfg {String} createLinkText The default text for the create link prompt
27493      */
27494     createLinkText : 'Please enter the URL for the link:',
27495     /**
27496      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27497      */
27498     defaultLinkValue : 'http:/'+'/',
27499    
27500     
27501       /**
27502      * @cfg {Array} fontFamilies An array of available font families
27503      */
27504     fontFamilies : [
27505         'Arial',
27506         'Courier New',
27507         'Tahoma',
27508         'Times New Roman',
27509         'Verdana'
27510     ],
27511     
27512     specialChars : [
27513            "&#169;",
27514           "&#174;",     
27515           "&#8482;",    
27516           "&#163;" ,    
27517          // "&#8212;",    
27518           "&#8230;",    
27519           "&#247;" ,    
27520         //  "&#225;" ,     ?? a acute?
27521            "&#8364;"    , //Euro
27522        //   "&#8220;"    ,
27523         //  "&#8221;"    ,
27524         //  "&#8226;"    ,
27525           "&#176;"  //   , // degrees
27526
27527          // "&#233;"     , // e ecute
27528          // "&#250;"     , // u ecute?
27529     ],
27530     
27531     specialElements : [
27532         {
27533             text: "Insert Table",
27534             xtype: 'MenuItem',
27535             xns : Roo.Menu,
27536             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27537                 
27538         },
27539         {    
27540             text: "Insert Image",
27541             xtype: 'MenuItem',
27542             xns : Roo.Menu,
27543             ihtml : '<img src="about:blank"/>'
27544             
27545         }
27546         
27547          
27548     ],
27549     
27550     
27551     inputElements : [ 
27552             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27553             "input:submit", "input:button", "select", "textarea", "label" ],
27554     formats : [
27555         ["p"] ,  
27556         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27557         ["pre"],[ "code"], 
27558         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27559         ['div'],['span'],
27560         ['sup'],['sub']
27561     ],
27562     
27563     cleanStyles : [
27564         "font-size"
27565     ],
27566      /**
27567      * @cfg {String} defaultFont default font to use.
27568      */
27569     defaultFont: 'tahoma',
27570    
27571     fontSelect : false,
27572     
27573     
27574     formatCombo : false,
27575     
27576     init : function(editor)
27577     {
27578         this.editor = editor;
27579         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27580         var editorcore = this.editorcore;
27581         
27582         var _t = this;
27583         
27584         var fid = editorcore.frameId;
27585         var etb = this;
27586         function btn(id, toggle, handler){
27587             var xid = fid + '-'+ id ;
27588             return {
27589                 id : xid,
27590                 cmd : id,
27591                 cls : 'x-btn-icon x-edit-'+id,
27592                 enableToggle:toggle !== false,
27593                 scope: _t, // was editor...
27594                 handler:handler||_t.relayBtnCmd,
27595                 clickEvent:'mousedown',
27596                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27597                 tabIndex:-1
27598             };
27599         }
27600         
27601         
27602         
27603         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27604         this.tb = tb;
27605          // stop form submits
27606         tb.el.on('click', function(e){
27607             e.preventDefault(); // what does this do?
27608         });
27609
27610         if(!this.disable.font) { // && !Roo.isSafari){
27611             /* why no safari for fonts 
27612             editor.fontSelect = tb.el.createChild({
27613                 tag:'select',
27614                 tabIndex: -1,
27615                 cls:'x-font-select',
27616                 html: this.createFontOptions()
27617             });
27618             
27619             editor.fontSelect.on('change', function(){
27620                 var font = editor.fontSelect.dom.value;
27621                 editor.relayCmd('fontname', font);
27622                 editor.deferFocus();
27623             }, editor);
27624             
27625             tb.add(
27626                 editor.fontSelect.dom,
27627                 '-'
27628             );
27629             */
27630             
27631         };
27632         if(!this.disable.formats){
27633             this.formatCombo = new Roo.form.ComboBox({
27634                 store: new Roo.data.SimpleStore({
27635                     id : 'tag',
27636                     fields: ['tag'],
27637                     data : this.formats // from states.js
27638                 }),
27639                 blockFocus : true,
27640                 name : '',
27641                 //autoCreate : {tag: "div",  size: "20"},
27642                 displayField:'tag',
27643                 typeAhead: false,
27644                 mode: 'local',
27645                 editable : false,
27646                 triggerAction: 'all',
27647                 emptyText:'Add tag',
27648                 selectOnFocus:true,
27649                 width:135,
27650                 listeners : {
27651                     'select': function(c, r, i) {
27652                         editorcore.insertTag(r.get('tag'));
27653                         editor.focus();
27654                     }
27655                 }
27656
27657             });
27658             tb.addField(this.formatCombo);
27659             
27660         }
27661         
27662         if(!this.disable.format){
27663             tb.add(
27664                 btn('bold'),
27665                 btn('italic'),
27666                 btn('underline'),
27667                 btn('strikethrough')
27668             );
27669         };
27670         if(!this.disable.fontSize){
27671             tb.add(
27672                 '-',
27673                 
27674                 
27675                 btn('increasefontsize', false, editorcore.adjustFont),
27676                 btn('decreasefontsize', false, editorcore.adjustFont)
27677             );
27678         };
27679         
27680         
27681         if(!this.disable.colors){
27682             tb.add(
27683                 '-', {
27684                     id:editorcore.frameId +'-forecolor',
27685                     cls:'x-btn-icon x-edit-forecolor',
27686                     clickEvent:'mousedown',
27687                     tooltip: this.buttonTips['forecolor'] || undefined,
27688                     tabIndex:-1,
27689                     menu : new Roo.menu.ColorMenu({
27690                         allowReselect: true,
27691                         focus: Roo.emptyFn,
27692                         value:'000000',
27693                         plain:true,
27694                         selectHandler: function(cp, color){
27695                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27696                             editor.deferFocus();
27697                         },
27698                         scope: editorcore,
27699                         clickEvent:'mousedown'
27700                     })
27701                 }, {
27702                     id:editorcore.frameId +'backcolor',
27703                     cls:'x-btn-icon x-edit-backcolor',
27704                     clickEvent:'mousedown',
27705                     tooltip: this.buttonTips['backcolor'] || undefined,
27706                     tabIndex:-1,
27707                     menu : new Roo.menu.ColorMenu({
27708                         focus: Roo.emptyFn,
27709                         value:'FFFFFF',
27710                         plain:true,
27711                         allowReselect: true,
27712                         selectHandler: function(cp, color){
27713                             if(Roo.isGecko){
27714                                 editorcore.execCmd('useCSS', false);
27715                                 editorcore.execCmd('hilitecolor', color);
27716                                 editorcore.execCmd('useCSS', true);
27717                                 editor.deferFocus();
27718                             }else{
27719                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27720                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27721                                 editor.deferFocus();
27722                             }
27723                         },
27724                         scope:editorcore,
27725                         clickEvent:'mousedown'
27726                     })
27727                 }
27728             );
27729         };
27730         // now add all the items...
27731         
27732
27733         if(!this.disable.alignments){
27734             tb.add(
27735                 '-',
27736                 btn('justifyleft'),
27737                 btn('justifycenter'),
27738                 btn('justifyright')
27739             );
27740         };
27741
27742         //if(!Roo.isSafari){
27743             if(!this.disable.links){
27744                 tb.add(
27745                     '-',
27746                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27747                 );
27748             };
27749
27750             if(!this.disable.lists){
27751                 tb.add(
27752                     '-',
27753                     btn('insertorderedlist'),
27754                     btn('insertunorderedlist')
27755                 );
27756             }
27757             if(!this.disable.sourceEdit){
27758                 tb.add(
27759                     '-',
27760                     btn('sourceedit', true, function(btn){
27761                         this.toggleSourceEdit(btn.pressed);
27762                     })
27763                 );
27764             }
27765         //}
27766         
27767         var smenu = { };
27768         // special menu.. - needs to be tidied up..
27769         if (!this.disable.special) {
27770             smenu = {
27771                 text: "&#169;",
27772                 cls: 'x-edit-none',
27773                 
27774                 menu : {
27775                     items : []
27776                 }
27777             };
27778             for (var i =0; i < this.specialChars.length; i++) {
27779                 smenu.menu.items.push({
27780                     
27781                     html: this.specialChars[i],
27782                     handler: function(a,b) {
27783                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27784                         //editor.insertAtCursor(a.html);
27785                         
27786                     },
27787                     tabIndex:-1
27788                 });
27789             }
27790             
27791             
27792             tb.add(smenu);
27793             
27794             
27795         }
27796         
27797         var cmenu = { };
27798         if (!this.disable.cleanStyles) {
27799             cmenu = {
27800                 cls: 'x-btn-icon x-btn-clear',
27801                 
27802                 menu : {
27803                     items : []
27804                 }
27805             };
27806             for (var i =0; i < this.cleanStyles.length; i++) {
27807                 cmenu.menu.items.push({
27808                     actiontype : this.cleanStyles[i],
27809                     html: 'Remove ' + this.cleanStyles[i],
27810                     handler: function(a,b) {
27811 //                        Roo.log(a);
27812 //                        Roo.log(b);
27813                         var c = Roo.get(editorcore.doc.body);
27814                         c.select('[style]').each(function(s) {
27815                             s.dom.style.removeProperty(a.actiontype);
27816                         });
27817                         editorcore.syncValue();
27818                     },
27819                     tabIndex:-1
27820                 });
27821             }
27822             cmenu.menu.items.push({
27823                 actiontype : 'tablewidths',
27824                 html: 'Remove Table Widths',
27825                 handler: function(a,b) {
27826                     editorcore.cleanTableWidths();
27827                     editorcore.syncValue();
27828                 },
27829                 tabIndex:-1
27830             });
27831             cmenu.menu.items.push({
27832                 actiontype : 'word',
27833                 html: 'Remove MS Word Formating',
27834                 handler: function(a,b) {
27835                     editorcore.cleanWord();
27836                     editorcore.syncValue();
27837                 },
27838                 tabIndex:-1
27839             });
27840             
27841             cmenu.menu.items.push({
27842                 actiontype : 'all',
27843                 html: 'Remove All Styles',
27844                 handler: function(a,b) {
27845                     
27846                     var c = Roo.get(editorcore.doc.body);
27847                     c.select('[style]').each(function(s) {
27848                         s.dom.removeAttribute('style');
27849                     });
27850                     editorcore.syncValue();
27851                 },
27852                 tabIndex:-1
27853             });
27854             
27855             cmenu.menu.items.push({
27856                 actiontype : 'all',
27857                 html: 'Remove All CSS Classes',
27858                 handler: function(a,b) {
27859                     
27860                     var c = Roo.get(editorcore.doc.body);
27861                     c.select('[class]').each(function(s) {
27862                         s.dom.removeAttribute('class');
27863                     });
27864                     editorcore.cleanWord();
27865                     editorcore.syncValue();
27866                 },
27867                 tabIndex:-1
27868             });
27869             
27870              cmenu.menu.items.push({
27871                 actiontype : 'tidy',
27872                 html: 'Tidy HTML Source',
27873                 handler: function(a,b) {
27874                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27875                     editorcore.syncValue();
27876                 },
27877                 tabIndex:-1
27878             });
27879             
27880             
27881             tb.add(cmenu);
27882         }
27883          
27884         if (!this.disable.specialElements) {
27885             var semenu = {
27886                 text: "Other;",
27887                 cls: 'x-edit-none',
27888                 menu : {
27889                     items : []
27890                 }
27891             };
27892             for (var i =0; i < this.specialElements.length; i++) {
27893                 semenu.menu.items.push(
27894                     Roo.apply({ 
27895                         handler: function(a,b) {
27896                             editor.insertAtCursor(this.ihtml);
27897                         }
27898                     }, this.specialElements[i])
27899                 );
27900                     
27901             }
27902             
27903             tb.add(semenu);
27904             
27905             
27906         }
27907          
27908         
27909         if (this.btns) {
27910             for(var i =0; i< this.btns.length;i++) {
27911                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27912                 b.cls =  'x-edit-none';
27913                 
27914                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27915                     b.cls += ' x-init-enable';
27916                 }
27917                 
27918                 b.scope = editorcore;
27919                 tb.add(b);
27920             }
27921         
27922         }
27923         
27924         
27925         
27926         // disable everything...
27927         
27928         this.tb.items.each(function(item){
27929             
27930            if(
27931                 item.id != editorcore.frameId+ '-sourceedit' && 
27932                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27933             ){
27934                 
27935                 item.disable();
27936             }
27937         });
27938         this.rendered = true;
27939         
27940         // the all the btns;
27941         editor.on('editorevent', this.updateToolbar, this);
27942         // other toolbars need to implement this..
27943         //editor.on('editmodechange', this.updateToolbar, this);
27944     },
27945     
27946     
27947     relayBtnCmd : function(btn) {
27948         this.editorcore.relayCmd(btn.cmd);
27949     },
27950     // private used internally
27951     createLink : function(){
27952         //Roo.log("create link?");
27953         var ec = this.editorcore;
27954         var ar = ec.getAllAncestors();
27955         var n = false;
27956         for(var i = 0;i< ar.length;i++) {
27957             if (ar[i] && ar[i].nodeName == 'A') {
27958                 n = ar[i];
27959                 break;
27960             }
27961         }
27962         
27963         (function() {
27964             
27965             Roo.MessageBox.show({
27966                 title : "Add / Edit Link URL",
27967                 msg : "Enter the url for the link",
27968                 buttons: Roo.MessageBox.OKCANCEL,
27969                 fn: function(btn, url){
27970                     if (btn != 'ok') {
27971                         return;
27972                     }
27973                     if(url && url != 'http:/'+'/'){
27974                         if (n) {
27975                             n.setAttribute('href', url);
27976                         } else {
27977                             ec.relayCmd('createlink', url);
27978                         }
27979                     }
27980                 },
27981                 minWidth:250,
27982                 prompt:true,
27983                 //multiline: multiline,
27984                 modal : true,
27985                 value :  n  ? n.getAttribute('href') : '' 
27986             });
27987             
27988              
27989         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27990         
27991     },
27992
27993     
27994     /**
27995      * Protected method that will not generally be called directly. It triggers
27996      * a toolbar update by reading the markup state of the current selection in the editor.
27997      */
27998     updateToolbar: function(){
27999
28000         if(!this.editorcore.activated){
28001             this.editor.onFirstFocus();
28002             return;
28003         }
28004
28005         var btns = this.tb.items.map, 
28006             doc = this.editorcore.doc,
28007             frameId = this.editorcore.frameId;
28008
28009         if(!this.disable.font && !Roo.isSafari){
28010             /*
28011             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
28012             if(name != this.fontSelect.dom.value){
28013                 this.fontSelect.dom.value = name;
28014             }
28015             */
28016         }
28017         if(!this.disable.format){
28018             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
28019             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
28020             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
28021             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
28022         }
28023         if(!this.disable.alignments){
28024             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
28025             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
28026             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
28027         }
28028         if(!Roo.isSafari && !this.disable.lists){
28029             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
28030             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
28031         }
28032         
28033         var ans = this.editorcore.getAllAncestors();
28034         if (this.formatCombo) {
28035             
28036             
28037             var store = this.formatCombo.store;
28038             this.formatCombo.setValue("");
28039             for (var i =0; i < ans.length;i++) {
28040                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28041                     // select it..
28042                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28043                     break;
28044                 }
28045             }
28046         }
28047         
28048         
28049         
28050         // hides menus... - so this cant be on a menu...
28051         Roo.menu.MenuMgr.hideAll();
28052
28053         //this.editorsyncValue();
28054     },
28055    
28056     
28057     createFontOptions : function(){
28058         var buf = [], fs = this.fontFamilies, ff, lc;
28059         
28060         
28061         
28062         for(var i = 0, len = fs.length; i< len; i++){
28063             ff = fs[i];
28064             lc = ff.toLowerCase();
28065             buf.push(
28066                 '<option value="',lc,'" style="font-family:',ff,';"',
28067                     (this.defaultFont == lc ? ' selected="true">' : '>'),
28068                     ff,
28069                 '</option>'
28070             );
28071         }
28072         return buf.join('');
28073     },
28074     
28075     toggleSourceEdit : function(sourceEditMode){
28076         
28077         Roo.log("toolbar toogle");
28078         if(sourceEditMode === undefined){
28079             sourceEditMode = !this.sourceEditMode;
28080         }
28081         this.sourceEditMode = sourceEditMode === true;
28082         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
28083         // just toggle the button?
28084         if(btn.pressed !== this.sourceEditMode){
28085             btn.toggle(this.sourceEditMode);
28086             return;
28087         }
28088         
28089         if(sourceEditMode){
28090             Roo.log("disabling buttons");
28091             this.tb.items.each(function(item){
28092                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
28093                     item.disable();
28094                 }
28095             });
28096           
28097         }else{
28098             Roo.log("enabling buttons");
28099             if(this.editorcore.initialized){
28100                 this.tb.items.each(function(item){
28101                     item.enable();
28102                 });
28103                 // initialize 'blocks'
28104                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
28105                     Roo.htmleditor.Block.factory(e).updateElement(e);
28106                 },this);
28107             
28108             }
28109             
28110         }
28111         Roo.log("calling toggole on editor");
28112         // tell the editor that it's been pressed..
28113         this.editor.toggleSourceEdit(sourceEditMode);
28114        
28115     },
28116      /**
28117      * Object collection of toolbar tooltips for the buttons in the editor. The key
28118      * is the command id associated with that button and the value is a valid QuickTips object.
28119      * For example:
28120 <pre><code>
28121 {
28122     bold : {
28123         title: 'Bold (Ctrl+B)',
28124         text: 'Make the selected text bold.',
28125         cls: 'x-html-editor-tip'
28126     },
28127     italic : {
28128         title: 'Italic (Ctrl+I)',
28129         text: 'Make the selected text italic.',
28130         cls: 'x-html-editor-tip'
28131     },
28132     ...
28133 </code></pre>
28134     * @type Object
28135      */
28136     buttonTips : {
28137         bold : {
28138             title: 'Bold (Ctrl+B)',
28139             text: 'Make the selected text bold.',
28140             cls: 'x-html-editor-tip'
28141         },
28142         italic : {
28143             title: 'Italic (Ctrl+I)',
28144             text: 'Make the selected text italic.',
28145             cls: 'x-html-editor-tip'
28146         },
28147         underline : {
28148             title: 'Underline (Ctrl+U)',
28149             text: 'Underline the selected text.',
28150             cls: 'x-html-editor-tip'
28151         },
28152         strikethrough : {
28153             title: 'Strikethrough',
28154             text: 'Strikethrough the selected text.',
28155             cls: 'x-html-editor-tip'
28156         },
28157         increasefontsize : {
28158             title: 'Grow Text',
28159             text: 'Increase the font size.',
28160             cls: 'x-html-editor-tip'
28161         },
28162         decreasefontsize : {
28163             title: 'Shrink Text',
28164             text: 'Decrease the font size.',
28165             cls: 'x-html-editor-tip'
28166         },
28167         backcolor : {
28168             title: 'Text Highlight Color',
28169             text: 'Change the background color of the selected text.',
28170             cls: 'x-html-editor-tip'
28171         },
28172         forecolor : {
28173             title: 'Font Color',
28174             text: 'Change the color of the selected text.',
28175             cls: 'x-html-editor-tip'
28176         },
28177         justifyleft : {
28178             title: 'Align Text Left',
28179             text: 'Align text to the left.',
28180             cls: 'x-html-editor-tip'
28181         },
28182         justifycenter : {
28183             title: 'Center Text',
28184             text: 'Center text in the editor.',
28185             cls: 'x-html-editor-tip'
28186         },
28187         justifyright : {
28188             title: 'Align Text Right',
28189             text: 'Align text to the right.',
28190             cls: 'x-html-editor-tip'
28191         },
28192         insertunorderedlist : {
28193             title: 'Bullet List',
28194             text: 'Start a bulleted list.',
28195             cls: 'x-html-editor-tip'
28196         },
28197         insertorderedlist : {
28198             title: 'Numbered List',
28199             text: 'Start a numbered list.',
28200             cls: 'x-html-editor-tip'
28201         },
28202         createlink : {
28203             title: 'Hyperlink',
28204             text: 'Make the selected text a hyperlink.',
28205             cls: 'x-html-editor-tip'
28206         },
28207         sourceedit : {
28208             title: 'Source Edit',
28209             text: 'Switch to source editing mode.',
28210             cls: 'x-html-editor-tip'
28211         }
28212     },
28213     // private
28214     onDestroy : function(){
28215         if(this.rendered){
28216             
28217             this.tb.items.each(function(item){
28218                 if(item.menu){
28219                     item.menu.removeAll();
28220                     if(item.menu.el){
28221                         item.menu.el.destroy();
28222                     }
28223                 }
28224                 item.destroy();
28225             });
28226              
28227         }
28228     },
28229     onFirstFocus: function() {
28230         this.tb.items.each(function(item){
28231            item.enable();
28232         });
28233     }
28234 };
28235
28236
28237
28238
28239 // <script type="text/javascript">
28240 /*
28241  * Based on
28242  * Ext JS Library 1.1.1
28243  * Copyright(c) 2006-2007, Ext JS, LLC.
28244  *  
28245  
28246  */
28247
28248  
28249 /**
28250  * @class Roo.form.HtmlEditor.ToolbarContext
28251  * Context Toolbar
28252  * 
28253  * Usage:
28254  *
28255  new Roo.form.HtmlEditor({
28256     ....
28257     toolbars : [
28258         { xtype: 'ToolbarStandard', styles : {} }
28259         { xtype: 'ToolbarContext', disable : {} }
28260     ]
28261 })
28262
28263      
28264  * 
28265  * @config : {Object} disable List of elements to disable.. (not done yet.)
28266  * @config : {Object} styles  Map of styles available.
28267  * 
28268  */
28269
28270 Roo.form.HtmlEditor.ToolbarContext = function(config)
28271 {
28272     
28273     Roo.apply(this, config);
28274     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28275     // dont call parent... till later.
28276     this.styles = this.styles || {};
28277 }
28278
28279  
28280
28281 Roo.form.HtmlEditor.ToolbarContext.types = {
28282     'IMG' : [
28283         {
28284             name : 'width',
28285             title: "Width",
28286             width: 40
28287         },
28288         {
28289             name : 'height',
28290             title: "Height",
28291             width: 40
28292         },
28293         {
28294             name : 'align',
28295             title: "Align",
28296             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28297             width : 80
28298             
28299         },
28300         {
28301             name : 'border',
28302             title: "Border",
28303             width: 40
28304         },
28305         {
28306             name : 'alt',
28307             title: "Alt",
28308             width: 120
28309         },
28310         {
28311             name : 'src',
28312             title: "Src",
28313             width: 220
28314         }
28315         
28316     ],
28317     
28318     'FIGURE' : [
28319         {
28320             name : 'align',
28321             title: "Align",
28322             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28323             width : 80  
28324         }
28325     ],
28326     'A' : [
28327         {
28328             name : 'name',
28329             title: "Name",
28330             width: 50
28331         },
28332         {
28333             name : 'target',
28334             title: "Target",
28335             width: 120
28336         },
28337         {
28338             name : 'href',
28339             title: "Href",
28340             width: 220
28341         } // border?
28342         
28343     ],
28344     
28345     'INPUT' : [
28346         {
28347             name : 'name',
28348             title: "name",
28349             width: 120
28350         },
28351         {
28352             name : 'value',
28353             title: "Value",
28354             width: 120
28355         },
28356         {
28357             name : 'width',
28358             title: "Width",
28359             width: 40
28360         }
28361     ],
28362     'LABEL' : [
28363          {
28364             name : 'for',
28365             title: "For",
28366             width: 120
28367         }
28368     ],
28369     'TEXTAREA' : [
28370         {
28371             name : 'name',
28372             title: "name",
28373             width: 120
28374         },
28375         {
28376             name : 'rows',
28377             title: "Rows",
28378             width: 20
28379         },
28380         {
28381             name : 'cols',
28382             title: "Cols",
28383             width: 20
28384         }
28385     ],
28386     'SELECT' : [
28387         {
28388             name : 'name',
28389             title: "name",
28390             width: 120
28391         },
28392         {
28393             name : 'selectoptions',
28394             title: "Options",
28395             width: 200
28396         }
28397     ],
28398     
28399     // should we really allow this??
28400     // should this just be 
28401     'BODY' : [
28402         
28403         {
28404             name : 'title',
28405             title: "Title",
28406             width: 200,
28407             disabled : true
28408         }
28409     ],
28410  
28411     '*' : [
28412         // empty.
28413     ]
28414
28415 };
28416
28417 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28418 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28419
28420 Roo.form.HtmlEditor.ToolbarContext.options = {
28421         'font-family'  : [ 
28422                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28423                 [ 'Courier New', 'Courier New'],
28424                 [ 'Tahoma', 'Tahoma'],
28425                 [ 'Times New Roman,serif', 'Times'],
28426                 [ 'Verdana','Verdana' ]
28427         ]
28428 };
28429
28430 // fixme - these need to be configurable..
28431  
28432
28433 //Roo.form.HtmlEditor.ToolbarContext.types
28434
28435
28436 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28437     
28438     tb: false,
28439     
28440     rendered: false,
28441     
28442     editor : false,
28443     editorcore : false,
28444     /**
28445      * @cfg {Object} disable  List of toolbar elements to disable
28446          
28447      */
28448     disable : false,
28449     /**
28450      * @cfg {Object} styles List of styles 
28451      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28452      *
28453      * These must be defined in the page, so they get rendered correctly..
28454      * .headline { }
28455      * TD.underline { }
28456      * 
28457      */
28458     styles : false,
28459     
28460     options: false,
28461     
28462     toolbars : false,
28463     
28464     init : function(editor)
28465     {
28466         this.editor = editor;
28467         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28468         var editorcore = this.editorcore;
28469         
28470         var fid = editorcore.frameId;
28471         var etb = this;
28472         function btn(id, toggle, handler){
28473             var xid = fid + '-'+ id ;
28474             return {
28475                 id : xid,
28476                 cmd : id,
28477                 cls : 'x-btn-icon x-edit-'+id,
28478                 enableToggle:toggle !== false,
28479                 scope: editorcore, // was editor...
28480                 handler:handler||editorcore.relayBtnCmd,
28481                 clickEvent:'mousedown',
28482                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28483                 tabIndex:-1
28484             };
28485         }
28486         // create a new element.
28487         var wdiv = editor.wrap.createChild({
28488                 tag: 'div'
28489             }, editor.wrap.dom.firstChild.nextSibling, true);
28490         
28491         // can we do this more than once??
28492         
28493          // stop form submits
28494       
28495  
28496         // disable everything...
28497         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28498         this.toolbars = {};
28499         // block toolbars are built in updateToolbar when needed.
28500         for (var i in  ty) {
28501             
28502             this.toolbars[i] = this.buildToolbar(ty[i],i);
28503         }
28504         this.tb = this.toolbars.BODY;
28505         this.tb.el.show();
28506         this.buildFooter();
28507         this.footer.show();
28508         editor.on('hide', function( ) { this.footer.hide() }, this);
28509         editor.on('show', function( ) { this.footer.show() }, this);
28510         
28511          
28512         this.rendered = true;
28513         
28514         // the all the btns;
28515         editor.on('editorevent', this.updateToolbar, this);
28516         // other toolbars need to implement this..
28517         //editor.on('editmodechange', this.updateToolbar, this);
28518     },
28519     
28520     
28521     
28522     /**
28523      * Protected method that will not generally be called directly. It triggers
28524      * a toolbar update by reading the markup state of the current selection in the editor.
28525      *
28526      * Note you can force an update by calling on('editorevent', scope, false)
28527      */
28528     updateToolbar: function(editor ,ev, sel)
28529     {
28530         
28531         if (ev) {
28532             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28533         }
28534         
28535         //Roo.log(ev);
28536         // capture mouse up - this is handy for selecting images..
28537         // perhaps should go somewhere else...
28538         if(!this.editorcore.activated){
28539              this.editor.onFirstFocus();
28540             return;
28541         }
28542         //Roo.log(ev ? ev.target : 'NOTARGET');
28543         
28544         
28545         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28546         // selectNode - might want to handle IE?
28547         
28548         
28549         
28550         if (ev &&
28551             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28552             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28553             // they have click on an image...
28554             // let's see if we can change the selection...
28555             sel = ev.target;
28556             
28557             // this triggers looping?
28558             //this.editorcore.selectNode(sel);
28559              
28560         }
28561         
28562         // this forces an id..
28563         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28564              e.classList.remove('roo-ed-selection');
28565         });
28566         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28567         //Roo.get(node).addClass('roo-ed-selection');
28568       
28569         //var updateFooter = sel ? false : true; 
28570         
28571         
28572         var ans = this.editorcore.getAllAncestors();
28573         
28574         // pick
28575         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28576         
28577         if (!sel) { 
28578             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28579             sel = sel ? sel : this.editorcore.doc.body;
28580             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28581             
28582         }
28583         
28584         var tn = sel.tagName.toUpperCase();
28585         var lastSel = this.tb.selectedNode;
28586         this.tb.selectedNode = sel;
28587         var left_label = tn;
28588         
28589         // ok see if we are editing a block?
28590         
28591         var db = false;
28592         // you are not actually selecting the block.
28593         if (sel && sel.hasAttribute('data-block')) {
28594             db = sel;
28595         } else if (sel && sel.closest('[data-block]')) {
28596             
28597             db = sel.closest('[data-block]');
28598             //var cepar = sel.closest('[contenteditable=true]');
28599             //if (db && cepar && cepar.tagName != 'BODY') {
28600             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28601             //}   
28602         }
28603         
28604         
28605         var block = false;
28606         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28607         if (db && this.editorcore.enableBlocks) {
28608             block = Roo.htmleditor.Block.factory(db);
28609             
28610             
28611             if (block) {
28612                  db.className = (
28613                         db.classList.length > 0  ? db.className + ' ' : ''
28614                     )  + 'roo-ed-selection';
28615                  
28616                  // since we removed it earlier... its not there..
28617                 tn = 'BLOCK.' + db.getAttribute('data-block');
28618                 
28619                 //this.editorcore.selectNode(db);
28620                 if (typeof(this.toolbars[tn]) == 'undefined') {
28621                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28622                 }
28623                 this.toolbars[tn].selectedNode = db;
28624                 left_label = block.friendly_name;
28625                 ans = this.editorcore.getAllAncestors();
28626             }
28627             
28628                 
28629             
28630         }
28631         
28632         
28633         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28634             return; // no change?
28635         }
28636         
28637         
28638           
28639         this.tb.el.hide();
28640         ///console.log("show: " + tn);
28641         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28642         
28643         this.tb.el.show();
28644         // update name
28645         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28646         
28647         
28648         // update attributes
28649         if (block && this.tb.fields) {
28650              
28651             this.tb.fields.each(function(e) {
28652                 e.setValue(block[e.name]);
28653             });
28654             
28655             
28656         } else  if (this.tb.fields && this.tb.selectedNode) {
28657             this.tb.fields.each( function(e) {
28658                 if (e.stylename) {
28659                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28660                     return;
28661                 } 
28662                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28663             }, this);
28664             this.updateToolbarStyles(this.tb.selectedNode);  
28665         }
28666         
28667         
28668        
28669         Roo.menu.MenuMgr.hideAll();
28670
28671         
28672         
28673     
28674         // update the footer
28675         //
28676         this.updateFooter(ans);
28677              
28678     },
28679     
28680     updateToolbarStyles : function(sel)
28681     {
28682         var hasStyles = false;
28683         for(var i in this.styles) {
28684             hasStyles = true;
28685             break;
28686         }
28687         
28688         // update styles
28689         if (hasStyles && this.tb.hasStyles) { 
28690             var st = this.tb.fields.item(0);
28691             
28692             st.store.removeAll();
28693             var cn = sel.className.split(/\s+/);
28694             
28695             var avs = [];
28696             if (this.styles['*']) {
28697                 
28698                 Roo.each(this.styles['*'], function(v) {
28699                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28700                 });
28701             }
28702             if (this.styles[tn]) { 
28703                 Roo.each(this.styles[tn], function(v) {
28704                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28705                 });
28706             }
28707             
28708             st.store.loadData(avs);
28709             st.collapse();
28710             st.setValue(cn);
28711         }
28712     },
28713     
28714      
28715     updateFooter : function(ans)
28716     {
28717         var html = '';
28718         if (ans === false) {
28719             this.footDisp.dom.innerHTML = '';
28720             return;
28721         }
28722         
28723         this.footerEls = ans.reverse();
28724         Roo.each(this.footerEls, function(a,i) {
28725             if (!a) { return; }
28726             html += html.length ? ' &gt; '  :  '';
28727             
28728             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28729             
28730         });
28731        
28732         // 
28733         var sz = this.footDisp.up('td').getSize();
28734         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28735         this.footDisp.dom.style.marginLeft = '5px';
28736         
28737         this.footDisp.dom.style.overflow = 'hidden';
28738         
28739         this.footDisp.dom.innerHTML = html;
28740             
28741         
28742     },
28743    
28744        
28745     // private
28746     onDestroy : function(){
28747         if(this.rendered){
28748             
28749             this.tb.items.each(function(item){
28750                 if(item.menu){
28751                     item.menu.removeAll();
28752                     if(item.menu.el){
28753                         item.menu.el.destroy();
28754                     }
28755                 }
28756                 item.destroy();
28757             });
28758              
28759         }
28760     },
28761     onFirstFocus: function() {
28762         // need to do this for all the toolbars..
28763         this.tb.items.each(function(item){
28764            item.enable();
28765         });
28766     },
28767     buildToolbar: function(tlist, nm, friendly_name, block)
28768     {
28769         var editor = this.editor;
28770         var editorcore = this.editorcore;
28771          // create a new element.
28772         var wdiv = editor.wrap.createChild({
28773                 tag: 'div'
28774             }, editor.wrap.dom.firstChild.nextSibling, true);
28775         
28776        
28777         var tb = new Roo.Toolbar(wdiv);
28778         ///this.tb = tb; // << this sets the active toolbar..
28779         if (tlist === false && block) {
28780             tlist = block.contextMenu(this);
28781         }
28782         
28783         tb.hasStyles = false;
28784         tb.name = nm;
28785         
28786         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28787         
28788         var styles = Array.from(this.styles);
28789         
28790         
28791         // styles...
28792         if (styles && styles.length) {
28793             tb.hasStyles = true;
28794             // this needs a multi-select checkbox...
28795             tb.addField( new Roo.form.ComboBox({
28796                 store: new Roo.data.SimpleStore({
28797                     id : 'val',
28798                     fields: ['val', 'selected'],
28799                     data : [] 
28800                 }),
28801                 name : '-roo-edit-className',
28802                 attrname : 'className',
28803                 displayField: 'val',
28804                 typeAhead: false,
28805                 mode: 'local',
28806                 editable : false,
28807                 triggerAction: 'all',
28808                 emptyText:'Select Style',
28809                 selectOnFocus:true,
28810                 width: 130,
28811                 listeners : {
28812                     'select': function(c, r, i) {
28813                         // initial support only for on class per el..
28814                         tb.selectedNode.className =  r ? r.get('val') : '';
28815                         editorcore.syncValue();
28816                     }
28817                 }
28818     
28819             }));
28820         }
28821         
28822         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28823         
28824         
28825         for (var i = 0; i < tlist.length; i++) {
28826             
28827             // newer versions will use xtype cfg to create menus.
28828             if (typeof(tlist[i].xtype) != 'undefined') {
28829                 
28830                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28831                 
28832                 
28833                 continue;
28834             }
28835             
28836             var item = tlist[i];
28837             tb.add(item.title + ":&nbsp;");
28838             
28839             
28840             //optname == used so you can configure the options available..
28841             var opts = item.opts ? item.opts : false;
28842             if (item.optname) { // use the b
28843                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28844            
28845             }
28846             
28847             if (opts) {
28848                 // opts == pulldown..
28849                 tb.addField( new Roo.form.ComboBox({
28850                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28851                         id : 'val',
28852                         fields: ['val', 'display'],
28853                         data : opts  
28854                     }),
28855                     name : '-roo-edit-' + tlist[i].name,
28856                     
28857                     attrname : tlist[i].name,
28858                     stylename : item.style ? item.style : false,
28859                     
28860                     displayField: item.displayField ? item.displayField : 'val',
28861                     valueField :  'val',
28862                     typeAhead: false,
28863                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28864                     editable : false,
28865                     triggerAction: 'all',
28866                     emptyText:'Select',
28867                     selectOnFocus:true,
28868                     width: item.width ? item.width  : 130,
28869                     listeners : {
28870                         'select': function(c, r, i) {
28871                              
28872                             
28873                             if (c.stylename) {
28874                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28875                                 editorcore.syncValue();
28876                                 return;
28877                             }
28878                             if (r === false) {
28879                                 tb.selectedNode.removeAttribute(c.attrname);
28880                                 editorcore.syncValue();
28881                                 return;
28882                             }
28883                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28884                             editorcore.syncValue();
28885                         }
28886                     }
28887
28888                 }));
28889                 continue;
28890                     
28891                  
28892                 /*
28893                 tb.addField( new Roo.form.TextField({
28894                     name: i,
28895                     width: 100,
28896                     //allowBlank:false,
28897                     value: ''
28898                 }));
28899                 continue;
28900                 */
28901             }
28902             tb.addField( new Roo.form.TextField({
28903                 name: '-roo-edit-' + tlist[i].name,
28904                 attrname : tlist[i].name,
28905                 
28906                 width: item.width,
28907                 //allowBlank:true,
28908                 value: '',
28909                 listeners: {
28910                     'change' : function(f, nv, ov) {
28911                         
28912                          
28913                         tb.selectedNode.setAttribute(f.attrname, nv);
28914                         editorcore.syncValue();
28915                     }
28916                 }
28917             }));
28918              
28919         }
28920         
28921         var _this = this;
28922         var show_delete = !block || block.deleteTitle !== false;
28923         if(nm == 'BODY'){
28924             show_delete = false;
28925             tb.addSeparator();
28926         
28927             tb.addButton( {
28928                 text: 'Stylesheets',
28929
28930                 listeners : {
28931                     click : function ()
28932                     {
28933                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28934                     }
28935                 }
28936             });
28937         }
28938         
28939         tb.addFill();
28940         if (show_delete) {
28941             tb.addButton({
28942                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28943         
28944                 listeners : {
28945                     click : function ()
28946                     {
28947                         var sn = tb.selectedNode;
28948                         if (block) {
28949                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28950                             
28951                         }
28952                         if (!sn) {
28953                             return;
28954                         }
28955                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28956                         if (sn.hasAttribute('data-block')) {
28957                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28958                             sn.parentNode.removeChild(sn);
28959                             
28960                         } else if (sn && sn.tagName != 'BODY') {
28961                             // remove and keep parents.
28962                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28963                             a.replaceTag(sn);
28964                         }
28965                         
28966                         
28967                         var range = editorcore.createRange();
28968             
28969                         range.setStart(stn,0);
28970                         range.setEnd(stn,0); 
28971                         var selection = editorcore.getSelection();
28972                         selection.removeAllRanges();
28973                         selection.addRange(range);
28974                         
28975                         
28976                         //_this.updateToolbar(null, null, pn);
28977                         _this.updateToolbar(null, null, null);
28978                         _this.updateFooter(false);
28979                         
28980                     }
28981                 }
28982                 
28983                         
28984                     
28985                 
28986             });
28987         }    
28988         
28989         tb.el.on('click', function(e){
28990             e.preventDefault(); // what does this do?
28991         });
28992         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28993         tb.el.hide();
28994         
28995         // dont need to disable them... as they will get hidden
28996         return tb;
28997          
28998         
28999     },
29000     buildFooter : function()
29001     {
29002         
29003         var fel = this.editor.wrap.createChild();
29004         this.footer = new Roo.Toolbar(fel);
29005         // toolbar has scrolly on left / right?
29006         var footDisp= new Roo.Toolbar.Fill();
29007         var _t = this;
29008         this.footer.add(
29009             {
29010                 text : '&lt;',
29011                 xtype: 'Button',
29012                 handler : function() {
29013                     _t.footDisp.scrollTo('left',0,true)
29014                 }
29015             }
29016         );
29017         this.footer.add( footDisp );
29018         this.footer.add( 
29019             {
29020                 text : '&gt;',
29021                 xtype: 'Button',
29022                 handler : function() {
29023                     // no animation..
29024                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
29025                 }
29026             }
29027         );
29028         var fel = Roo.get(footDisp.el);
29029         fel.addClass('x-editor-context');
29030         this.footDispWrap = fel; 
29031         this.footDispWrap.overflow  = 'hidden';
29032         
29033         this.footDisp = fel.createChild();
29034         this.footDispWrap.on('click', this.onContextClick, this)
29035         
29036         
29037     },
29038     // when the footer contect changes
29039     onContextClick : function (ev,dom)
29040     {
29041         ev.preventDefault();
29042         var  cn = dom.className;
29043         //Roo.log(cn);
29044         if (!cn.match(/x-ed-loc-/)) {
29045             return;
29046         }
29047         var n = cn.split('-').pop();
29048         var ans = this.footerEls;
29049         var sel = ans[n];
29050         
29051         this.editorcore.selectNode(sel);
29052         
29053         
29054         this.updateToolbar(null, null, sel);
29055         
29056         
29057     }
29058     
29059     
29060     
29061     
29062     
29063 });
29064
29065
29066
29067
29068
29069 /*
29070  * Based on:
29071  * Ext JS Library 1.1.1
29072  * Copyright(c) 2006-2007, Ext JS, LLC.
29073  *
29074  * Originally Released Under LGPL - original licence link has changed is not relivant.
29075  *
29076  * Fork - LGPL
29077  * <script type="text/javascript">
29078  */
29079  
29080 /**
29081  * @class Roo.form.BasicForm
29082  * @extends Roo.util.Observable
29083  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
29084  * @constructor
29085  * @param {String/HTMLElement/Roo.Element} el The form element or its id
29086  * @param {Object} config Configuration options
29087  */
29088 Roo.form.BasicForm = function(el, config){
29089     this.allItems = [];
29090     this.childForms = [];
29091     Roo.apply(this, config);
29092     /*
29093      * The Roo.form.Field items in this form.
29094      * @type MixedCollection
29095      */
29096      
29097      
29098     this.items = new Roo.util.MixedCollection(false, function(o){
29099         return o.id || (o.id = Roo.id());
29100     });
29101     this.addEvents({
29102         /**
29103          * @event beforeaction
29104          * Fires before any action is performed. Return false to cancel the action.
29105          * @param {Form} this
29106          * @param {Action} action The action to be performed
29107          */
29108         beforeaction: true,
29109         /**
29110          * @event actionfailed
29111          * Fires when an action fails.
29112          * @param {Form} this
29113          * @param {Action} action The action that failed
29114          */
29115         actionfailed : true,
29116         /**
29117          * @event actioncomplete
29118          * Fires when an action is completed.
29119          * @param {Form} this
29120          * @param {Action} action The action that completed
29121          */
29122         actioncomplete : true
29123     });
29124     if(el){
29125         this.initEl(el);
29126     }
29127     Roo.form.BasicForm.superclass.constructor.call(this);
29128     
29129     Roo.form.BasicForm.popover.apply();
29130 };
29131
29132 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
29133     /**
29134      * @cfg {String} method
29135      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
29136      */
29137     /**
29138      * @cfg {DataReader} reader
29139      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
29140      * This is optional as there is built-in support for processing JSON.
29141      */
29142     /**
29143      * @cfg {DataReader} errorReader
29144      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
29145      * This is completely optional as there is built-in support for processing JSON.
29146      */
29147     /**
29148      * @cfg {String} url
29149      * The URL to use for form actions if one isn't supplied in the action options.
29150      */
29151     /**
29152      * @cfg {Boolean} fileUpload
29153      * Set to true if this form is a file upload.
29154      */
29155      
29156     /**
29157      * @cfg {Object} baseParams
29158      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29159      */
29160      /**
29161      
29162     /**
29163      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29164      */
29165     timeout: 30,
29166
29167     // private
29168     activeAction : null,
29169
29170     /**
29171      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29172      * or setValues() data instead of when the form was first created.
29173      */
29174     trackResetOnLoad : false,
29175     
29176     
29177     /**
29178      * childForms - used for multi-tab forms
29179      * @type {Array}
29180      */
29181     childForms : false,
29182     
29183     /**
29184      * allItems - full list of fields.
29185      * @type {Array}
29186      */
29187     allItems : false,
29188     
29189     /**
29190      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29191      * element by passing it or its id or mask the form itself by passing in true.
29192      * @type Mixed
29193      */
29194     waitMsgTarget : false,
29195     
29196     /**
29197      * @type Boolean
29198      */
29199     disableMask : false,
29200     
29201     /**
29202      * @cfg {Boolean} errorMask (true|false) default false
29203      */
29204     errorMask : false,
29205     
29206     /**
29207      * @cfg {Number} maskOffset Default 100
29208      */
29209     maskOffset : 100,
29210
29211     // private
29212     initEl : function(el){
29213         this.el = Roo.get(el);
29214         this.id = this.el.id || Roo.id();
29215         this.el.on('submit', this.onSubmit, this);
29216         this.el.addClass('x-form');
29217     },
29218
29219     // private
29220     onSubmit : function(e){
29221         e.stopEvent();
29222     },
29223
29224     /**
29225      * Returns true if client-side validation on the form is successful.
29226      * @return Boolean
29227      */
29228     isValid : function(){
29229         var valid = true;
29230         var target = false;
29231         this.items.each(function(f){
29232             if(f.validate()){
29233                 return;
29234             }
29235             
29236             valid = false;
29237                 
29238             if(!target && f.el.isVisible(true)){
29239                 target = f;
29240             }
29241         });
29242         
29243         if(this.errorMask && !valid){
29244             Roo.form.BasicForm.popover.mask(this, target);
29245         }
29246         
29247         return valid;
29248     },
29249     /**
29250      * Returns array of invalid form fields.
29251      * @return Array
29252      */
29253     
29254     invalidFields : function()
29255     {
29256         var ret = [];
29257         this.items.each(function(f){
29258             if(f.validate()){
29259                 return;
29260             }
29261             ret.push(f);
29262             
29263         });
29264         
29265         return ret;
29266     },
29267     
29268     
29269     /**
29270      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29271      * @return Boolean
29272      */
29273     isDirty : function(){
29274         var dirty = false;
29275         this.items.each(function(f){
29276            if(f.isDirty()){
29277                dirty = true;
29278                return false;
29279            }
29280         });
29281         return dirty;
29282     },
29283     
29284     /**
29285      * Returns true if any fields in this form have changed since their original load. (New version)
29286      * @return Boolean
29287      */
29288     
29289     hasChanged : function()
29290     {
29291         var dirty = false;
29292         this.items.each(function(f){
29293            if(f.hasChanged()){
29294                dirty = true;
29295                return false;
29296            }
29297         });
29298         return dirty;
29299         
29300     },
29301     /**
29302      * Resets all hasChanged to 'false' -
29303      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29304      * So hasChanged storage is only to be used for this purpose
29305      * @return Boolean
29306      */
29307     resetHasChanged : function()
29308     {
29309         this.items.each(function(f){
29310            f.resetHasChanged();
29311         });
29312         
29313     },
29314     
29315     
29316     /**
29317      * Performs a predefined action (submit or load) or custom actions you define on this form.
29318      * @param {String} actionName The name of the action type
29319      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29320      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29321      * accept other config options):
29322      * <pre>
29323 Property          Type             Description
29324 ----------------  ---------------  ----------------------------------------------------------------------------------
29325 url               String           The url for the action (defaults to the form's url)
29326 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29327 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29328 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29329                                    validate the form on the client (defaults to false)
29330      * </pre>
29331      * @return {BasicForm} this
29332      */
29333     doAction : function(action, options){
29334         if(typeof action == 'string'){
29335             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29336         }
29337         if(this.fireEvent('beforeaction', this, action) !== false){
29338             this.beforeAction(action);
29339             action.run.defer(100, action);
29340         }
29341         return this;
29342     },
29343
29344     /**
29345      * Shortcut to do a submit action.
29346      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29347      * @return {BasicForm} this
29348      */
29349     submit : function(options){
29350         this.doAction('submit', options);
29351         return this;
29352     },
29353
29354     /**
29355      * Shortcut to do a load action.
29356      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29357      * @return {BasicForm} this
29358      */
29359     load : function(options){
29360         this.doAction('load', options);
29361         return this;
29362     },
29363
29364     /**
29365      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29366      * @param {Record} record The record to edit
29367      * @return {BasicForm} this
29368      */
29369     updateRecord : function(record){
29370         record.beginEdit();
29371         var fs = record.fields;
29372         fs.each(function(f){
29373             var field = this.findField(f.name);
29374             if(field){
29375                 record.set(f.name, field.getValue());
29376             }
29377         }, this);
29378         record.endEdit();
29379         return this;
29380     },
29381
29382     /**
29383      * Loads an Roo.data.Record into this form.
29384      * @param {Record} record The record to load
29385      * @return {BasicForm} this
29386      */
29387     loadRecord : function(record){
29388         this.setValues(record.data);
29389         return this;
29390     },
29391
29392     // private
29393     beforeAction : function(action){
29394         var o = action.options;
29395         
29396         if(!this.disableMask) {
29397             if(this.waitMsgTarget === true){
29398                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29399             }else if(this.waitMsgTarget){
29400                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29401                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29402             }else {
29403                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29404             }
29405         }
29406         
29407          
29408     },
29409
29410     // private
29411     afterAction : function(action, success){
29412         this.activeAction = null;
29413         var o = action.options;
29414         
29415         if(!this.disableMask) {
29416             if(this.waitMsgTarget === true){
29417                 this.el.unmask();
29418             }else if(this.waitMsgTarget){
29419                 this.waitMsgTarget.unmask();
29420             }else{
29421                 Roo.MessageBox.updateProgress(1);
29422                 Roo.MessageBox.hide();
29423             }
29424         }
29425         
29426         if(success){
29427             if(o.reset){
29428                 this.reset();
29429             }
29430             Roo.callback(o.success, o.scope, [this, action]);
29431             this.fireEvent('actioncomplete', this, action);
29432             
29433         }else{
29434             
29435             // failure condition..
29436             // we have a scenario where updates need confirming.
29437             // eg. if a locking scenario exists..
29438             // we look for { errors : { needs_confirm : true }} in the response.
29439             if (
29440                 (typeof(action.result) != 'undefined')  &&
29441                 (typeof(action.result.errors) != 'undefined')  &&
29442                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29443            ){
29444                 var _t = this;
29445                 Roo.MessageBox.confirm(
29446                     "Change requires confirmation",
29447                     action.result.errorMsg,
29448                     function(r) {
29449                         if (r != 'yes') {
29450                             return;
29451                         }
29452                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29453                     }
29454                     
29455                 );
29456                 
29457                 
29458                 
29459                 return;
29460             }
29461             
29462             Roo.callback(o.failure, o.scope, [this, action]);
29463             // show an error message if no failed handler is set..
29464             if (!this.hasListener('actionfailed')) {
29465                 Roo.MessageBox.alert("Error",
29466                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29467                         action.result.errorMsg :
29468                         "Saving Failed, please check your entries or try again"
29469                 );
29470             }
29471             
29472             this.fireEvent('actionfailed', this, action);
29473         }
29474         
29475     },
29476
29477     /**
29478      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29479      * @param {String} id The value to search for
29480      * @return Field
29481      */
29482     findField : function(id){
29483         var field = this.items.get(id);
29484         if(!field){
29485             this.items.each(function(f){
29486                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29487                     field = f;
29488                     return false;
29489                 }
29490             });
29491         }
29492         return field || null;
29493     },
29494
29495     /**
29496      * Add a secondary form to this one, 
29497      * Used to provide tabbed forms. One form is primary, with hidden values 
29498      * which mirror the elements from the other forms.
29499      * 
29500      * @param {Roo.form.Form} form to add.
29501      * 
29502      */
29503     addForm : function(form)
29504     {
29505        
29506         if (this.childForms.indexOf(form) > -1) {
29507             // already added..
29508             return;
29509         }
29510         this.childForms.push(form);
29511         var n = '';
29512         Roo.each(form.allItems, function (fe) {
29513             
29514             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29515             if (this.findField(n)) { // already added..
29516                 return;
29517             }
29518             var add = new Roo.form.Hidden({
29519                 name : n
29520             });
29521             add.render(this.el);
29522             
29523             this.add( add );
29524         }, this);
29525         
29526     },
29527     /**
29528      * Mark fields in this form invalid in bulk.
29529      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29530      * @return {BasicForm} this
29531      */
29532     markInvalid : function(errors){
29533         if(errors instanceof Array){
29534             for(var i = 0, len = errors.length; i < len; i++){
29535                 var fieldError = errors[i];
29536                 var f = this.findField(fieldError.id);
29537                 if(f){
29538                     f.markInvalid(fieldError.msg);
29539                 }
29540             }
29541         }else{
29542             var field, id;
29543             for(id in errors){
29544                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29545                     field.markInvalid(errors[id]);
29546                 }
29547             }
29548         }
29549         Roo.each(this.childForms || [], function (f) {
29550             f.markInvalid(errors);
29551         });
29552         
29553         return this;
29554     },
29555
29556     /**
29557      * Set values for fields in this form in bulk.
29558      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29559      * @return {BasicForm} this
29560      */
29561     setValues : function(values){
29562         if(values instanceof Array){ // array of objects
29563             for(var i = 0, len = values.length; i < len; i++){
29564                 var v = values[i];
29565                 var f = this.findField(v.id);
29566                 if(f){
29567                     f.setValue(v.value);
29568                     if(this.trackResetOnLoad){
29569                         f.originalValue = f.getValue();
29570                     }
29571                 }
29572             }
29573         }else{ // object hash
29574             var field, id;
29575             for(id in values){
29576                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29577                     
29578                     if (field.setFromData && 
29579                         field.valueField && 
29580                         field.displayField &&
29581                         // combos' with local stores can 
29582                         // be queried via setValue()
29583                         // to set their value..
29584                         (field.store && !field.store.isLocal)
29585                         ) {
29586                         // it's a combo
29587                         var sd = { };
29588                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29589                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29590                         field.setFromData(sd);
29591                         
29592                     } else {
29593                         field.setValue(values[id]);
29594                     }
29595                     
29596                     
29597                     if(this.trackResetOnLoad){
29598                         field.originalValue = field.getValue();
29599                     }
29600                 }
29601             }
29602         }
29603         this.resetHasChanged();
29604         
29605         
29606         Roo.each(this.childForms || [], function (f) {
29607             f.setValues(values);
29608             f.resetHasChanged();
29609         });
29610                 
29611         return this;
29612     },
29613  
29614     /**
29615      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29616      * they are returned as an array.
29617      * @param {Boolean} asString
29618      * @return {Object}
29619      */
29620     getValues : function(asString)
29621     {
29622         if (this.childForms) {
29623             // copy values from the child forms
29624             Roo.each(this.childForms, function (f) {
29625                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29626             }, this);
29627         }
29628         
29629         // use formdata
29630         if (typeof(FormData) != 'undefined' && asString !== true) {
29631             // this relies on a 'recent' version of chrome apparently...
29632             try {
29633                 var fd = (new FormData(this.el.dom)).entries();
29634                 var ret = {};
29635                 var ent = fd.next();
29636                 while (!ent.done) {
29637                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29638                     ent = fd.next();
29639                 };
29640                 return ret;
29641             } catch(e) {
29642                 
29643             }
29644             
29645         }
29646         
29647         
29648         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29649         if(asString === true){
29650             return fs;
29651         }
29652         return Roo.urlDecode(fs);
29653     },
29654     
29655     /**
29656      * Returns the fields in this form as an object with key/value pairs. 
29657      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29658      * Normally this will not return readOnly data 
29659      * @param {Boolean} with_readonly return readonly field data.
29660      * @return {Object}
29661      */
29662     getFieldValues : function(with_readonly)
29663     {
29664         if (this.childForms) {
29665             // copy values from the child forms
29666             // should this call getFieldValues - probably not as we do not currently copy
29667             // hidden fields when we generate..
29668             Roo.each(this.childForms, function (f) {
29669                 this.setValues(f.getFieldValues());
29670             }, this);
29671         }
29672         
29673         var ret = {};
29674         this.items.each(function(f){
29675             
29676             if (f.readOnly && with_readonly !== true) {
29677                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29678                         // if a subform contains a copy of them.
29679                         // if you have subforms with the same editable data, you will need to copy the data back
29680                         // and forth.
29681             }
29682             
29683             if (!f.getName()) {
29684                 return;
29685             }
29686             var v = f.getValue();
29687             if (f.inputType =='radio') {
29688                 if (typeof(ret[f.getName()]) == 'undefined') {
29689                     ret[f.getName()] = ''; // empty..
29690                 }
29691                 
29692                 if (!f.el.dom.checked) {
29693                     return;
29694                     
29695                 }
29696                 v = f.el.dom.value;
29697                 
29698             }
29699             
29700             // not sure if this supported any more..
29701             if ((typeof(v) == 'object') && f.getRawValue) {
29702                 v = f.getRawValue() ; // dates..
29703             }
29704             // combo boxes where name != hiddenName...
29705             if (f.name != f.getName()) {
29706                 ret[f.name] = f.getRawValue();
29707             }
29708             ret[f.getName()] = v;
29709         });
29710         
29711         return ret;
29712     },
29713
29714     /**
29715      * Clears all invalid messages in this form.
29716      * @return {BasicForm} this
29717      */
29718     clearInvalid : function(){
29719         this.items.each(function(f){
29720            f.clearInvalid();
29721         });
29722         
29723         Roo.each(this.childForms || [], function (f) {
29724             f.clearInvalid();
29725         });
29726         
29727         
29728         return this;
29729     },
29730
29731     /**
29732      * Resets this form.
29733      * @return {BasicForm} this
29734      */
29735     reset : function(){
29736         this.items.each(function(f){
29737             f.reset();
29738         });
29739         
29740         Roo.each(this.childForms || [], function (f) {
29741             f.reset();
29742         });
29743         this.resetHasChanged();
29744         
29745         return this;
29746     },
29747
29748     /**
29749      * Add Roo.form components to this form.
29750      * @param {Field} field1
29751      * @param {Field} field2 (optional)
29752      * @param {Field} etc (optional)
29753      * @return {BasicForm} this
29754      */
29755     add : function(){
29756         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29757         return this;
29758     },
29759
29760
29761     /**
29762      * Removes a field from the items collection (does NOT remove its markup).
29763      * @param {Field} field
29764      * @return {BasicForm} this
29765      */
29766     remove : function(field){
29767         this.items.remove(field);
29768         return this;
29769     },
29770
29771     /**
29772      * Looks at the fields in this form, checks them for an id attribute,
29773      * and calls applyTo on the existing dom element with that id.
29774      * @return {BasicForm} this
29775      */
29776     render : function(){
29777         this.items.each(function(f){
29778             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29779                 f.applyTo(f.id);
29780             }
29781         });
29782         return this;
29783     },
29784
29785     /**
29786      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29787      * @param {Object} values
29788      * @return {BasicForm} this
29789      */
29790     applyToFields : function(o){
29791         this.items.each(function(f){
29792            Roo.apply(f, o);
29793         });
29794         return this;
29795     },
29796
29797     /**
29798      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29799      * @param {Object} values
29800      * @return {BasicForm} this
29801      */
29802     applyIfToFields : function(o){
29803         this.items.each(function(f){
29804            Roo.applyIf(f, o);
29805         });
29806         return this;
29807     }
29808 });
29809
29810 // back compat
29811 Roo.BasicForm = Roo.form.BasicForm;
29812
29813 Roo.apply(Roo.form.BasicForm, {
29814     
29815     popover : {
29816         
29817         padding : 5,
29818         
29819         isApplied : false,
29820         
29821         isMasked : false,
29822         
29823         form : false,
29824         
29825         target : false,
29826         
29827         intervalID : false,
29828         
29829         maskEl : false,
29830         
29831         apply : function()
29832         {
29833             if(this.isApplied){
29834                 return;
29835             }
29836             
29837             this.maskEl = {
29838                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29839                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29840                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29841                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29842             };
29843             
29844             this.maskEl.top.enableDisplayMode("block");
29845             this.maskEl.left.enableDisplayMode("block");
29846             this.maskEl.bottom.enableDisplayMode("block");
29847             this.maskEl.right.enableDisplayMode("block");
29848             
29849             Roo.get(document.body).on('click', function(){
29850                 this.unmask();
29851             }, this);
29852             
29853             Roo.get(document.body).on('touchstart', function(){
29854                 this.unmask();
29855             }, this);
29856             
29857             this.isApplied = true
29858         },
29859         
29860         mask : function(form, target)
29861         {
29862             this.form = form;
29863             
29864             this.target = target;
29865             
29866             if(!this.form.errorMask || !target.el){
29867                 return;
29868             }
29869             
29870             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29871             
29872             var ot = this.target.el.calcOffsetsTo(scrollable);
29873             
29874             var scrollTo = ot[1] - this.form.maskOffset;
29875             
29876             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29877             
29878             scrollable.scrollTo('top', scrollTo);
29879             
29880             var el = this.target.wrap || this.target.el;
29881             
29882             var box = el.getBox();
29883             
29884             this.maskEl.top.setStyle('position', 'absolute');
29885             this.maskEl.top.setStyle('z-index', 10000);
29886             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29887             this.maskEl.top.setLeft(0);
29888             this.maskEl.top.setTop(0);
29889             this.maskEl.top.show();
29890             
29891             this.maskEl.left.setStyle('position', 'absolute');
29892             this.maskEl.left.setStyle('z-index', 10000);
29893             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29894             this.maskEl.left.setLeft(0);
29895             this.maskEl.left.setTop(box.y - this.padding);
29896             this.maskEl.left.show();
29897
29898             this.maskEl.bottom.setStyle('position', 'absolute');
29899             this.maskEl.bottom.setStyle('z-index', 10000);
29900             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29901             this.maskEl.bottom.setLeft(0);
29902             this.maskEl.bottom.setTop(box.bottom + this.padding);
29903             this.maskEl.bottom.show();
29904
29905             this.maskEl.right.setStyle('position', 'absolute');
29906             this.maskEl.right.setStyle('z-index', 10000);
29907             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29908             this.maskEl.right.setLeft(box.right + this.padding);
29909             this.maskEl.right.setTop(box.y - this.padding);
29910             this.maskEl.right.show();
29911
29912             this.intervalID = window.setInterval(function() {
29913                 Roo.form.BasicForm.popover.unmask();
29914             }, 10000);
29915
29916             window.onwheel = function(){ return false;};
29917             
29918             (function(){ this.isMasked = true; }).defer(500, this);
29919             
29920         },
29921         
29922         unmask : function()
29923         {
29924             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29925                 return;
29926             }
29927             
29928             this.maskEl.top.setStyle('position', 'absolute');
29929             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29930             this.maskEl.top.hide();
29931
29932             this.maskEl.left.setStyle('position', 'absolute');
29933             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29934             this.maskEl.left.hide();
29935
29936             this.maskEl.bottom.setStyle('position', 'absolute');
29937             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29938             this.maskEl.bottom.hide();
29939
29940             this.maskEl.right.setStyle('position', 'absolute');
29941             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29942             this.maskEl.right.hide();
29943             
29944             window.onwheel = function(){ return true;};
29945             
29946             if(this.intervalID){
29947                 window.clearInterval(this.intervalID);
29948                 this.intervalID = false;
29949             }
29950             
29951             this.isMasked = false;
29952             
29953         }
29954         
29955     }
29956     
29957 });/*
29958  * Based on:
29959  * Ext JS Library 1.1.1
29960  * Copyright(c) 2006-2007, Ext JS, LLC.
29961  *
29962  * Originally Released Under LGPL - original licence link has changed is not relivant.
29963  *
29964  * Fork - LGPL
29965  * <script type="text/javascript">
29966  */
29967
29968 /**
29969  * @class Roo.form.Form
29970  * @extends Roo.form.BasicForm
29971  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29972  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29973  * @constructor
29974  * @param {Object} config Configuration options
29975  */
29976 Roo.form.Form = function(config){
29977     var xitems =  [];
29978     if (config.items) {
29979         xitems = config.items;
29980         delete config.items;
29981     }
29982    
29983     
29984     Roo.form.Form.superclass.constructor.call(this, null, config);
29985     this.url = this.url || this.action;
29986     if(!this.root){
29987         this.root = new Roo.form.Layout(Roo.applyIf({
29988             id: Roo.id()
29989         }, config));
29990     }
29991     this.active = this.root;
29992     /**
29993      * Array of all the buttons that have been added to this form via {@link addButton}
29994      * @type Array
29995      */
29996     this.buttons = [];
29997     this.allItems = [];
29998     this.addEvents({
29999         /**
30000          * @event clientvalidation
30001          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30002          * @param {Form} this
30003          * @param {Boolean} valid true if the form has passed client-side validation
30004          */
30005         clientvalidation: true,
30006         /**
30007          * @event rendered
30008          * Fires when the form is rendered
30009          * @param {Roo.form.Form} form
30010          */
30011         rendered : true
30012     });
30013     
30014     if (this.progressUrl) {
30015             // push a hidden field onto the list of fields..
30016             this.addxtype( {
30017                     xns: Roo.form, 
30018                     xtype : 'Hidden', 
30019                     name : 'UPLOAD_IDENTIFIER' 
30020             });
30021         }
30022         
30023     
30024     Roo.each(xitems, this.addxtype, this);
30025     
30026 };
30027
30028 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
30029      /**
30030      * @cfg {Roo.Button} buttons[] buttons at bottom of form
30031      */
30032     
30033     /**
30034      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
30035      */
30036     /**
30037      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
30038      */
30039     /**
30040      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
30041      */
30042     buttonAlign:'center',
30043
30044     /**
30045      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
30046      */
30047     minButtonWidth:75,
30048
30049     /**
30050      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
30051      * This property cascades to child containers if not set.
30052      */
30053     labelAlign:'left',
30054
30055     /**
30056      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
30057      * fires a looping event with that state. This is required to bind buttons to the valid
30058      * state using the config value formBind:true on the button.
30059      */
30060     monitorValid : false,
30061
30062     /**
30063      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
30064      */
30065     monitorPoll : 200,
30066     
30067     /**
30068      * @cfg {String} progressUrl - Url to return progress data 
30069      */
30070     
30071     progressUrl : false,
30072     /**
30073      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
30074      * sending a formdata with extra parameters - eg uploaded elements.
30075      */
30076     
30077     formData : false,
30078     
30079     /**
30080      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
30081      * fields are added and the column is closed. If no fields are passed the column remains open
30082      * until end() is called.
30083      * @param {Object} config The config to pass to the column
30084      * @param {Field} field1 (optional)
30085      * @param {Field} field2 (optional)
30086      * @param {Field} etc (optional)
30087      * @return Column The column container object
30088      */
30089     column : function(c){
30090         var col = new Roo.form.Column(c);
30091         this.start(col);
30092         if(arguments.length > 1){ // duplicate code required because of Opera
30093             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30094             this.end();
30095         }
30096         return col;
30097     },
30098
30099     /**
30100      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
30101      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
30102      * until end() is called.
30103      * @param {Object} config The config to pass to the fieldset
30104      * @param {Field} field1 (optional)
30105      * @param {Field} field2 (optional)
30106      * @param {Field} etc (optional)
30107      * @return FieldSet The fieldset container object
30108      */
30109     fieldset : function(c){
30110         var fs = new Roo.form.FieldSet(c);
30111         this.start(fs);
30112         if(arguments.length > 1){ // duplicate code required because of Opera
30113             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30114             this.end();
30115         }
30116         return fs;
30117     },
30118
30119     /**
30120      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
30121      * fields are added and the container is closed. If no fields are passed the container remains open
30122      * until end() is called.
30123      * @param {Object} config The config to pass to the Layout
30124      * @param {Field} field1 (optional)
30125      * @param {Field} field2 (optional)
30126      * @param {Field} etc (optional)
30127      * @return Layout The container object
30128      */
30129     container : function(c){
30130         var l = new Roo.form.Layout(c);
30131         this.start(l);
30132         if(arguments.length > 1){ // duplicate code required because of Opera
30133             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30134             this.end();
30135         }
30136         return l;
30137     },
30138
30139     /**
30140      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
30141      * @param {Object} container A Roo.form.Layout or subclass of Layout
30142      * @return {Form} this
30143      */
30144     start : function(c){
30145         // cascade label info
30146         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
30147         this.active.stack.push(c);
30148         c.ownerCt = this.active;
30149         this.active = c;
30150         return this;
30151     },
30152
30153     /**
30154      * Closes the current open container
30155      * @return {Form} this
30156      */
30157     end : function(){
30158         if(this.active == this.root){
30159             return this;
30160         }
30161         this.active = this.active.ownerCt;
30162         return this;
30163     },
30164
30165     /**
30166      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
30167      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30168      * as the label of the field.
30169      * @param {Field} field1
30170      * @param {Field} field2 (optional)
30171      * @param {Field} etc. (optional)
30172      * @return {Form} this
30173      */
30174     add : function(){
30175         this.active.stack.push.apply(this.active.stack, arguments);
30176         this.allItems.push.apply(this.allItems,arguments);
30177         var r = [];
30178         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30179             if(a[i].isFormField){
30180                 r.push(a[i]);
30181             }
30182         }
30183         if(r.length > 0){
30184             Roo.form.Form.superclass.add.apply(this, r);
30185         }
30186         return this;
30187     },
30188     
30189
30190     
30191     
30192     
30193      /**
30194      * Find any element that has been added to a form, using it's ID or name
30195      * This can include framesets, columns etc. along with regular fields..
30196      * @param {String} id - id or name to find.
30197      
30198      * @return {Element} e - or false if nothing found.
30199      */
30200     findbyId : function(id)
30201     {
30202         var ret = false;
30203         if (!id) {
30204             return ret;
30205         }
30206         Roo.each(this.allItems, function(f){
30207             if (f.id == id || f.name == id ){
30208                 ret = f;
30209                 return false;
30210             }
30211         });
30212         return ret;
30213     },
30214
30215     
30216     
30217     /**
30218      * Render this form into the passed container. This should only be called once!
30219      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30220      * @return {Form} this
30221      */
30222     render : function(ct)
30223     {
30224         
30225         
30226         
30227         ct = Roo.get(ct);
30228         var o = this.autoCreate || {
30229             tag: 'form',
30230             method : this.method || 'POST',
30231             id : this.id || Roo.id()
30232         };
30233         this.initEl(ct.createChild(o));
30234
30235         this.root.render(this.el);
30236         
30237        
30238              
30239         this.items.each(function(f){
30240             f.render('x-form-el-'+f.id);
30241         });
30242
30243         if(this.buttons.length > 0){
30244             // tables are required to maintain order and for correct IE layout
30245             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30246                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30247                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30248             }}, null, true);
30249             var tr = tb.getElementsByTagName('tr')[0];
30250             for(var i = 0, len = this.buttons.length; i < len; i++) {
30251                 var b = this.buttons[i];
30252                 var td = document.createElement('td');
30253                 td.className = 'x-form-btn-td';
30254                 b.render(tr.appendChild(td));
30255             }
30256         }
30257         if(this.monitorValid){ // initialize after render
30258             this.startMonitoring();
30259         }
30260         this.fireEvent('rendered', this);
30261         return this;
30262     },
30263
30264     /**
30265      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30266      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30267      * object or a valid Roo.DomHelper element config
30268      * @param {Function} handler The function called when the button is clicked
30269      * @param {Object} scope (optional) The scope of the handler function
30270      * @return {Roo.Button}
30271      */
30272     addButton : function(config, handler, scope){
30273         var bc = {
30274             handler: handler,
30275             scope: scope,
30276             minWidth: this.minButtonWidth,
30277             hideParent:true
30278         };
30279         if(typeof config == "string"){
30280             bc.text = config;
30281         }else{
30282             Roo.apply(bc, config);
30283         }
30284         var btn = new Roo.Button(null, bc);
30285         this.buttons.push(btn);
30286         return btn;
30287     },
30288
30289      /**
30290      * Adds a series of form elements (using the xtype property as the factory method.
30291      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30292      * @param {Object} config 
30293      */
30294     
30295     addxtype : function()
30296     {
30297         var ar = Array.prototype.slice.call(arguments, 0);
30298         var ret = false;
30299         for(var i = 0; i < ar.length; i++) {
30300             if (!ar[i]) {
30301                 continue; // skip -- if this happends something invalid got sent, we 
30302                 // should ignore it, as basically that interface element will not show up
30303                 // and that should be pretty obvious!!
30304             }
30305             
30306             if (Roo.form[ar[i].xtype]) {
30307                 ar[i].form = this;
30308                 var fe = Roo.factory(ar[i], Roo.form);
30309                 if (!ret) {
30310                     ret = fe;
30311                 }
30312                 fe.form = this;
30313                 if (fe.store) {
30314                     fe.store.form = this;
30315                 }
30316                 if (fe.isLayout) {  
30317                          
30318                     this.start(fe);
30319                     this.allItems.push(fe);
30320                     if (fe.items && fe.addxtype) {
30321                         fe.addxtype.apply(fe, fe.items);
30322                         delete fe.items;
30323                     }
30324                      this.end();
30325                     continue;
30326                 }
30327                 
30328                 
30329                  
30330                 this.add(fe);
30331               //  console.log('adding ' + ar[i].xtype);
30332             }
30333             if (ar[i].xtype == 'Button') {  
30334                 //console.log('adding button');
30335                 //console.log(ar[i]);
30336                 this.addButton(ar[i]);
30337                 this.allItems.push(fe);
30338                 continue;
30339             }
30340             
30341             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30342                 alert('end is not supported on xtype any more, use items');
30343             //    this.end();
30344             //    //console.log('adding end');
30345             }
30346             
30347         }
30348         return ret;
30349     },
30350     
30351     /**
30352      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30353      * option "monitorValid"
30354      */
30355     startMonitoring : function(){
30356         if(!this.bound){
30357             this.bound = true;
30358             Roo.TaskMgr.start({
30359                 run : this.bindHandler,
30360                 interval : this.monitorPoll || 200,
30361                 scope: this
30362             });
30363         }
30364     },
30365
30366     /**
30367      * Stops monitoring of the valid state of this form
30368      */
30369     stopMonitoring : function(){
30370         this.bound = false;
30371     },
30372
30373     // private
30374     bindHandler : function(){
30375         if(!this.bound){
30376             return false; // stops binding
30377         }
30378         var valid = true;
30379         this.items.each(function(f){
30380             if(!f.isValid(true)){
30381                 valid = false;
30382                 return false;
30383             }
30384         });
30385         for(var i = 0, len = this.buttons.length; i < len; i++){
30386             var btn = this.buttons[i];
30387             if(btn.formBind === true && btn.disabled === valid){
30388                 btn.setDisabled(!valid);
30389             }
30390         }
30391         this.fireEvent('clientvalidation', this, valid);
30392     }
30393     
30394     
30395     
30396     
30397     
30398     
30399     
30400     
30401 });
30402
30403
30404 // back compat
30405 Roo.Form = Roo.form.Form;
30406 /*
30407  * Based on:
30408  * Ext JS Library 1.1.1
30409  * Copyright(c) 2006-2007, Ext JS, LLC.
30410  *
30411  * Originally Released Under LGPL - original licence link has changed is not relivant.
30412  *
30413  * Fork - LGPL
30414  * <script type="text/javascript">
30415  */
30416
30417 // as we use this in bootstrap.
30418 Roo.namespace('Roo.form');
30419  /**
30420  * @class Roo.form.Action
30421  * Internal Class used to handle form actions
30422  * @constructor
30423  * @param {Roo.form.BasicForm} el The form element or its id
30424  * @param {Object} config Configuration options
30425  */
30426
30427  
30428  
30429 // define the action interface
30430 Roo.form.Action = function(form, options){
30431     this.form = form;
30432     this.options = options || {};
30433 };
30434 /**
30435  * Client Validation Failed
30436  * @const 
30437  */
30438 Roo.form.Action.CLIENT_INVALID = 'client';
30439 /**
30440  * Server Validation Failed
30441  * @const 
30442  */
30443 Roo.form.Action.SERVER_INVALID = 'server';
30444  /**
30445  * Connect to Server Failed
30446  * @const 
30447  */
30448 Roo.form.Action.CONNECT_FAILURE = 'connect';
30449 /**
30450  * Reading Data from Server Failed
30451  * @const 
30452  */
30453 Roo.form.Action.LOAD_FAILURE = 'load';
30454
30455 Roo.form.Action.prototype = {
30456     type : 'default',
30457     failureType : undefined,
30458     response : undefined,
30459     result : undefined,
30460
30461     // interface method
30462     run : function(options){
30463
30464     },
30465
30466     // interface method
30467     success : function(response){
30468
30469     },
30470
30471     // interface method
30472     handleResponse : function(response){
30473
30474     },
30475
30476     // default connection failure
30477     failure : function(response){
30478         
30479         this.response = response;
30480         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30481         this.form.afterAction(this, false);
30482     },
30483
30484     processResponse : function(response){
30485         this.response = response;
30486         if(!response.responseText){
30487             return true;
30488         }
30489         this.result = this.handleResponse(response);
30490         return this.result;
30491     },
30492
30493     // utility functions used internally
30494     getUrl : function(appendParams){
30495         var url = this.options.url || this.form.url || this.form.el.dom.action;
30496         if(appendParams){
30497             var p = this.getParams();
30498             if(p){
30499                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30500             }
30501         }
30502         return url;
30503     },
30504
30505     getMethod : function(){
30506         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30507     },
30508
30509     getParams : function(){
30510         var bp = this.form.baseParams;
30511         var p = this.options.params;
30512         if(p){
30513             if(typeof p == "object"){
30514                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30515             }else if(typeof p == 'string' && bp){
30516                 p += '&' + Roo.urlEncode(bp);
30517             }
30518         }else if(bp){
30519             p = Roo.urlEncode(bp);
30520         }
30521         return p;
30522     },
30523
30524     createCallback : function(){
30525         return {
30526             success: this.success,
30527             failure: this.failure,
30528             scope: this,
30529             timeout: (this.form.timeout*1000),
30530             upload: this.form.fileUpload ? this.success : undefined
30531         };
30532     }
30533 };
30534
30535 Roo.form.Action.Submit = function(form, options){
30536     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30537 };
30538
30539 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30540     type : 'submit',
30541
30542     haveProgress : false,
30543     uploadComplete : false,
30544     
30545     // uploadProgress indicator.
30546     uploadProgress : function()
30547     {
30548         if (!this.form.progressUrl) {
30549             return;
30550         }
30551         
30552         if (!this.haveProgress) {
30553             Roo.MessageBox.progress("Uploading", "Uploading");
30554         }
30555         if (this.uploadComplete) {
30556            Roo.MessageBox.hide();
30557            return;
30558         }
30559         
30560         this.haveProgress = true;
30561    
30562         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30563         
30564         var c = new Roo.data.Connection();
30565         c.request({
30566             url : this.form.progressUrl,
30567             params: {
30568                 id : uid
30569             },
30570             method: 'GET',
30571             success : function(req){
30572                //console.log(data);
30573                 var rdata = false;
30574                 var edata;
30575                 try  {
30576                    rdata = Roo.decode(req.responseText)
30577                 } catch (e) {
30578                     Roo.log("Invalid data from server..");
30579                     Roo.log(edata);
30580                     return;
30581                 }
30582                 if (!rdata || !rdata.success) {
30583                     Roo.log(rdata);
30584                     Roo.MessageBox.alert(Roo.encode(rdata));
30585                     return;
30586                 }
30587                 var data = rdata.data;
30588                 
30589                 if (this.uploadComplete) {
30590                    Roo.MessageBox.hide();
30591                    return;
30592                 }
30593                    
30594                 if (data){
30595                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30596                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30597                     );
30598                 }
30599                 this.uploadProgress.defer(2000,this);
30600             },
30601        
30602             failure: function(data) {
30603                 Roo.log('progress url failed ');
30604                 Roo.log(data);
30605             },
30606             scope : this
30607         });
30608            
30609     },
30610     
30611     
30612     run : function()
30613     {
30614         // run get Values on the form, so it syncs any secondary forms.
30615         this.form.getValues();
30616         
30617         var o = this.options;
30618         var method = this.getMethod();
30619         var isPost = method == 'POST';
30620         if(o.clientValidation === false || this.form.isValid()){
30621             
30622             if (this.form.progressUrl) {
30623                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30624                     (new Date() * 1) + '' + Math.random());
30625                     
30626             } 
30627             
30628             
30629             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30630                 form:this.form.el.dom,
30631                 url:this.getUrl(!isPost),
30632                 method: method,
30633                 params:isPost ? this.getParams() : null,
30634                 isUpload: this.form.fileUpload,
30635                 formData : this.form.formData
30636             }));
30637             
30638             this.uploadProgress();
30639
30640         }else if (o.clientValidation !== false){ // client validation failed
30641             this.failureType = Roo.form.Action.CLIENT_INVALID;
30642             this.form.afterAction(this, false);
30643         }
30644     },
30645
30646     success : function(response)
30647     {
30648         this.uploadComplete= true;
30649         if (this.haveProgress) {
30650             Roo.MessageBox.hide();
30651         }
30652         
30653         
30654         var result = this.processResponse(response);
30655         if(result === true || result.success){
30656             this.form.afterAction(this, true);
30657             return;
30658         }
30659         if(result.errors){
30660             this.form.markInvalid(result.errors);
30661             this.failureType = Roo.form.Action.SERVER_INVALID;
30662         }
30663         this.form.afterAction(this, false);
30664     },
30665     failure : function(response)
30666     {
30667         this.uploadComplete= true;
30668         if (this.haveProgress) {
30669             Roo.MessageBox.hide();
30670         }
30671         
30672         this.response = response;
30673         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30674         this.form.afterAction(this, false);
30675     },
30676     
30677     handleResponse : function(response){
30678         if(this.form.errorReader){
30679             var rs = this.form.errorReader.read(response);
30680             var errors = [];
30681             if(rs.records){
30682                 for(var i = 0, len = rs.records.length; i < len; i++) {
30683                     var r = rs.records[i];
30684                     errors[i] = r.data;
30685                 }
30686             }
30687             if(errors.length < 1){
30688                 errors = null;
30689             }
30690             return {
30691                 success : rs.success,
30692                 errors : errors
30693             };
30694         }
30695         var ret = false;
30696         try {
30697             ret = Roo.decode(response.responseText);
30698         } catch (e) {
30699             ret = {
30700                 success: false,
30701                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30702                 errors : []
30703             };
30704         }
30705         return ret;
30706         
30707     }
30708 });
30709
30710
30711 Roo.form.Action.Load = function(form, options){
30712     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30713     this.reader = this.form.reader;
30714 };
30715
30716 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30717     type : 'load',
30718
30719     run : function(){
30720         
30721         Roo.Ajax.request(Roo.apply(
30722                 this.createCallback(), {
30723                     method:this.getMethod(),
30724                     url:this.getUrl(false),
30725                     params:this.getParams()
30726         }));
30727     },
30728
30729     success : function(response){
30730         
30731         var result = this.processResponse(response);
30732         if(result === true || !result.success || !result.data){
30733             this.failureType = Roo.form.Action.LOAD_FAILURE;
30734             this.form.afterAction(this, false);
30735             return;
30736         }
30737         this.form.clearInvalid();
30738         this.form.setValues(result.data);
30739         this.form.afterAction(this, true);
30740     },
30741
30742     handleResponse : function(response){
30743         if(this.form.reader){
30744             var rs = this.form.reader.read(response);
30745             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30746             return {
30747                 success : rs.success,
30748                 data : data
30749             };
30750         }
30751         return Roo.decode(response.responseText);
30752     }
30753 });
30754
30755 Roo.form.Action.ACTION_TYPES = {
30756     'load' : Roo.form.Action.Load,
30757     'submit' : Roo.form.Action.Submit
30758 };/*
30759  * Based on:
30760  * Ext JS Library 1.1.1
30761  * Copyright(c) 2006-2007, Ext JS, LLC.
30762  *
30763  * Originally Released Under LGPL - original licence link has changed is not relivant.
30764  *
30765  * Fork - LGPL
30766  * <script type="text/javascript">
30767  */
30768  
30769 /**
30770  * @class Roo.form.Layout
30771  * @extends Roo.Component
30772  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30773  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30774  * @constructor
30775  * @param {Object} config Configuration options
30776  */
30777 Roo.form.Layout = function(config){
30778     var xitems = [];
30779     if (config.items) {
30780         xitems = config.items;
30781         delete config.items;
30782     }
30783     Roo.form.Layout.superclass.constructor.call(this, config);
30784     this.stack = [];
30785     Roo.each(xitems, this.addxtype, this);
30786      
30787 };
30788
30789 Roo.extend(Roo.form.Layout, Roo.Component, {
30790     /**
30791      * @cfg {String/Object} autoCreate
30792      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30793      */
30794     /**
30795      * @cfg {String/Object/Function} style
30796      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30797      * a function which returns such a specification.
30798      */
30799     /**
30800      * @cfg {String} labelAlign
30801      * Valid values are "left," "top" and "right" (defaults to "left")
30802      */
30803     /**
30804      * @cfg {Number} labelWidth
30805      * Fixed width in pixels of all field labels (defaults to undefined)
30806      */
30807     /**
30808      * @cfg {Boolean} clear
30809      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30810      */
30811     clear : true,
30812     /**
30813      * @cfg {String} labelSeparator
30814      * The separator to use after field labels (defaults to ':')
30815      */
30816     labelSeparator : ':',
30817     /**
30818      * @cfg {Boolean} hideLabels
30819      * True to suppress the display of field labels in this layout (defaults to false)
30820      */
30821     hideLabels : false,
30822
30823     // private
30824     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30825     
30826     isLayout : true,
30827     
30828     // private
30829     onRender : function(ct, position){
30830         if(this.el){ // from markup
30831             this.el = Roo.get(this.el);
30832         }else {  // generate
30833             var cfg = this.getAutoCreate();
30834             this.el = ct.createChild(cfg, position);
30835         }
30836         if(this.style){
30837             this.el.applyStyles(this.style);
30838         }
30839         if(this.labelAlign){
30840             this.el.addClass('x-form-label-'+this.labelAlign);
30841         }
30842         if(this.hideLabels){
30843             this.labelStyle = "display:none";
30844             this.elementStyle = "padding-left:0;";
30845         }else{
30846             if(typeof this.labelWidth == 'number'){
30847                 this.labelStyle = "width:"+this.labelWidth+"px;";
30848                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30849             }
30850             if(this.labelAlign == 'top'){
30851                 this.labelStyle = "width:auto;";
30852                 this.elementStyle = "padding-left:0;";
30853             }
30854         }
30855         var stack = this.stack;
30856         var slen = stack.length;
30857         if(slen > 0){
30858             if(!this.fieldTpl){
30859                 var t = new Roo.Template(
30860                     '<div class="x-form-item {5}">',
30861                         '<label for="{0}" style="{2}">{1}{4}</label>',
30862                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30863                         '</div>',
30864                     '</div><div class="x-form-clear-left"></div>'
30865                 );
30866                 t.disableFormats = true;
30867                 t.compile();
30868                 Roo.form.Layout.prototype.fieldTpl = t;
30869             }
30870             for(var i = 0; i < slen; i++) {
30871                 if(stack[i].isFormField){
30872                     this.renderField(stack[i]);
30873                 }else{
30874                     this.renderComponent(stack[i]);
30875                 }
30876             }
30877         }
30878         if(this.clear){
30879             this.el.createChild({cls:'x-form-clear'});
30880         }
30881     },
30882
30883     // private
30884     renderField : function(f){
30885         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30886                f.id, //0
30887                f.fieldLabel, //1
30888                f.labelStyle||this.labelStyle||'', //2
30889                this.elementStyle||'', //3
30890                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30891                f.itemCls||this.itemCls||''  //5
30892        ], true).getPrevSibling());
30893     },
30894
30895     // private
30896     renderComponent : function(c){
30897         c.render(c.isLayout ? this.el : this.el.createChild());    
30898     },
30899     /**
30900      * Adds a object form elements (using the xtype property as the factory method.)
30901      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30902      * @param {Object} config 
30903      */
30904     addxtype : function(o)
30905     {
30906         // create the lement.
30907         o.form = this.form;
30908         var fe = Roo.factory(o, Roo.form);
30909         this.form.allItems.push(fe);
30910         this.stack.push(fe);
30911         
30912         if (fe.isFormField) {
30913             this.form.items.add(fe);
30914         }
30915          
30916         return fe;
30917     }
30918 });
30919
30920 /**
30921  * @class Roo.form.Column
30922  * @extends Roo.form.Layout
30923  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30924  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30925  * @constructor
30926  * @param {Object} config Configuration options
30927  */
30928 Roo.form.Column = function(config){
30929     Roo.form.Column.superclass.constructor.call(this, config);
30930 };
30931
30932 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30933     /**
30934      * @cfg {Number/String} width
30935      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30936      */
30937     /**
30938      * @cfg {String/Object} autoCreate
30939      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30940      */
30941
30942     // private
30943     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30944
30945     // private
30946     onRender : function(ct, position){
30947         Roo.form.Column.superclass.onRender.call(this, ct, position);
30948         if(this.width){
30949             this.el.setWidth(this.width);
30950         }
30951     }
30952 });
30953
30954
30955 /**
30956  * @class Roo.form.Row
30957  * @extends Roo.form.Layout
30958  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30959  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30960  * @constructor
30961  * @param {Object} config Configuration options
30962  */
30963
30964  
30965 Roo.form.Row = function(config){
30966     Roo.form.Row.superclass.constructor.call(this, config);
30967 };
30968  
30969 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30970       /**
30971      * @cfg {Number/String} width
30972      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30973      */
30974     /**
30975      * @cfg {Number/String} height
30976      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30977      */
30978     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30979     
30980     padWidth : 20,
30981     // private
30982     onRender : function(ct, position){
30983         //console.log('row render');
30984         if(!this.rowTpl){
30985             var t = new Roo.Template(
30986                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30987                     '<label for="{0}" style="{2}">{1}{4}</label>',
30988                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30989                     '</div>',
30990                 '</div>'
30991             );
30992             t.disableFormats = true;
30993             t.compile();
30994             Roo.form.Layout.prototype.rowTpl = t;
30995         }
30996         this.fieldTpl = this.rowTpl;
30997         
30998         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30999         var labelWidth = 100;
31000         
31001         if ((this.labelAlign != 'top')) {
31002             if (typeof this.labelWidth == 'number') {
31003                 labelWidth = this.labelWidth
31004             }
31005             this.padWidth =  20 + labelWidth;
31006             
31007         }
31008         
31009         Roo.form.Column.superclass.onRender.call(this, ct, position);
31010         if(this.width){
31011             this.el.setWidth(this.width);
31012         }
31013         if(this.height){
31014             this.el.setHeight(this.height);
31015         }
31016     },
31017     
31018     // private
31019     renderField : function(f){
31020         f.fieldEl = this.fieldTpl.append(this.el, [
31021                f.id, f.fieldLabel,
31022                f.labelStyle||this.labelStyle||'',
31023                this.elementStyle||'',
31024                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
31025                f.itemCls||this.itemCls||'',
31026                f.width ? f.width + this.padWidth : 160 + this.padWidth
31027        ],true);
31028     }
31029 });
31030  
31031
31032 /**
31033  * @class Roo.form.FieldSet
31034  * @extends Roo.form.Layout
31035  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31036  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
31037  * @constructor
31038  * @param {Object} config Configuration options
31039  */
31040 Roo.form.FieldSet = function(config){
31041     Roo.form.FieldSet.superclass.constructor.call(this, config);
31042 };
31043
31044 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
31045     /**
31046      * @cfg {String} legend
31047      * The text to display as the legend for the FieldSet (defaults to '')
31048      */
31049     /**
31050      * @cfg {String/Object} autoCreate
31051      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
31052      */
31053
31054     // private
31055     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
31056
31057     // private
31058     onRender : function(ct, position){
31059         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
31060         if(this.legend){
31061             this.setLegend(this.legend);
31062         }
31063     },
31064
31065     // private
31066     setLegend : function(text){
31067         if(this.rendered){
31068             this.el.child('legend').update(text);
31069         }
31070     }
31071 });/*
31072  * Based on:
31073  * Ext JS Library 1.1.1
31074  * Copyright(c) 2006-2007, Ext JS, LLC.
31075  *
31076  * Originally Released Under LGPL - original licence link has changed is not relivant.
31077  *
31078  * Fork - LGPL
31079  * <script type="text/javascript">
31080  */
31081 /**
31082  * @class Roo.form.VTypes
31083  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
31084  * @static
31085  */
31086 Roo.form.VTypes = function(){
31087     // closure these in so they are only created once.
31088     var alpha = /^[a-zA-Z_]+$/;
31089     var alphanum = /^[a-zA-Z0-9_]+$/;
31090     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
31091     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31092
31093     // All these messages and functions are configurable
31094     return {
31095         /**
31096          * The function used to validate email addresses
31097          * @param {String} value The email address
31098          */
31099         'email' : function(v){
31100             return email.test(v);
31101         },
31102         /**
31103          * The error text to display when the email validation function returns false
31104          * @type String
31105          */
31106         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
31107         /**
31108          * The keystroke filter mask to be applied on email input
31109          * @type RegExp
31110          */
31111         'emailMask' : /[a-z0-9_\.\-@]/i,
31112
31113         /**
31114          * The function used to validate URLs
31115          * @param {String} value The URL
31116          */
31117         'url' : function(v){
31118             return url.test(v);
31119         },
31120         /**
31121          * The error text to display when the url validation function returns false
31122          * @type String
31123          */
31124         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
31125         
31126         /**
31127          * The function used to validate alpha values
31128          * @param {String} value The value
31129          */
31130         'alpha' : function(v){
31131             return alpha.test(v);
31132         },
31133         /**
31134          * The error text to display when the alpha validation function returns false
31135          * @type String
31136          */
31137         'alphaText' : 'This field should only contain letters and _',
31138         /**
31139          * The keystroke filter mask to be applied on alpha input
31140          * @type RegExp
31141          */
31142         'alphaMask' : /[a-z_]/i,
31143
31144         /**
31145          * The function used to validate alphanumeric values
31146          * @param {String} value The value
31147          */
31148         'alphanum' : function(v){
31149             return alphanum.test(v);
31150         },
31151         /**
31152          * The error text to display when the alphanumeric validation function returns false
31153          * @type String
31154          */
31155         'alphanumText' : 'This field should only contain letters, numbers and _',
31156         /**
31157          * The keystroke filter mask to be applied on alphanumeric input
31158          * @type RegExp
31159          */
31160         'alphanumMask' : /[a-z0-9_]/i
31161     };
31162 }();//<script type="text/javascript">
31163
31164 /**
31165  * @class Roo.form.FCKeditor
31166  * @extends Roo.form.TextArea
31167  * Wrapper around the FCKEditor http://www.fckeditor.net
31168  * @constructor
31169  * Creates a new FCKeditor
31170  * @param {Object} config Configuration options
31171  */
31172 Roo.form.FCKeditor = function(config){
31173     Roo.form.FCKeditor.superclass.constructor.call(this, config);
31174     this.addEvents({
31175          /**
31176          * @event editorinit
31177          * Fired when the editor is initialized - you can add extra handlers here..
31178          * @param {FCKeditor} this
31179          * @param {Object} the FCK object.
31180          */
31181         editorinit : true
31182     });
31183     
31184     
31185 };
31186 Roo.form.FCKeditor.editors = { };
31187 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31188 {
31189     //defaultAutoCreate : {
31190     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
31191     //},
31192     // private
31193     /**
31194      * @cfg {Object} fck options - see fck manual for details.
31195      */
31196     fckconfig : false,
31197     
31198     /**
31199      * @cfg {Object} fck toolbar set (Basic or Default)
31200      */
31201     toolbarSet : 'Basic',
31202     /**
31203      * @cfg {Object} fck BasePath
31204      */ 
31205     basePath : '/fckeditor/',
31206     
31207     
31208     frame : false,
31209     
31210     value : '',
31211     
31212    
31213     onRender : function(ct, position)
31214     {
31215         if(!this.el){
31216             this.defaultAutoCreate = {
31217                 tag: "textarea",
31218                 style:"width:300px;height:60px;",
31219                 autocomplete: "new-password"
31220             };
31221         }
31222         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31223         /*
31224         if(this.grow){
31225             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31226             if(this.preventScrollbars){
31227                 this.el.setStyle("overflow", "hidden");
31228             }
31229             this.el.setHeight(this.growMin);
31230         }
31231         */
31232         //console.log('onrender' + this.getId() );
31233         Roo.form.FCKeditor.editors[this.getId()] = this;
31234          
31235
31236         this.replaceTextarea() ;
31237         
31238     },
31239     
31240     getEditor : function() {
31241         return this.fckEditor;
31242     },
31243     /**
31244      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31245      * @param {Mixed} value The value to set
31246      */
31247     
31248     
31249     setValue : function(value)
31250     {
31251         //console.log('setValue: ' + value);
31252         
31253         if(typeof(value) == 'undefined') { // not sure why this is happending...
31254             return;
31255         }
31256         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31257         
31258         //if(!this.el || !this.getEditor()) {
31259         //    this.value = value;
31260             //this.setValue.defer(100,this,[value]);    
31261         //    return;
31262         //} 
31263         
31264         if(!this.getEditor()) {
31265             return;
31266         }
31267         
31268         this.getEditor().SetData(value);
31269         
31270         //
31271
31272     },
31273
31274     /**
31275      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31276      * @return {Mixed} value The field value
31277      */
31278     getValue : function()
31279     {
31280         
31281         if (this.frame && this.frame.dom.style.display == 'none') {
31282             return Roo.form.FCKeditor.superclass.getValue.call(this);
31283         }
31284         
31285         if(!this.el || !this.getEditor()) {
31286            
31287            // this.getValue.defer(100,this); 
31288             return this.value;
31289         }
31290        
31291         
31292         var value=this.getEditor().GetData();
31293         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31294         return Roo.form.FCKeditor.superclass.getValue.call(this);
31295         
31296
31297     },
31298
31299     /**
31300      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31301      * @return {Mixed} value The field value
31302      */
31303     getRawValue : function()
31304     {
31305         if (this.frame && this.frame.dom.style.display == 'none') {
31306             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31307         }
31308         
31309         if(!this.el || !this.getEditor()) {
31310             //this.getRawValue.defer(100,this); 
31311             return this.value;
31312             return;
31313         }
31314         
31315         
31316         
31317         var value=this.getEditor().GetData();
31318         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31319         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31320          
31321     },
31322     
31323     setSize : function(w,h) {
31324         
31325         
31326         
31327         //if (this.frame && this.frame.dom.style.display == 'none') {
31328         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31329         //    return;
31330         //}
31331         //if(!this.el || !this.getEditor()) {
31332         //    this.setSize.defer(100,this, [w,h]); 
31333         //    return;
31334         //}
31335         
31336         
31337         
31338         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31339         
31340         this.frame.dom.setAttribute('width', w);
31341         this.frame.dom.setAttribute('height', h);
31342         this.frame.setSize(w,h);
31343         
31344     },
31345     
31346     toggleSourceEdit : function(value) {
31347         
31348       
31349          
31350         this.el.dom.style.display = value ? '' : 'none';
31351         this.frame.dom.style.display = value ?  'none' : '';
31352         
31353     },
31354     
31355     
31356     focus: function(tag)
31357     {
31358         if (this.frame.dom.style.display == 'none') {
31359             return Roo.form.FCKeditor.superclass.focus.call(this);
31360         }
31361         if(!this.el || !this.getEditor()) {
31362             this.focus.defer(100,this, [tag]); 
31363             return;
31364         }
31365         
31366         
31367         
31368         
31369         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31370         this.getEditor().Focus();
31371         if (tgs.length) {
31372             if (!this.getEditor().Selection.GetSelection()) {
31373                 this.focus.defer(100,this, [tag]); 
31374                 return;
31375             }
31376             
31377             
31378             var r = this.getEditor().EditorDocument.createRange();
31379             r.setStart(tgs[0],0);
31380             r.setEnd(tgs[0],0);
31381             this.getEditor().Selection.GetSelection().removeAllRanges();
31382             this.getEditor().Selection.GetSelection().addRange(r);
31383             this.getEditor().Focus();
31384         }
31385         
31386     },
31387     
31388     
31389     
31390     replaceTextarea : function()
31391     {
31392         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31393             return ;
31394         }
31395         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31396         //{
31397             // We must check the elements firstly using the Id and then the name.
31398         var oTextarea = document.getElementById( this.getId() );
31399         
31400         var colElementsByName = document.getElementsByName( this.getId() ) ;
31401          
31402         oTextarea.style.display = 'none' ;
31403
31404         if ( oTextarea.tabIndex ) {            
31405             this.TabIndex = oTextarea.tabIndex ;
31406         }
31407         
31408         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31409         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31410         this.frame = Roo.get(this.getId() + '___Frame')
31411     },
31412     
31413     _getConfigHtml : function()
31414     {
31415         var sConfig = '' ;
31416
31417         for ( var o in this.fckconfig ) {
31418             sConfig += sConfig.length > 0  ? '&amp;' : '';
31419             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31420         }
31421
31422         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31423     },
31424     
31425     
31426     _getIFrameHtml : function()
31427     {
31428         var sFile = 'fckeditor.html' ;
31429         /* no idea what this is about..
31430         try
31431         {
31432             if ( (/fcksource=true/i).test( window.top.location.search ) )
31433                 sFile = 'fckeditor.original.html' ;
31434         }
31435         catch (e) { 
31436         */
31437
31438         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31439         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31440         
31441         
31442         var html = '<iframe id="' + this.getId() +
31443             '___Frame" src="' + sLink +
31444             '" width="' + this.width +
31445             '" height="' + this.height + '"' +
31446             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31447             ' frameborder="0" scrolling="no"></iframe>' ;
31448
31449         return html ;
31450     },
31451     
31452     _insertHtmlBefore : function( html, element )
31453     {
31454         if ( element.insertAdjacentHTML )       {
31455             // IE
31456             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31457         } else { // Gecko
31458             var oRange = document.createRange() ;
31459             oRange.setStartBefore( element ) ;
31460             var oFragment = oRange.createContextualFragment( html );
31461             element.parentNode.insertBefore( oFragment, element ) ;
31462         }
31463     }
31464     
31465     
31466   
31467     
31468     
31469     
31470     
31471
31472 });
31473
31474 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31475
31476 function FCKeditor_OnComplete(editorInstance){
31477     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31478     f.fckEditor = editorInstance;
31479     //console.log("loaded");
31480     f.fireEvent('editorinit', f, editorInstance);
31481
31482   
31483
31484  
31485
31486
31487
31488
31489
31490
31491
31492
31493
31494
31495
31496
31497
31498
31499
31500 //<script type="text/javascript">
31501 /**
31502  * @class Roo.form.GridField
31503  * @extends Roo.form.Field
31504  * Embed a grid (or editable grid into a form)
31505  * STATUS ALPHA
31506  * 
31507  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31508  * it needs 
31509  * xgrid.store = Roo.data.Store
31510  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31511  * xgrid.store.reader = Roo.data.JsonReader 
31512  * 
31513  * 
31514  * @constructor
31515  * Creates a new GridField
31516  * @param {Object} config Configuration options
31517  */
31518 Roo.form.GridField = function(config){
31519     Roo.form.GridField.superclass.constructor.call(this, config);
31520      
31521 };
31522
31523 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31524     /**
31525      * @cfg {Number} width  - used to restrict width of grid..
31526      */
31527     width : 100,
31528     /**
31529      * @cfg {Number} height - used to restrict height of grid..
31530      */
31531     height : 50,
31532      /**
31533      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31534          * 
31535          *}
31536      */
31537     xgrid : false, 
31538     /**
31539      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31540      * {tag: "input", type: "checkbox", autocomplete: "off"})
31541      */
31542    // defaultAutoCreate : { tag: 'div' },
31543     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31544     /**
31545      * @cfg {String} addTitle Text to include for adding a title.
31546      */
31547     addTitle : false,
31548     //
31549     onResize : function(){
31550         Roo.form.Field.superclass.onResize.apply(this, arguments);
31551     },
31552
31553     initEvents : function(){
31554         // Roo.form.Checkbox.superclass.initEvents.call(this);
31555         // has no events...
31556        
31557     },
31558
31559
31560     getResizeEl : function(){
31561         return this.wrap;
31562     },
31563
31564     getPositionEl : function(){
31565         return this.wrap;
31566     },
31567
31568     // private
31569     onRender : function(ct, position){
31570         
31571         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31572         var style = this.style;
31573         delete this.style;
31574         
31575         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31576         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31577         this.viewEl = this.wrap.createChild({ tag: 'div' });
31578         if (style) {
31579             this.viewEl.applyStyles(style);
31580         }
31581         if (this.width) {
31582             this.viewEl.setWidth(this.width);
31583         }
31584         if (this.height) {
31585             this.viewEl.setHeight(this.height);
31586         }
31587         //if(this.inputValue !== undefined){
31588         //this.setValue(this.value);
31589         
31590         
31591         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31592         
31593         
31594         this.grid.render();
31595         this.grid.getDataSource().on('remove', this.refreshValue, this);
31596         this.grid.getDataSource().on('update', this.refreshValue, this);
31597         this.grid.on('afteredit', this.refreshValue, this);
31598  
31599     },
31600      
31601     
31602     /**
31603      * Sets the value of the item. 
31604      * @param {String} either an object  or a string..
31605      */
31606     setValue : function(v){
31607         //this.value = v;
31608         v = v || []; // empty set..
31609         // this does not seem smart - it really only affects memoryproxy grids..
31610         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31611             var ds = this.grid.getDataSource();
31612             // assumes a json reader..
31613             var data = {}
31614             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31615             ds.loadData( data);
31616         }
31617         // clear selection so it does not get stale.
31618         if (this.grid.sm) { 
31619             this.grid.sm.clearSelections();
31620         }
31621         
31622         Roo.form.GridField.superclass.setValue.call(this, v);
31623         this.refreshValue();
31624         // should load data in the grid really....
31625     },
31626     
31627     // private
31628     refreshValue: function() {
31629          var val = [];
31630         this.grid.getDataSource().each(function(r) {
31631             val.push(r.data);
31632         });
31633         this.el.dom.value = Roo.encode(val);
31634     }
31635     
31636      
31637     
31638     
31639 });/*
31640  * Based on:
31641  * Ext JS Library 1.1.1
31642  * Copyright(c) 2006-2007, Ext JS, LLC.
31643  *
31644  * Originally Released Under LGPL - original licence link has changed is not relivant.
31645  *
31646  * Fork - LGPL
31647  * <script type="text/javascript">
31648  */
31649 /**
31650  * @class Roo.form.DisplayField
31651  * @extends Roo.form.Field
31652  * A generic Field to display non-editable data.
31653  * @cfg {Boolean} closable (true|false) default false
31654  * @constructor
31655  * Creates a new Display Field item.
31656  * @param {Object} config Configuration options
31657  */
31658 Roo.form.DisplayField = function(config){
31659     Roo.form.DisplayField.superclass.constructor.call(this, config);
31660     
31661     this.addEvents({
31662         /**
31663          * @event close
31664          * Fires after the click the close btn
31665              * @param {Roo.form.DisplayField} this
31666              */
31667         close : true
31668     });
31669 };
31670
31671 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31672     inputType:      'hidden',
31673     allowBlank:     true,
31674     readOnly:         true,
31675     
31676  
31677     /**
31678      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31679      */
31680     focusClass : undefined,
31681     /**
31682      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31683      */
31684     fieldClass: 'x-form-field',
31685     
31686      /**
31687      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31688      */
31689     valueRenderer: undefined,
31690     
31691     width: 100,
31692     /**
31693      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31694      * {tag: "input", type: "checkbox", autocomplete: "off"})
31695      */
31696      
31697  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31698  
31699     closable : false,
31700     
31701     onResize : function(){
31702         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31703         
31704     },
31705
31706     initEvents : function(){
31707         // Roo.form.Checkbox.superclass.initEvents.call(this);
31708         // has no events...
31709         
31710         if(this.closable){
31711             this.closeEl.on('click', this.onClose, this);
31712         }
31713        
31714     },
31715
31716
31717     getResizeEl : function(){
31718         return this.wrap;
31719     },
31720
31721     getPositionEl : function(){
31722         return this.wrap;
31723     },
31724
31725     // private
31726     onRender : function(ct, position){
31727         
31728         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31729         //if(this.inputValue !== undefined){
31730         this.wrap = this.el.wrap();
31731         
31732         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31733         
31734         if(this.closable){
31735             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31736         }
31737         
31738         if (this.bodyStyle) {
31739             this.viewEl.applyStyles(this.bodyStyle);
31740         }
31741         //this.viewEl.setStyle('padding', '2px');
31742         
31743         this.setValue(this.value);
31744         
31745     },
31746 /*
31747     // private
31748     initValue : Roo.emptyFn,
31749
31750   */
31751
31752         // private
31753     onClick : function(){
31754         
31755     },
31756
31757     /**
31758      * Sets the checked state of the checkbox.
31759      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31760      */
31761     setValue : function(v){
31762         this.value = v;
31763         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31764         // this might be called before we have a dom element..
31765         if (!this.viewEl) {
31766             return;
31767         }
31768         this.viewEl.dom.innerHTML = html;
31769         Roo.form.DisplayField.superclass.setValue.call(this, v);
31770
31771     },
31772     
31773     onClose : function(e)
31774     {
31775         e.preventDefault();
31776         
31777         this.fireEvent('close', this);
31778     }
31779 });/*
31780  * 
31781  * Licence- LGPL
31782  * 
31783  */
31784
31785 /**
31786  * @class Roo.form.DayPicker
31787  * @extends Roo.form.Field
31788  * A Day picker show [M] [T] [W] ....
31789  * @constructor
31790  * Creates a new Day Picker
31791  * @param {Object} config Configuration options
31792  */
31793 Roo.form.DayPicker= function(config){
31794     Roo.form.DayPicker.superclass.constructor.call(this, config);
31795      
31796 };
31797
31798 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31799     /**
31800      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31801      */
31802     focusClass : undefined,
31803     /**
31804      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31805      */
31806     fieldClass: "x-form-field",
31807    
31808     /**
31809      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31810      * {tag: "input", type: "checkbox", autocomplete: "off"})
31811      */
31812     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31813     
31814    
31815     actionMode : 'viewEl', 
31816     //
31817     // private
31818  
31819     inputType : 'hidden',
31820     
31821      
31822     inputElement: false, // real input element?
31823     basedOn: false, // ????
31824     
31825     isFormField: true, // not sure where this is needed!!!!
31826
31827     onResize : function(){
31828         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31829         if(!this.boxLabel){
31830             this.el.alignTo(this.wrap, 'c-c');
31831         }
31832     },
31833
31834     initEvents : function(){
31835         Roo.form.Checkbox.superclass.initEvents.call(this);
31836         this.el.on("click", this.onClick,  this);
31837         this.el.on("change", this.onClick,  this);
31838     },
31839
31840
31841     getResizeEl : function(){
31842         return this.wrap;
31843     },
31844
31845     getPositionEl : function(){
31846         return this.wrap;
31847     },
31848
31849     
31850     // private
31851     onRender : function(ct, position){
31852         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31853        
31854         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31855         
31856         var r1 = '<table><tr>';
31857         var r2 = '<tr class="x-form-daypick-icons">';
31858         for (var i=0; i < 7; i++) {
31859             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31860             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31861         }
31862         
31863         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31864         viewEl.select('img').on('click', this.onClick, this);
31865         this.viewEl = viewEl;   
31866         
31867         
31868         // this will not work on Chrome!!!
31869         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31870         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31871         
31872         
31873           
31874
31875     },
31876
31877     // private
31878     initValue : Roo.emptyFn,
31879
31880     /**
31881      * Returns the checked state of the checkbox.
31882      * @return {Boolean} True if checked, else false
31883      */
31884     getValue : function(){
31885         return this.el.dom.value;
31886         
31887     },
31888
31889         // private
31890     onClick : function(e){ 
31891         //this.setChecked(!this.checked);
31892         Roo.get(e.target).toggleClass('x-menu-item-checked');
31893         this.refreshValue();
31894         //if(this.el.dom.checked != this.checked){
31895         //    this.setValue(this.el.dom.checked);
31896        // }
31897     },
31898     
31899     // private
31900     refreshValue : function()
31901     {
31902         var val = '';
31903         this.viewEl.select('img',true).each(function(e,i,n)  {
31904             val += e.is(".x-menu-item-checked") ? String(n) : '';
31905         });
31906         this.setValue(val, true);
31907     },
31908
31909     /**
31910      * Sets the checked state of the checkbox.
31911      * On is always based on a string comparison between inputValue and the param.
31912      * @param {Boolean/String} value - the value to set 
31913      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31914      */
31915     setValue : function(v,suppressEvent){
31916         if (!this.el.dom) {
31917             return;
31918         }
31919         var old = this.el.dom.value ;
31920         this.el.dom.value = v;
31921         if (suppressEvent) {
31922             return ;
31923         }
31924          
31925         // update display..
31926         this.viewEl.select('img',true).each(function(e,i,n)  {
31927             
31928             var on = e.is(".x-menu-item-checked");
31929             var newv = v.indexOf(String(n)) > -1;
31930             if (on != newv) {
31931                 e.toggleClass('x-menu-item-checked');
31932             }
31933             
31934         });
31935         
31936         
31937         this.fireEvent('change', this, v, old);
31938         
31939         
31940     },
31941    
31942     // handle setting of hidden value by some other method!!?!?
31943     setFromHidden: function()
31944     {
31945         if(!this.el){
31946             return;
31947         }
31948         //console.log("SET FROM HIDDEN");
31949         //alert('setFrom hidden');
31950         this.setValue(this.el.dom.value);
31951     },
31952     
31953     onDestroy : function()
31954     {
31955         if(this.viewEl){
31956             Roo.get(this.viewEl).remove();
31957         }
31958          
31959         Roo.form.DayPicker.superclass.onDestroy.call(this);
31960     }
31961
31962 });/*
31963  * RooJS Library 1.1.1
31964  * Copyright(c) 2008-2011  Alan Knowles
31965  *
31966  * License - LGPL
31967  */
31968  
31969
31970 /**
31971  * @class Roo.form.ComboCheck
31972  * @extends Roo.form.ComboBox
31973  * A combobox for multiple select items.
31974  *
31975  * FIXME - could do with a reset button..
31976  * 
31977  * @constructor
31978  * Create a new ComboCheck
31979  * @param {Object} config Configuration options
31980  */
31981 Roo.form.ComboCheck = function(config){
31982     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31983     // should verify some data...
31984     // like
31985     // hiddenName = required..
31986     // displayField = required
31987     // valudField == required
31988     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31989     var _t = this;
31990     Roo.each(req, function(e) {
31991         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31992             throw "Roo.form.ComboCheck : missing value for: " + e;
31993         }
31994     });
31995     
31996     
31997 };
31998
31999 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
32000      
32001      
32002     editable : false,
32003      
32004     selectedClass: 'x-menu-item-checked', 
32005     
32006     // private
32007     onRender : function(ct, position){
32008         var _t = this;
32009         
32010         
32011         
32012         if(!this.tpl){
32013             var cls = 'x-combo-list';
32014
32015             
32016             this.tpl =  new Roo.Template({
32017                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
32018                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
32019                    '<span>{' + this.displayField + '}</span>' +
32020                     '</div>' 
32021                 
32022             });
32023         }
32024  
32025         
32026         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
32027         this.view.singleSelect = false;
32028         this.view.multiSelect = true;
32029         this.view.toggleSelect = true;
32030         this.pageTb.add(new Roo.Toolbar.Fill(), {
32031             
32032             text: 'Done',
32033             handler: function()
32034             {
32035                 _t.collapse();
32036             }
32037         });
32038     },
32039     
32040     onViewOver : function(e, t){
32041         // do nothing...
32042         return;
32043         
32044     },
32045     
32046     onViewClick : function(doFocus,index){
32047         return;
32048         
32049     },
32050     select: function () {
32051         //Roo.log("SELECT CALLED");
32052     },
32053      
32054     selectByValue : function(xv, scrollIntoView){
32055         var ar = this.getValueArray();
32056         var sels = [];
32057         
32058         Roo.each(ar, function(v) {
32059             if(v === undefined || v === null){
32060                 return;
32061             }
32062             var r = this.findRecord(this.valueField, v);
32063             if(r){
32064                 sels.push(this.store.indexOf(r))
32065                 
32066             }
32067         },this);
32068         this.view.select(sels);
32069         return false;
32070     },
32071     
32072     
32073     
32074     onSelect : function(record, index){
32075        // Roo.log("onselect Called");
32076        // this is only called by the clear button now..
32077         this.view.clearSelections();
32078         this.setValue('[]');
32079         if (this.value != this.valueBefore) {
32080             this.fireEvent('change', this, this.value, this.valueBefore);
32081             this.valueBefore = this.value;
32082         }
32083     },
32084     getValueArray : function()
32085     {
32086         var ar = [] ;
32087         
32088         try {
32089             //Roo.log(this.value);
32090             if (typeof(this.value) == 'undefined') {
32091                 return [];
32092             }
32093             var ar = Roo.decode(this.value);
32094             return  ar instanceof Array ? ar : []; //?? valid?
32095             
32096         } catch(e) {
32097             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
32098             return [];
32099         }
32100          
32101     },
32102     expand : function ()
32103     {
32104         
32105         Roo.form.ComboCheck.superclass.expand.call(this);
32106         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
32107         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
32108         
32109
32110     },
32111     
32112     collapse : function(){
32113         Roo.form.ComboCheck.superclass.collapse.call(this);
32114         var sl = this.view.getSelectedIndexes();
32115         var st = this.store;
32116         var nv = [];
32117         var tv = [];
32118         var r;
32119         Roo.each(sl, function(i) {
32120             r = st.getAt(i);
32121             nv.push(r.get(this.valueField));
32122         },this);
32123         this.setValue(Roo.encode(nv));
32124         if (this.value != this.valueBefore) {
32125
32126             this.fireEvent('change', this, this.value, this.valueBefore);
32127             this.valueBefore = this.value;
32128         }
32129         
32130     },
32131     
32132     setValue : function(v){
32133         // Roo.log(v);
32134         this.value = v;
32135         
32136         var vals = this.getValueArray();
32137         var tv = [];
32138         Roo.each(vals, function(k) {
32139             var r = this.findRecord(this.valueField, k);
32140             if(r){
32141                 tv.push(r.data[this.displayField]);
32142             }else if(this.valueNotFoundText !== undefined){
32143                 tv.push( this.valueNotFoundText );
32144             }
32145         },this);
32146        // Roo.log(tv);
32147         
32148         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
32149         this.hiddenField.value = v;
32150         this.value = v;
32151     }
32152     
32153 });/*
32154  * Based on:
32155  * Ext JS Library 1.1.1
32156  * Copyright(c) 2006-2007, Ext JS, LLC.
32157  *
32158  * Originally Released Under LGPL - original licence link has changed is not relivant.
32159  *
32160  * Fork - LGPL
32161  * <script type="text/javascript">
32162  */
32163  
32164 /**
32165  * @class Roo.form.Signature
32166  * @extends Roo.form.Field
32167  * Signature field.  
32168  * @constructor
32169  * 
32170  * @param {Object} config Configuration options
32171  */
32172
32173 Roo.form.Signature = function(config){
32174     Roo.form.Signature.superclass.constructor.call(this, config);
32175     
32176     this.addEvents({// not in used??
32177          /**
32178          * @event confirm
32179          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32180              * @param {Roo.form.Signature} combo This combo box
32181              */
32182         'confirm' : true,
32183         /**
32184          * @event reset
32185          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32186              * @param {Roo.form.ComboBox} combo This combo box
32187              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32188              */
32189         'reset' : true
32190     });
32191 };
32192
32193 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
32194     /**
32195      * @cfg {Object} labels Label to use when rendering a form.
32196      * defaults to 
32197      * labels : { 
32198      *      clear : "Clear",
32199      *      confirm : "Confirm"
32200      *  }
32201      */
32202     labels : { 
32203         clear : "Clear",
32204         confirm : "Confirm"
32205     },
32206     /**
32207      * @cfg {Number} width The signature panel width (defaults to 300)
32208      */
32209     width: 300,
32210     /**
32211      * @cfg {Number} height The signature panel height (defaults to 100)
32212      */
32213     height : 100,
32214     /**
32215      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32216      */
32217     allowBlank : false,
32218     
32219     //private
32220     // {Object} signPanel The signature SVG panel element (defaults to {})
32221     signPanel : {},
32222     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32223     isMouseDown : false,
32224     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32225     isConfirmed : false,
32226     // {String} signatureTmp SVG mapping string (defaults to empty string)
32227     signatureTmp : '',
32228     
32229     
32230     defaultAutoCreate : { // modified by initCompnoent..
32231         tag: "input",
32232         type:"hidden"
32233     },
32234
32235     // private
32236     onRender : function(ct, position){
32237         
32238         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32239         
32240         this.wrap = this.el.wrap({
32241             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32242         });
32243         
32244         this.createToolbar(this);
32245         this.signPanel = this.wrap.createChild({
32246                 tag: 'div',
32247                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32248             }, this.el
32249         );
32250             
32251         this.svgID = Roo.id();
32252         this.svgEl = this.signPanel.createChild({
32253               xmlns : 'http://www.w3.org/2000/svg',
32254               tag : 'svg',
32255               id : this.svgID + "-svg",
32256               width: this.width,
32257               height: this.height,
32258               viewBox: '0 0 '+this.width+' '+this.height,
32259               cn : [
32260                 {
32261                     tag: "rect",
32262                     id: this.svgID + "-svg-r",
32263                     width: this.width,
32264                     height: this.height,
32265                     fill: "#ffa"
32266                 },
32267                 {
32268                     tag: "line",
32269                     id: this.svgID + "-svg-l",
32270                     x1: "0", // start
32271                     y1: (this.height*0.8), // start set the line in 80% of height
32272                     x2: this.width, // end
32273                     y2: (this.height*0.8), // end set the line in 80% of height
32274                     'stroke': "#666",
32275                     'stroke-width': "1",
32276                     'stroke-dasharray': "3",
32277                     'shape-rendering': "crispEdges",
32278                     'pointer-events': "none"
32279                 },
32280                 {
32281                     tag: "path",
32282                     id: this.svgID + "-svg-p",
32283                     'stroke': "navy",
32284                     'stroke-width': "3",
32285                     'fill': "none",
32286                     'pointer-events': 'none'
32287                 }
32288               ]
32289         });
32290         this.createSVG();
32291         this.svgBox = this.svgEl.dom.getScreenCTM();
32292     },
32293     createSVG : function(){ 
32294         var svg = this.signPanel;
32295         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32296         var t = this;
32297
32298         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32299         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32300         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32301         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32302         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32303         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32304         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32305         
32306     },
32307     isTouchEvent : function(e){
32308         return e.type.match(/^touch/);
32309     },
32310     getCoords : function (e) {
32311         var pt    = this.svgEl.dom.createSVGPoint();
32312         pt.x = e.clientX; 
32313         pt.y = e.clientY;
32314         if (this.isTouchEvent(e)) {
32315             pt.x =  e.targetTouches[0].clientX;
32316             pt.y = e.targetTouches[0].clientY;
32317         }
32318         var a = this.svgEl.dom.getScreenCTM();
32319         var b = a.inverse();
32320         var mx = pt.matrixTransform(b);
32321         return mx.x + ',' + mx.y;
32322     },
32323     //mouse event headler 
32324     down : function (e) {
32325         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32326         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32327         
32328         this.isMouseDown = true;
32329         
32330         e.preventDefault();
32331     },
32332     move : function (e) {
32333         if (this.isMouseDown) {
32334             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32335             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32336         }
32337         
32338         e.preventDefault();
32339     },
32340     up : function (e) {
32341         this.isMouseDown = false;
32342         var sp = this.signatureTmp.split(' ');
32343         
32344         if(sp.length > 1){
32345             if(!sp[sp.length-2].match(/^L/)){
32346                 sp.pop();
32347                 sp.pop();
32348                 sp.push("");
32349                 this.signatureTmp = sp.join(" ");
32350             }
32351         }
32352         if(this.getValue() != this.signatureTmp){
32353             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32354             this.isConfirmed = false;
32355         }
32356         e.preventDefault();
32357     },
32358     
32359     /**
32360      * Protected method that will not generally be called directly. It
32361      * is called when the editor creates its toolbar. Override this method if you need to
32362      * add custom toolbar buttons.
32363      * @param {HtmlEditor} editor
32364      */
32365     createToolbar : function(editor){
32366          function btn(id, toggle, handler){
32367             var xid = fid + '-'+ id ;
32368             return {
32369                 id : xid,
32370                 cmd : id,
32371                 cls : 'x-btn-icon x-edit-'+id,
32372                 enableToggle:toggle !== false,
32373                 scope: editor, // was editor...
32374                 handler:handler||editor.relayBtnCmd,
32375                 clickEvent:'mousedown',
32376                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32377                 tabIndex:-1
32378             };
32379         }
32380         
32381         
32382         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32383         this.tb = tb;
32384         this.tb.add(
32385            {
32386                 cls : ' x-signature-btn x-signature-'+id,
32387                 scope: editor, // was editor...
32388                 handler: this.reset,
32389                 clickEvent:'mousedown',
32390                 text: this.labels.clear
32391             },
32392             {
32393                  xtype : 'Fill',
32394                  xns: Roo.Toolbar
32395             }, 
32396             {
32397                 cls : '  x-signature-btn x-signature-'+id,
32398                 scope: editor, // was editor...
32399                 handler: this.confirmHandler,
32400                 clickEvent:'mousedown',
32401                 text: this.labels.confirm
32402             }
32403         );
32404     
32405     },
32406     //public
32407     /**
32408      * when user is clicked confirm then show this image.....
32409      * 
32410      * @return {String} Image Data URI
32411      */
32412     getImageDataURI : function(){
32413         var svg = this.svgEl.dom.parentNode.innerHTML;
32414         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32415         return src; 
32416     },
32417     /**
32418      * 
32419      * @return {Boolean} this.isConfirmed
32420      */
32421     getConfirmed : function(){
32422         return this.isConfirmed;
32423     },
32424     /**
32425      * 
32426      * @return {Number} this.width
32427      */
32428     getWidth : function(){
32429         return this.width;
32430     },
32431     /**
32432      * 
32433      * @return {Number} this.height
32434      */
32435     getHeight : function(){
32436         return this.height;
32437     },
32438     // private
32439     getSignature : function(){
32440         return this.signatureTmp;
32441     },
32442     // private
32443     reset : function(){
32444         this.signatureTmp = '';
32445         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32446         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32447         this.isConfirmed = false;
32448         Roo.form.Signature.superclass.reset.call(this);
32449     },
32450     setSignature : function(s){
32451         this.signatureTmp = s;
32452         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32453         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32454         this.setValue(s);
32455         this.isConfirmed = false;
32456         Roo.form.Signature.superclass.reset.call(this);
32457     }, 
32458     test : function(){
32459 //        Roo.log(this.signPanel.dom.contentWindow.up())
32460     },
32461     //private
32462     setConfirmed : function(){
32463         
32464         
32465         
32466 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32467     },
32468     // private
32469     confirmHandler : function(){
32470         if(!this.getSignature()){
32471             return;
32472         }
32473         
32474         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32475         this.setValue(this.getSignature());
32476         this.isConfirmed = true;
32477         
32478         this.fireEvent('confirm', this);
32479     },
32480     // private
32481     // Subclasses should provide the validation implementation by overriding this
32482     validateValue : function(value){
32483         if(this.allowBlank){
32484             return true;
32485         }
32486         
32487         if(this.isConfirmed){
32488             return true;
32489         }
32490         return false;
32491     }
32492 });/*
32493  * Based on:
32494  * Ext JS Library 1.1.1
32495  * Copyright(c) 2006-2007, Ext JS, LLC.
32496  *
32497  * Originally Released Under LGPL - original licence link has changed is not relivant.
32498  *
32499  * Fork - LGPL
32500  * <script type="text/javascript">
32501  */
32502  
32503
32504 /**
32505  * @class Roo.form.ComboBox
32506  * @extends Roo.form.TriggerField
32507  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32508  * @constructor
32509  * Create a new ComboBox.
32510  * @param {Object} config Configuration options
32511  */
32512 Roo.form.Select = function(config){
32513     Roo.form.Select.superclass.constructor.call(this, config);
32514      
32515 };
32516
32517 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32518     /**
32519      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32520      */
32521     /**
32522      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32523      * rendering into an Roo.Editor, defaults to false)
32524      */
32525     /**
32526      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32527      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32528      */
32529     /**
32530      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32531      */
32532     /**
32533      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32534      * the dropdown list (defaults to undefined, with no header element)
32535      */
32536
32537      /**
32538      * @cfg {String/Roo.Template} tpl The template to use to render the output
32539      */
32540      
32541     // private
32542     defaultAutoCreate : {tag: "select"  },
32543     /**
32544      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32545      */
32546     listWidth: undefined,
32547     /**
32548      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32549      * mode = 'remote' or 'text' if mode = 'local')
32550      */
32551     displayField: undefined,
32552     /**
32553      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32554      * mode = 'remote' or 'value' if mode = 'local'). 
32555      * Note: use of a valueField requires the user make a selection
32556      * in order for a value to be mapped.
32557      */
32558     valueField: undefined,
32559     
32560     
32561     /**
32562      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32563      * field's data value (defaults to the underlying DOM element's name)
32564      */
32565     hiddenName: undefined,
32566     /**
32567      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32568      */
32569     listClass: '',
32570     /**
32571      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32572      */
32573     selectedClass: 'x-combo-selected',
32574     /**
32575      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32576      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32577      * which displays a downward arrow icon).
32578      */
32579     triggerClass : 'x-form-arrow-trigger',
32580     /**
32581      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32582      */
32583     shadow:'sides',
32584     /**
32585      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32586      * anchor positions (defaults to 'tl-bl')
32587      */
32588     listAlign: 'tl-bl?',
32589     /**
32590      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32591      */
32592     maxHeight: 300,
32593     /**
32594      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32595      * query specified by the allQuery config option (defaults to 'query')
32596      */
32597     triggerAction: 'query',
32598     /**
32599      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32600      * (defaults to 4, does not apply if editable = false)
32601      */
32602     minChars : 4,
32603     /**
32604      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32605      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32606      */
32607     typeAhead: false,
32608     /**
32609      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32610      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32611      */
32612     queryDelay: 500,
32613     /**
32614      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32615      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32616      */
32617     pageSize: 0,
32618     /**
32619      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32620      * when editable = true (defaults to false)
32621      */
32622     selectOnFocus:false,
32623     /**
32624      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32625      */
32626     queryParam: 'query',
32627     /**
32628      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32629      * when mode = 'remote' (defaults to 'Loading...')
32630      */
32631     loadingText: 'Loading...',
32632     /**
32633      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32634      */
32635     resizable: false,
32636     /**
32637      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32638      */
32639     handleHeight : 8,
32640     /**
32641      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32642      * traditional select (defaults to true)
32643      */
32644     editable: true,
32645     /**
32646      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32647      */
32648     allQuery: '',
32649     /**
32650      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32651      */
32652     mode: 'remote',
32653     /**
32654      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32655      * listWidth has a higher value)
32656      */
32657     minListWidth : 70,
32658     /**
32659      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32660      * allow the user to set arbitrary text into the field (defaults to false)
32661      */
32662     forceSelection:false,
32663     /**
32664      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32665      * if typeAhead = true (defaults to 250)
32666      */
32667     typeAheadDelay : 250,
32668     /**
32669      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32670      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32671      */
32672     valueNotFoundText : undefined,
32673     
32674     /**
32675      * @cfg {String} defaultValue The value displayed after loading the store.
32676      */
32677     defaultValue: '',
32678     
32679     /**
32680      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32681      */
32682     blockFocus : false,
32683     
32684     /**
32685      * @cfg {Boolean} disableClear Disable showing of clear button.
32686      */
32687     disableClear : false,
32688     /**
32689      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32690      */
32691     alwaysQuery : false,
32692     
32693     //private
32694     addicon : false,
32695     editicon: false,
32696     
32697     // element that contains real text value.. (when hidden is used..)
32698      
32699     // private
32700     onRender : function(ct, position){
32701         Roo.form.Field.prototype.onRender.call(this, ct, position);
32702         
32703         if(this.store){
32704             this.store.on('beforeload', this.onBeforeLoad, this);
32705             this.store.on('load', this.onLoad, this);
32706             this.store.on('loadexception', this.onLoadException, this);
32707             this.store.load({});
32708         }
32709         
32710         
32711         
32712     },
32713
32714     // private
32715     initEvents : function(){
32716         //Roo.form.ComboBox.superclass.initEvents.call(this);
32717  
32718     },
32719
32720     onDestroy : function(){
32721        
32722         if(this.store){
32723             this.store.un('beforeload', this.onBeforeLoad, this);
32724             this.store.un('load', this.onLoad, this);
32725             this.store.un('loadexception', this.onLoadException, this);
32726         }
32727         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32728     },
32729
32730     // private
32731     fireKey : function(e){
32732         if(e.isNavKeyPress() && !this.list.isVisible()){
32733             this.fireEvent("specialkey", this, e);
32734         }
32735     },
32736
32737     // private
32738     onResize: function(w, h){
32739         
32740         return; 
32741     
32742         
32743     },
32744
32745     /**
32746      * Allow or prevent the user from directly editing the field text.  If false is passed,
32747      * the user will only be able to select from the items defined in the dropdown list.  This method
32748      * is the runtime equivalent of setting the 'editable' config option at config time.
32749      * @param {Boolean} value True to allow the user to directly edit the field text
32750      */
32751     setEditable : function(value){
32752          
32753     },
32754
32755     // private
32756     onBeforeLoad : function(){
32757         
32758         Roo.log("Select before load");
32759         return;
32760     
32761         this.innerList.update(this.loadingText ?
32762                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32763         //this.restrictHeight();
32764         this.selectedIndex = -1;
32765     },
32766
32767     // private
32768     onLoad : function(){
32769
32770     
32771         var dom = this.el.dom;
32772         dom.innerHTML = '';
32773          var od = dom.ownerDocument;
32774          
32775         if (this.emptyText) {
32776             var op = od.createElement('option');
32777             op.setAttribute('value', '');
32778             op.innerHTML = String.format('{0}', this.emptyText);
32779             dom.appendChild(op);
32780         }
32781         if(this.store.getCount() > 0){
32782            
32783             var vf = this.valueField;
32784             var df = this.displayField;
32785             this.store.data.each(function(r) {
32786                 // which colmsn to use... testing - cdoe / title..
32787                 var op = od.createElement('option');
32788                 op.setAttribute('value', r.data[vf]);
32789                 op.innerHTML = String.format('{0}', r.data[df]);
32790                 dom.appendChild(op);
32791             });
32792             if (typeof(this.defaultValue != 'undefined')) {
32793                 this.setValue(this.defaultValue);
32794             }
32795             
32796              
32797         }else{
32798             //this.onEmptyResults();
32799         }
32800         //this.el.focus();
32801     },
32802     // private
32803     onLoadException : function()
32804     {
32805         dom.innerHTML = '';
32806             
32807         Roo.log("Select on load exception");
32808         return;
32809     
32810         this.collapse();
32811         Roo.log(this.store.reader.jsonData);
32812         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32813             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32814         }
32815         
32816         
32817     },
32818     // private
32819     onTypeAhead : function(){
32820          
32821     },
32822
32823     // private
32824     onSelect : function(record, index){
32825         Roo.log('on select?');
32826         return;
32827         if(this.fireEvent('beforeselect', this, record, index) !== false){
32828             this.setFromData(index > -1 ? record.data : false);
32829             this.collapse();
32830             this.fireEvent('select', this, record, index);
32831         }
32832     },
32833
32834     /**
32835      * Returns the currently selected field value or empty string if no value is set.
32836      * @return {String} value The selected value
32837      */
32838     getValue : function(){
32839         var dom = this.el.dom;
32840         this.value = dom.options[dom.selectedIndex].value;
32841         return this.value;
32842         
32843     },
32844
32845     /**
32846      * Clears any text/value currently set in the field
32847      */
32848     clearValue : function(){
32849         this.value = '';
32850         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32851         
32852     },
32853
32854     /**
32855      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32856      * will be displayed in the field.  If the value does not match the data value of an existing item,
32857      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32858      * Otherwise the field will be blank (although the value will still be set).
32859      * @param {String} value The value to match
32860      */
32861     setValue : function(v){
32862         var d = this.el.dom;
32863         for (var i =0; i < d.options.length;i++) {
32864             if (v == d.options[i].value) {
32865                 d.selectedIndex = i;
32866                 this.value = v;
32867                 return;
32868             }
32869         }
32870         this.clearValue();
32871     },
32872     /**
32873      * @property {Object} the last set data for the element
32874      */
32875     
32876     lastData : false,
32877     /**
32878      * Sets the value of the field based on a object which is related to the record format for the store.
32879      * @param {Object} value the value to set as. or false on reset?
32880      */
32881     setFromData : function(o){
32882         Roo.log('setfrom data?');
32883          
32884         
32885         
32886     },
32887     // private
32888     reset : function(){
32889         this.clearValue();
32890     },
32891     // private
32892     findRecord : function(prop, value){
32893         
32894         return false;
32895     
32896         var record;
32897         if(this.store.getCount() > 0){
32898             this.store.each(function(r){
32899                 if(r.data[prop] == value){
32900                     record = r;
32901                     return false;
32902                 }
32903                 return true;
32904             });
32905         }
32906         return record;
32907     },
32908     
32909     getName: function()
32910     {
32911         // returns hidden if it's set..
32912         if (!this.rendered) {return ''};
32913         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32914         
32915     },
32916      
32917
32918     
32919
32920     // private
32921     onEmptyResults : function(){
32922         Roo.log('empty results');
32923         //this.collapse();
32924     },
32925
32926     /**
32927      * Returns true if the dropdown list is expanded, else false.
32928      */
32929     isExpanded : function(){
32930         return false;
32931     },
32932
32933     /**
32934      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32935      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32936      * @param {String} value The data value of the item to select
32937      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32938      * selected item if it is not currently in view (defaults to true)
32939      * @return {Boolean} True if the value matched an item in the list, else false
32940      */
32941     selectByValue : function(v, scrollIntoView){
32942         Roo.log('select By Value');
32943         return false;
32944     
32945         if(v !== undefined && v !== null){
32946             var r = this.findRecord(this.valueField || this.displayField, v);
32947             if(r){
32948                 this.select(this.store.indexOf(r), scrollIntoView);
32949                 return true;
32950             }
32951         }
32952         return false;
32953     },
32954
32955     /**
32956      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32957      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32958      * @param {Number} index The zero-based index of the list item to select
32959      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32960      * selected item if it is not currently in view (defaults to true)
32961      */
32962     select : function(index, scrollIntoView){
32963         Roo.log('select ');
32964         return  ;
32965         
32966         this.selectedIndex = index;
32967         this.view.select(index);
32968         if(scrollIntoView !== false){
32969             var el = this.view.getNode(index);
32970             if(el){
32971                 this.innerList.scrollChildIntoView(el, false);
32972             }
32973         }
32974     },
32975
32976       
32977
32978     // private
32979     validateBlur : function(){
32980         
32981         return;
32982         
32983     },
32984
32985     // private
32986     initQuery : function(){
32987         this.doQuery(this.getRawValue());
32988     },
32989
32990     // private
32991     doForce : function(){
32992         if(this.el.dom.value.length > 0){
32993             this.el.dom.value =
32994                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32995              
32996         }
32997     },
32998
32999     /**
33000      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
33001      * query allowing the query action to be canceled if needed.
33002      * @param {String} query The SQL query to execute
33003      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
33004      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
33005      * saved in the current store (defaults to false)
33006      */
33007     doQuery : function(q, forceAll){
33008         
33009         Roo.log('doQuery?');
33010         if(q === undefined || q === null){
33011             q = '';
33012         }
33013         var qe = {
33014             query: q,
33015             forceAll: forceAll,
33016             combo: this,
33017             cancel:false
33018         };
33019         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
33020             return false;
33021         }
33022         q = qe.query;
33023         forceAll = qe.forceAll;
33024         if(forceAll === true || (q.length >= this.minChars)){
33025             if(this.lastQuery != q || this.alwaysQuery){
33026                 this.lastQuery = q;
33027                 if(this.mode == 'local'){
33028                     this.selectedIndex = -1;
33029                     if(forceAll){
33030                         this.store.clearFilter();
33031                     }else{
33032                         this.store.filter(this.displayField, q);
33033                     }
33034                     this.onLoad();
33035                 }else{
33036                     this.store.baseParams[this.queryParam] = q;
33037                     this.store.load({
33038                         params: this.getParams(q)
33039                     });
33040                     this.expand();
33041                 }
33042             }else{
33043                 this.selectedIndex = -1;
33044                 this.onLoad();   
33045             }
33046         }
33047     },
33048
33049     // private
33050     getParams : function(q){
33051         var p = {};
33052         //p[this.queryParam] = q;
33053         if(this.pageSize){
33054             p.start = 0;
33055             p.limit = this.pageSize;
33056         }
33057         return p;
33058     },
33059
33060     /**
33061      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
33062      */
33063     collapse : function(){
33064         
33065     },
33066
33067     // private
33068     collapseIf : function(e){
33069         
33070     },
33071
33072     /**
33073      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
33074      */
33075     expand : function(){
33076         
33077     } ,
33078
33079     // private
33080      
33081
33082     /** 
33083     * @cfg {Boolean} grow 
33084     * @hide 
33085     */
33086     /** 
33087     * @cfg {Number} growMin 
33088     * @hide 
33089     */
33090     /** 
33091     * @cfg {Number} growMax 
33092     * @hide 
33093     */
33094     /**
33095      * @hide
33096      * @method autoSize
33097      */
33098     
33099     setWidth : function()
33100     {
33101         
33102     },
33103     getResizeEl : function(){
33104         return this.el;
33105     }
33106 });//<script type="text/javasscript">
33107  
33108
33109 /**
33110  * @class Roo.DDView
33111  * A DnD enabled version of Roo.View.
33112  * @param {Element/String} container The Element in which to create the View.
33113  * @param {String} tpl The template string used to create the markup for each element of the View
33114  * @param {Object} config The configuration properties. These include all the config options of
33115  * {@link Roo.View} plus some specific to this class.<br>
33116  * <p>
33117  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
33118  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
33119  * <p>
33120  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
33121 .x-view-drag-insert-above {
33122         border-top:1px dotted #3366cc;
33123 }
33124 .x-view-drag-insert-below {
33125         border-bottom:1px dotted #3366cc;
33126 }
33127 </code></pre>
33128  * 
33129  */
33130  
33131 Roo.DDView = function(container, tpl, config) {
33132     Roo.DDView.superclass.constructor.apply(this, arguments);
33133     this.getEl().setStyle("outline", "0px none");
33134     this.getEl().unselectable();
33135     if (this.dragGroup) {
33136         this.setDraggable(this.dragGroup.split(","));
33137     }
33138     if (this.dropGroup) {
33139         this.setDroppable(this.dropGroup.split(","));
33140     }
33141     if (this.deletable) {
33142         this.setDeletable();
33143     }
33144     this.isDirtyFlag = false;
33145         this.addEvents({
33146                 "drop" : true
33147         });
33148 };
33149
33150 Roo.extend(Roo.DDView, Roo.View, {
33151 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
33152 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
33153 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
33154 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
33155
33156         isFormField: true,
33157
33158         reset: Roo.emptyFn,
33159         
33160         clearInvalid: Roo.form.Field.prototype.clearInvalid,
33161
33162         validate: function() {
33163                 return true;
33164         },
33165         
33166         destroy: function() {
33167                 this.purgeListeners();
33168                 this.getEl.removeAllListeners();
33169                 this.getEl().remove();
33170                 if (this.dragZone) {
33171                         if (this.dragZone.destroy) {
33172                                 this.dragZone.destroy();
33173                         }
33174                 }
33175                 if (this.dropZone) {
33176                         if (this.dropZone.destroy) {
33177                                 this.dropZone.destroy();
33178                         }
33179                 }
33180         },
33181
33182 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33183         getName: function() {
33184                 return this.name;
33185         },
33186
33187 /**     Loads the View from a JSON string representing the Records to put into the Store. */
33188         setValue: function(v) {
33189                 if (!this.store) {
33190                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
33191                 }
33192                 var data = {};
33193                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33194                 this.store.proxy = new Roo.data.MemoryProxy(data);
33195                 this.store.load();
33196         },
33197
33198 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33199         getValue: function() {
33200                 var result = '(';
33201                 this.store.each(function(rec) {
33202                         result += rec.id + ',';
33203                 });
33204                 return result.substr(0, result.length - 1) + ')';
33205         },
33206         
33207         getIds: function() {
33208                 var i = 0, result = new Array(this.store.getCount());
33209                 this.store.each(function(rec) {
33210                         result[i++] = rec.id;
33211                 });
33212                 return result;
33213         },
33214         
33215         isDirty: function() {
33216                 return this.isDirtyFlag;
33217         },
33218
33219 /**
33220  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33221  *      whole Element becomes the target, and this causes the drop gesture to append.
33222  */
33223     getTargetFromEvent : function(e) {
33224                 var target = e.getTarget();
33225                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33226                 target = target.parentNode;
33227                 }
33228                 if (!target) {
33229                         target = this.el.dom.lastChild || this.el.dom;
33230                 }
33231                 return target;
33232     },
33233
33234 /**
33235  *      Create the drag data which consists of an object which has the property "ddel" as
33236  *      the drag proxy element. 
33237  */
33238     getDragData : function(e) {
33239         var target = this.findItemFromChild(e.getTarget());
33240                 if(target) {
33241                         this.handleSelection(e);
33242                         var selNodes = this.getSelectedNodes();
33243             var dragData = {
33244                 source: this,
33245                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33246                 nodes: selNodes,
33247                 records: []
33248                         };
33249                         var selectedIndices = this.getSelectedIndexes();
33250                         for (var i = 0; i < selectedIndices.length; i++) {
33251                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33252                         }
33253                         if (selNodes.length == 1) {
33254                                 dragData.ddel = target.cloneNode(true); // the div element
33255                         } else {
33256                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33257                                 div.className = 'multi-proxy';
33258                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33259                                         div.appendChild(selNodes[i].cloneNode(true));
33260                                 }
33261                                 dragData.ddel = div;
33262                         }
33263             //console.log(dragData)
33264             //console.log(dragData.ddel.innerHTML)
33265                         return dragData;
33266                 }
33267         //console.log('nodragData')
33268                 return false;
33269     },
33270     
33271 /**     Specify to which ddGroup items in this DDView may be dragged. */
33272     setDraggable: function(ddGroup) {
33273         if (ddGroup instanceof Array) {
33274                 Roo.each(ddGroup, this.setDraggable, this);
33275                 return;
33276         }
33277         if (this.dragZone) {
33278                 this.dragZone.addToGroup(ddGroup);
33279         } else {
33280                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33281                                 containerScroll: true,
33282                                 ddGroup: ddGroup 
33283
33284                         });
33285 //                      Draggability implies selection. DragZone's mousedown selects the element.
33286                         if (!this.multiSelect) { this.singleSelect = true; }
33287
33288 //                      Wire the DragZone's handlers up to methods in *this*
33289                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33290                 }
33291     },
33292
33293 /**     Specify from which ddGroup this DDView accepts drops. */
33294     setDroppable: function(ddGroup) {
33295         if (ddGroup instanceof Array) {
33296                 Roo.each(ddGroup, this.setDroppable, this);
33297                 return;
33298         }
33299         if (this.dropZone) {
33300                 this.dropZone.addToGroup(ddGroup);
33301         } else {
33302                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33303                                 containerScroll: true,
33304                                 ddGroup: ddGroup
33305                         });
33306
33307 //                      Wire the DropZone's handlers up to methods in *this*
33308                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33309                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33310                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33311                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33312                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33313                 }
33314     },
33315
33316 /**     Decide whether to drop above or below a View node. */
33317     getDropPoint : function(e, n, dd){
33318         if (n == this.el.dom) { return "above"; }
33319                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33320                 var c = t + (b - t) / 2;
33321                 var y = Roo.lib.Event.getPageY(e);
33322                 if(y <= c) {
33323                         return "above";
33324                 }else{
33325                         return "below";
33326                 }
33327     },
33328
33329     onNodeEnter : function(n, dd, e, data){
33330                 return false;
33331     },
33332     
33333     onNodeOver : function(n, dd, e, data){
33334                 var pt = this.getDropPoint(e, n, dd);
33335                 // set the insert point style on the target node
33336                 var dragElClass = this.dropNotAllowed;
33337                 if (pt) {
33338                         var targetElClass;
33339                         if (pt == "above"){
33340                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33341                                 targetElClass = "x-view-drag-insert-above";
33342                         } else {
33343                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33344                                 targetElClass = "x-view-drag-insert-below";
33345                         }
33346                         if (this.lastInsertClass != targetElClass){
33347                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33348                                 this.lastInsertClass = targetElClass;
33349                         }
33350                 }
33351                 return dragElClass;
33352         },
33353
33354     onNodeOut : function(n, dd, e, data){
33355                 this.removeDropIndicators(n);
33356     },
33357
33358     onNodeDrop : function(n, dd, e, data){
33359         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33360                 return false;
33361         }
33362         var pt = this.getDropPoint(e, n, dd);
33363                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33364                 if (pt == "below") { insertAt++; }
33365                 for (var i = 0; i < data.records.length; i++) {
33366                         var r = data.records[i];
33367                         var dup = this.store.getById(r.id);
33368                         if (dup && (dd != this.dragZone)) {
33369                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33370                         } else {
33371                                 if (data.copy) {
33372                                         this.store.insert(insertAt++, r.copy());
33373                                 } else {
33374                                         data.source.isDirtyFlag = true;
33375                                         r.store.remove(r);
33376                                         this.store.insert(insertAt++, r);
33377                                 }
33378                                 this.isDirtyFlag = true;
33379                         }
33380                 }
33381                 this.dragZone.cachedTarget = null;
33382                 return true;
33383     },
33384
33385     removeDropIndicators : function(n){
33386                 if(n){
33387                         Roo.fly(n).removeClass([
33388                                 "x-view-drag-insert-above",
33389                                 "x-view-drag-insert-below"]);
33390                         this.lastInsertClass = "_noclass";
33391                 }
33392     },
33393
33394 /**
33395  *      Utility method. Add a delete option to the DDView's context menu.
33396  *      @param {String} imageUrl The URL of the "delete" icon image.
33397  */
33398         setDeletable: function(imageUrl) {
33399                 if (!this.singleSelect && !this.multiSelect) {
33400                         this.singleSelect = true;
33401                 }
33402                 var c = this.getContextMenu();
33403                 this.contextMenu.on("itemclick", function(item) {
33404                         switch (item.id) {
33405                                 case "delete":
33406                                         this.remove(this.getSelectedIndexes());
33407                                         break;
33408                         }
33409                 }, this);
33410                 this.contextMenu.add({
33411                         icon: imageUrl,
33412                         id: "delete",
33413                         text: 'Delete'
33414                 });
33415         },
33416         
33417 /**     Return the context menu for this DDView. */
33418         getContextMenu: function() {
33419                 if (!this.contextMenu) {
33420 //                      Create the View's context menu
33421                         this.contextMenu = new Roo.menu.Menu({
33422                                 id: this.id + "-contextmenu"
33423                         });
33424                         this.el.on("contextmenu", this.showContextMenu, this);
33425                 }
33426                 return this.contextMenu;
33427         },
33428         
33429         disableContextMenu: function() {
33430                 if (this.contextMenu) {
33431                         this.el.un("contextmenu", this.showContextMenu, this);
33432                 }
33433         },
33434
33435         showContextMenu: function(e, item) {
33436         item = this.findItemFromChild(e.getTarget());
33437                 if (item) {
33438                         e.stopEvent();
33439                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33440                         this.contextMenu.showAt(e.getXY());
33441             }
33442     },
33443
33444 /**
33445  *      Remove {@link Roo.data.Record}s at the specified indices.
33446  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33447  */
33448     remove: function(selectedIndices) {
33449                 selectedIndices = [].concat(selectedIndices);
33450                 for (var i = 0; i < selectedIndices.length; i++) {
33451                         var rec = this.store.getAt(selectedIndices[i]);
33452                         this.store.remove(rec);
33453                 }
33454     },
33455
33456 /**
33457  *      Double click fires the event, but also, if this is draggable, and there is only one other
33458  *      related DropZone, it transfers the selected node.
33459  */
33460     onDblClick : function(e){
33461         var item = this.findItemFromChild(e.getTarget());
33462         if(item){
33463             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33464                 return false;
33465             }
33466             if (this.dragGroup) {
33467                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33468                     while (targets.indexOf(this.dropZone) > -1) {
33469                             targets.remove(this.dropZone);
33470                                 }
33471                     if (targets.length == 1) {
33472                                         this.dragZone.cachedTarget = null;
33473                         var el = Roo.get(targets[0].getEl());
33474                         var box = el.getBox(true);
33475                         targets[0].onNodeDrop(el.dom, {
33476                                 target: el.dom,
33477                                 xy: [box.x, box.y + box.height - 1]
33478                         }, null, this.getDragData(e));
33479                     }
33480                 }
33481         }
33482     },
33483     
33484     handleSelection: function(e) {
33485                 this.dragZone.cachedTarget = null;
33486         var item = this.findItemFromChild(e.getTarget());
33487         if (!item) {
33488                 this.clearSelections(true);
33489                 return;
33490         }
33491                 if (item && (this.multiSelect || this.singleSelect)){
33492                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33493                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33494                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33495                                 this.unselect(item);
33496                         } else {
33497                                 this.select(item, this.multiSelect && e.ctrlKey);
33498                                 this.lastSelection = item;
33499                         }
33500                 }
33501     },
33502
33503     onItemClick : function(item, index, e){
33504                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33505                         return false;
33506                 }
33507                 return true;
33508     },
33509
33510     unselect : function(nodeInfo, suppressEvent){
33511                 var node = this.getNode(nodeInfo);
33512                 if(node && this.isSelected(node)){
33513                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33514                                 Roo.fly(node).removeClass(this.selectedClass);
33515                                 this.selections.remove(node);
33516                                 if(!suppressEvent){
33517                                         this.fireEvent("selectionchange", this, this.selections);
33518                                 }
33519                         }
33520                 }
33521     }
33522 });
33523 /*
33524  * Based on:
33525  * Ext JS Library 1.1.1
33526  * Copyright(c) 2006-2007, Ext JS, LLC.
33527  *
33528  * Originally Released Under LGPL - original licence link has changed is not relivant.
33529  *
33530  * Fork - LGPL
33531  * <script type="text/javascript">
33532  */
33533  
33534 /**
33535  * @class Roo.LayoutManager
33536  * @extends Roo.util.Observable
33537  * Base class for layout managers.
33538  */
33539 Roo.LayoutManager = function(container, config){
33540     Roo.LayoutManager.superclass.constructor.call(this);
33541     this.el = Roo.get(container);
33542     // ie scrollbar fix
33543     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33544         document.body.scroll = "no";
33545     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33546         this.el.position('relative');
33547     }
33548     this.id = this.el.id;
33549     this.el.addClass("x-layout-container");
33550     /** false to disable window resize monitoring @type Boolean */
33551     this.monitorWindowResize = true;
33552     this.regions = {};
33553     this.addEvents({
33554         /**
33555          * @event layout
33556          * Fires when a layout is performed. 
33557          * @param {Roo.LayoutManager} this
33558          */
33559         "layout" : true,
33560         /**
33561          * @event regionresized
33562          * Fires when the user resizes a region. 
33563          * @param {Roo.LayoutRegion} region The resized region
33564          * @param {Number} newSize The new size (width for east/west, height for north/south)
33565          */
33566         "regionresized" : true,
33567         /**
33568          * @event regioncollapsed
33569          * Fires when a region is collapsed. 
33570          * @param {Roo.LayoutRegion} region The collapsed region
33571          */
33572         "regioncollapsed" : true,
33573         /**
33574          * @event regionexpanded
33575          * Fires when a region is expanded.  
33576          * @param {Roo.LayoutRegion} region The expanded region
33577          */
33578         "regionexpanded" : true
33579     });
33580     this.updating = false;
33581     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33582 };
33583
33584 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33585     /**
33586      * Returns true if this layout is currently being updated
33587      * @return {Boolean}
33588      */
33589     isUpdating : function(){
33590         return this.updating; 
33591     },
33592     
33593     /**
33594      * Suspend the LayoutManager from doing auto-layouts while
33595      * making multiple add or remove calls
33596      */
33597     beginUpdate : function(){
33598         this.updating = true;    
33599     },
33600     
33601     /**
33602      * Restore auto-layouts and optionally disable the manager from performing a layout
33603      * @param {Boolean} noLayout true to disable a layout update 
33604      */
33605     endUpdate : function(noLayout){
33606         this.updating = false;
33607         if(!noLayout){
33608             this.layout();
33609         }    
33610     },
33611     
33612     layout: function(){
33613         
33614     },
33615     
33616     onRegionResized : function(region, newSize){
33617         this.fireEvent("regionresized", region, newSize);
33618         this.layout();
33619     },
33620     
33621     onRegionCollapsed : function(region){
33622         this.fireEvent("regioncollapsed", region);
33623     },
33624     
33625     onRegionExpanded : function(region){
33626         this.fireEvent("regionexpanded", region);
33627     },
33628         
33629     /**
33630      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33631      * performs box-model adjustments.
33632      * @return {Object} The size as an object {width: (the width), height: (the height)}
33633      */
33634     getViewSize : function(){
33635         var size;
33636         if(this.el.dom != document.body){
33637             size = this.el.getSize();
33638         }else{
33639             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33640         }
33641         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33642         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33643         return size;
33644     },
33645     
33646     /**
33647      * Returns the Element this layout is bound to.
33648      * @return {Roo.Element}
33649      */
33650     getEl : function(){
33651         return this.el;
33652     },
33653     
33654     /**
33655      * Returns the specified region.
33656      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33657      * @return {Roo.LayoutRegion}
33658      */
33659     getRegion : function(target){
33660         return this.regions[target.toLowerCase()];
33661     },
33662     
33663     onWindowResize : function(){
33664         if(this.monitorWindowResize){
33665             this.layout();
33666         }
33667     }
33668 });/*
33669  * Based on:
33670  * Ext JS Library 1.1.1
33671  * Copyright(c) 2006-2007, Ext JS, LLC.
33672  *
33673  * Originally Released Under LGPL - original licence link has changed is not relivant.
33674  *
33675  * Fork - LGPL
33676  * <script type="text/javascript">
33677  */
33678 /**
33679  * @class Roo.BorderLayout
33680  * @extends Roo.LayoutManager
33681  * @children Roo.ContentPanel
33682  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33683  * please see: <br><br>
33684  * <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>
33685  * <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>
33686  * Example:
33687  <pre><code>
33688  var layout = new Roo.BorderLayout(document.body, {
33689     north: {
33690         initialSize: 25,
33691         titlebar: false
33692     },
33693     west: {
33694         split:true,
33695         initialSize: 200,
33696         minSize: 175,
33697         maxSize: 400,
33698         titlebar: true,
33699         collapsible: true
33700     },
33701     east: {
33702         split:true,
33703         initialSize: 202,
33704         minSize: 175,
33705         maxSize: 400,
33706         titlebar: true,
33707         collapsible: true
33708     },
33709     south: {
33710         split:true,
33711         initialSize: 100,
33712         minSize: 100,
33713         maxSize: 200,
33714         titlebar: true,
33715         collapsible: true
33716     },
33717     center: {
33718         titlebar: true,
33719         autoScroll:true,
33720         resizeTabs: true,
33721         minTabWidth: 50,
33722         preferredTabWidth: 150
33723     }
33724 });
33725
33726 // shorthand
33727 var CP = Roo.ContentPanel;
33728
33729 layout.beginUpdate();
33730 layout.add("north", new CP("north", "North"));
33731 layout.add("south", new CP("south", {title: "South", closable: true}));
33732 layout.add("west", new CP("west", {title: "West"}));
33733 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33734 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33735 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33736 layout.getRegion("center").showPanel("center1");
33737 layout.endUpdate();
33738 </code></pre>
33739
33740 <b>The container the layout is rendered into can be either the body element or any other element.
33741 If it is not the body element, the container needs to either be an absolute positioned element,
33742 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33743 the container size if it is not the body element.</b>
33744
33745 * @constructor
33746 * Create a new BorderLayout
33747 * @param {String/HTMLElement/Element} container The container this layout is bound to
33748 * @param {Object} config Configuration options
33749  */
33750 Roo.BorderLayout = function(container, config){
33751     config = config || {};
33752     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33753     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33754     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33755         var target = this.factory.validRegions[i];
33756         if(config[target]){
33757             this.addRegion(target, config[target]);
33758         }
33759     }
33760 };
33761
33762 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33763         
33764         /**
33765          * @cfg {Roo.LayoutRegion} east
33766          */
33767         /**
33768          * @cfg {Roo.LayoutRegion} west
33769          */
33770         /**
33771          * @cfg {Roo.LayoutRegion} north
33772          */
33773         /**
33774          * @cfg {Roo.LayoutRegion} south
33775          */
33776         /**
33777          * @cfg {Roo.LayoutRegion} center
33778          */
33779     /**
33780      * Creates and adds a new region if it doesn't already exist.
33781      * @param {String} target The target region key (north, south, east, west or center).
33782      * @param {Object} config The regions config object
33783      * @return {BorderLayoutRegion} The new region
33784      */
33785     addRegion : function(target, config){
33786         if(!this.regions[target]){
33787             var r = this.factory.create(target, this, config);
33788             this.bindRegion(target, r);
33789         }
33790         return this.regions[target];
33791     },
33792
33793     // private (kinda)
33794     bindRegion : function(name, r){
33795         this.regions[name] = r;
33796         r.on("visibilitychange", this.layout, this);
33797         r.on("paneladded", this.layout, this);
33798         r.on("panelremoved", this.layout, this);
33799         r.on("invalidated", this.layout, this);
33800         r.on("resized", this.onRegionResized, this);
33801         r.on("collapsed", this.onRegionCollapsed, this);
33802         r.on("expanded", this.onRegionExpanded, this);
33803     },
33804
33805     /**
33806      * Performs a layout update.
33807      */
33808     layout : function(){
33809         if(this.updating) {
33810             return;
33811         }
33812         var size = this.getViewSize();
33813         var w = size.width;
33814         var h = size.height;
33815         var centerW = w;
33816         var centerH = h;
33817         var centerY = 0;
33818         var centerX = 0;
33819         //var x = 0, y = 0;
33820
33821         var rs = this.regions;
33822         var north = rs["north"];
33823         var south = rs["south"]; 
33824         var west = rs["west"];
33825         var east = rs["east"];
33826         var center = rs["center"];
33827         //if(this.hideOnLayout){ // not supported anymore
33828             //c.el.setStyle("display", "none");
33829         //}
33830         if(north && north.isVisible()){
33831             var b = north.getBox();
33832             var m = north.getMargins();
33833             b.width = w - (m.left+m.right);
33834             b.x = m.left;
33835             b.y = m.top;
33836             centerY = b.height + b.y + m.bottom;
33837             centerH -= centerY;
33838             north.updateBox(this.safeBox(b));
33839         }
33840         if(south && south.isVisible()){
33841             var b = south.getBox();
33842             var m = south.getMargins();
33843             b.width = w - (m.left+m.right);
33844             b.x = m.left;
33845             var totalHeight = (b.height + m.top + m.bottom);
33846             b.y = h - totalHeight + m.top;
33847             centerH -= totalHeight;
33848             south.updateBox(this.safeBox(b));
33849         }
33850         if(west && west.isVisible()){
33851             var b = west.getBox();
33852             var m = west.getMargins();
33853             b.height = centerH - (m.top+m.bottom);
33854             b.x = m.left;
33855             b.y = centerY + m.top;
33856             var totalWidth = (b.width + m.left + m.right);
33857             centerX += totalWidth;
33858             centerW -= totalWidth;
33859             west.updateBox(this.safeBox(b));
33860         }
33861         if(east && east.isVisible()){
33862             var b = east.getBox();
33863             var m = east.getMargins();
33864             b.height = centerH - (m.top+m.bottom);
33865             var totalWidth = (b.width + m.left + m.right);
33866             b.x = w - totalWidth + m.left;
33867             b.y = centerY + m.top;
33868             centerW -= totalWidth;
33869             east.updateBox(this.safeBox(b));
33870         }
33871         if(center){
33872             var m = center.getMargins();
33873             var centerBox = {
33874                 x: centerX + m.left,
33875                 y: centerY + m.top,
33876                 width: centerW - (m.left+m.right),
33877                 height: centerH - (m.top+m.bottom)
33878             };
33879             //if(this.hideOnLayout){
33880                 //center.el.setStyle("display", "block");
33881             //}
33882             center.updateBox(this.safeBox(centerBox));
33883         }
33884         this.el.repaint();
33885         this.fireEvent("layout", this);
33886     },
33887
33888     // private
33889     safeBox : function(box){
33890         box.width = Math.max(0, box.width);
33891         box.height = Math.max(0, box.height);
33892         return box;
33893     },
33894
33895     /**
33896      * Adds a ContentPanel (or subclass) to this layout.
33897      * @param {String} target The target region key (north, south, east, west or center).
33898      * @param {Roo.ContentPanel} panel The panel to add
33899      * @return {Roo.ContentPanel} The added panel
33900      */
33901     add : function(target, panel){
33902          
33903         target = target.toLowerCase();
33904         return this.regions[target].add(panel);
33905     },
33906
33907     /**
33908      * Remove a ContentPanel (or subclass) to this layout.
33909      * @param {String} target The target region key (north, south, east, west or center).
33910      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33911      * @return {Roo.ContentPanel} The removed panel
33912      */
33913     remove : function(target, panel){
33914         target = target.toLowerCase();
33915         return this.regions[target].remove(panel);
33916     },
33917
33918     /**
33919      * Searches all regions for a panel with the specified id
33920      * @param {String} panelId
33921      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33922      */
33923     findPanel : function(panelId){
33924         var rs = this.regions;
33925         for(var target in rs){
33926             if(typeof rs[target] != "function"){
33927                 var p = rs[target].getPanel(panelId);
33928                 if(p){
33929                     return p;
33930                 }
33931             }
33932         }
33933         return null;
33934     },
33935
33936     /**
33937      * Searches all regions for a panel with the specified id and activates (shows) it.
33938      * @param {String/ContentPanel} panelId The panels id or the panel itself
33939      * @return {Roo.ContentPanel} The shown panel or null
33940      */
33941     showPanel : function(panelId) {
33942       var rs = this.regions;
33943       for(var target in rs){
33944          var r = rs[target];
33945          if(typeof r != "function"){
33946             if(r.hasPanel(panelId)){
33947                return r.showPanel(panelId);
33948             }
33949          }
33950       }
33951       return null;
33952    },
33953
33954    /**
33955      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33956      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33957      */
33958     restoreState : function(provider){
33959         if(!provider){
33960             provider = Roo.state.Manager;
33961         }
33962         var sm = new Roo.LayoutStateManager();
33963         sm.init(this, provider);
33964     },
33965
33966     /**
33967      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33968      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33969      * a valid ContentPanel config object.  Example:
33970      * <pre><code>
33971 // Create the main layout
33972 var layout = new Roo.BorderLayout('main-ct', {
33973     west: {
33974         split:true,
33975         minSize: 175,
33976         titlebar: true
33977     },
33978     center: {
33979         title:'Components'
33980     }
33981 }, 'main-ct');
33982
33983 // Create and add multiple ContentPanels at once via configs
33984 layout.batchAdd({
33985    west: {
33986        id: 'source-files',
33987        autoCreate:true,
33988        title:'Ext Source Files',
33989        autoScroll:true,
33990        fitToFrame:true
33991    },
33992    center : {
33993        el: cview,
33994        autoScroll:true,
33995        fitToFrame:true,
33996        toolbar: tb,
33997        resizeEl:'cbody'
33998    }
33999 });
34000 </code></pre>
34001      * @param {Object} regions An object containing ContentPanel configs by region name
34002      */
34003     batchAdd : function(regions){
34004         this.beginUpdate();
34005         for(var rname in regions){
34006             var lr = this.regions[rname];
34007             if(lr){
34008                 this.addTypedPanels(lr, regions[rname]);
34009             }
34010         }
34011         this.endUpdate();
34012     },
34013
34014     // private
34015     addTypedPanels : function(lr, ps){
34016         if(typeof ps == 'string'){
34017             lr.add(new Roo.ContentPanel(ps));
34018         }
34019         else if(ps instanceof Array){
34020             for(var i =0, len = ps.length; i < len; i++){
34021                 this.addTypedPanels(lr, ps[i]);
34022             }
34023         }
34024         else if(!ps.events){ // raw config?
34025             var el = ps.el;
34026             delete ps.el; // prevent conflict
34027             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
34028         }
34029         else {  // panel object assumed!
34030             lr.add(ps);
34031         }
34032     },
34033     /**
34034      * Adds a xtype elements to the layout.
34035      * <pre><code>
34036
34037 layout.addxtype({
34038        xtype : 'ContentPanel',
34039        region: 'west',
34040        items: [ .... ]
34041    }
34042 );
34043
34044 layout.addxtype({
34045         xtype : 'NestedLayoutPanel',
34046         region: 'west',
34047         layout: {
34048            center: { },
34049            west: { }   
34050         },
34051         items : [ ... list of content panels or nested layout panels.. ]
34052    }
34053 );
34054 </code></pre>
34055      * @param {Object} cfg Xtype definition of item to add.
34056      */
34057     addxtype : function(cfg)
34058     {
34059         // basically accepts a pannel...
34060         // can accept a layout region..!?!?
34061         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34062         
34063         if (!cfg.xtype.match(/Panel$/)) {
34064             return false;
34065         }
34066         var ret = false;
34067         
34068         if (typeof(cfg.region) == 'undefined') {
34069             Roo.log("Failed to add Panel, region was not set");
34070             Roo.log(cfg);
34071             return false;
34072         }
34073         var region = cfg.region;
34074         delete cfg.region;
34075         
34076           
34077         var xitems = [];
34078         if (cfg.items) {
34079             xitems = cfg.items;
34080             delete cfg.items;
34081         }
34082         var nb = false;
34083         
34084         switch(cfg.xtype) 
34085         {
34086             case 'ContentPanel':  // ContentPanel (el, cfg)
34087             case 'ScrollPanel':  // ContentPanel (el, cfg)
34088             case 'ViewPanel': 
34089                 if(cfg.autoCreate) {
34090                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34091                 } else {
34092                     var el = this.el.createChild();
34093                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34094                 }
34095                 
34096                 this.add(region, ret);
34097                 break;
34098             
34099             
34100             case 'TreePanel': // our new panel!
34101                 cfg.el = this.el.createChild();
34102                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34103                 this.add(region, ret);
34104                 break;
34105             
34106             case 'NestedLayoutPanel': 
34107                 // create a new Layout (which is  a Border Layout...
34108                 var el = this.el.createChild();
34109                 var clayout = cfg.layout;
34110                 delete cfg.layout;
34111                 clayout.items   = clayout.items  || [];
34112                 // replace this exitems with the clayout ones..
34113                 xitems = clayout.items;
34114                  
34115                 
34116                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34117                     cfg.background = false;
34118                 }
34119                 var layout = new Roo.BorderLayout(el, clayout);
34120                 
34121                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
34122                 //console.log('adding nested layout panel '  + cfg.toSource());
34123                 this.add(region, ret);
34124                 nb = {}; /// find first...
34125                 break;
34126                 
34127             case 'GridPanel': 
34128             
34129                 // needs grid and region
34130                 
34131                 //var el = this.getRegion(region).el.createChild();
34132                 var el = this.el.createChild();
34133                 // create the grid first...
34134                 
34135                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
34136                 delete cfg.grid;
34137                 if (region == 'center' && this.active ) {
34138                     cfg.background = false;
34139                 }
34140                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
34141                 
34142                 this.add(region, ret);
34143                 if (cfg.background) {
34144                     ret.on('activate', function(gp) {
34145                         if (!gp.grid.rendered) {
34146                             gp.grid.render();
34147                         }
34148                     });
34149                 } else {
34150                     grid.render();
34151                 }
34152                 break;
34153            
34154            
34155            
34156                 
34157                 
34158                 
34159             default:
34160                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34161                     
34162                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34163                     this.add(region, ret);
34164                 } else {
34165                 
34166                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34167                     return null;
34168                 }
34169                 
34170              // GridPanel (grid, cfg)
34171             
34172         }
34173         this.beginUpdate();
34174         // add children..
34175         var region = '';
34176         var abn = {};
34177         Roo.each(xitems, function(i)  {
34178             region = nb && i.region ? i.region : false;
34179             
34180             var add = ret.addxtype(i);
34181            
34182             if (region) {
34183                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34184                 if (!i.background) {
34185                     abn[region] = nb[region] ;
34186                 }
34187             }
34188             
34189         });
34190         this.endUpdate();
34191
34192         // make the last non-background panel active..
34193         //if (nb) { Roo.log(abn); }
34194         if (nb) {
34195             
34196             for(var r in abn) {
34197                 region = this.getRegion(r);
34198                 if (region) {
34199                     // tried using nb[r], but it does not work..
34200                      
34201                     region.showPanel(abn[r]);
34202                    
34203                 }
34204             }
34205         }
34206         return ret;
34207         
34208     }
34209 });
34210
34211 /**
34212  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34213  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34214  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34215  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34216  * <pre><code>
34217 // shorthand
34218 var CP = Roo.ContentPanel;
34219
34220 var layout = Roo.BorderLayout.create({
34221     north: {
34222         initialSize: 25,
34223         titlebar: false,
34224         panels: [new CP("north", "North")]
34225     },
34226     west: {
34227         split:true,
34228         initialSize: 200,
34229         minSize: 175,
34230         maxSize: 400,
34231         titlebar: true,
34232         collapsible: true,
34233         panels: [new CP("west", {title: "West"})]
34234     },
34235     east: {
34236         split:true,
34237         initialSize: 202,
34238         minSize: 175,
34239         maxSize: 400,
34240         titlebar: true,
34241         collapsible: true,
34242         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34243     },
34244     south: {
34245         split:true,
34246         initialSize: 100,
34247         minSize: 100,
34248         maxSize: 200,
34249         titlebar: true,
34250         collapsible: true,
34251         panels: [new CP("south", {title: "South", closable: true})]
34252     },
34253     center: {
34254         titlebar: true,
34255         autoScroll:true,
34256         resizeTabs: true,
34257         minTabWidth: 50,
34258         preferredTabWidth: 150,
34259         panels: [
34260             new CP("center1", {title: "Close Me", closable: true}),
34261             new CP("center2", {title: "Center Panel", closable: false})
34262         ]
34263     }
34264 }, document.body);
34265
34266 layout.getRegion("center").showPanel("center1");
34267 </code></pre>
34268  * @param config
34269  * @param targetEl
34270  */
34271 Roo.BorderLayout.create = function(config, targetEl){
34272     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34273     layout.beginUpdate();
34274     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34275     for(var j = 0, jlen = regions.length; j < jlen; j++){
34276         var lr = regions[j];
34277         if(layout.regions[lr] && config[lr].panels){
34278             var r = layout.regions[lr];
34279             var ps = config[lr].panels;
34280             layout.addTypedPanels(r, ps);
34281         }
34282     }
34283     layout.endUpdate();
34284     return layout;
34285 };
34286
34287 // private
34288 Roo.BorderLayout.RegionFactory = {
34289     // private
34290     validRegions : ["north","south","east","west","center"],
34291
34292     // private
34293     create : function(target, mgr, config){
34294         target = target.toLowerCase();
34295         if(config.lightweight || config.basic){
34296             return new Roo.BasicLayoutRegion(mgr, config, target);
34297         }
34298         switch(target){
34299             case "north":
34300                 return new Roo.NorthLayoutRegion(mgr, config);
34301             case "south":
34302                 return new Roo.SouthLayoutRegion(mgr, config);
34303             case "east":
34304                 return new Roo.EastLayoutRegion(mgr, config);
34305             case "west":
34306                 return new Roo.WestLayoutRegion(mgr, config);
34307             case "center":
34308                 return new Roo.CenterLayoutRegion(mgr, config);
34309         }
34310         throw 'Layout region "'+target+'" not supported.';
34311     }
34312 };/*
34313  * Based on:
34314  * Ext JS Library 1.1.1
34315  * Copyright(c) 2006-2007, Ext JS, LLC.
34316  *
34317  * Originally Released Under LGPL - original licence link has changed is not relivant.
34318  *
34319  * Fork - LGPL
34320  * <script type="text/javascript">
34321  */
34322  
34323 /**
34324  * @class Roo.BasicLayoutRegion
34325  * @extends Roo.util.Observable
34326  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34327  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34328  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34329  */
34330 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34331     this.mgr = mgr;
34332     this.position  = pos;
34333     this.events = {
34334         /**
34335          * @scope Roo.BasicLayoutRegion
34336          */
34337         
34338         /**
34339          * @event beforeremove
34340          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34341          * @param {Roo.LayoutRegion} this
34342          * @param {Roo.ContentPanel} panel The panel
34343          * @param {Object} e The cancel event object
34344          */
34345         "beforeremove" : true,
34346         /**
34347          * @event invalidated
34348          * Fires when the layout for this region is changed.
34349          * @param {Roo.LayoutRegion} this
34350          */
34351         "invalidated" : true,
34352         /**
34353          * @event visibilitychange
34354          * Fires when this region is shown or hidden 
34355          * @param {Roo.LayoutRegion} this
34356          * @param {Boolean} visibility true or false
34357          */
34358         "visibilitychange" : true,
34359         /**
34360          * @event paneladded
34361          * Fires when a panel is added. 
34362          * @param {Roo.LayoutRegion} this
34363          * @param {Roo.ContentPanel} panel The panel
34364          */
34365         "paneladded" : true,
34366         /**
34367          * @event panelremoved
34368          * Fires when a panel is removed. 
34369          * @param {Roo.LayoutRegion} this
34370          * @param {Roo.ContentPanel} panel The panel
34371          */
34372         "panelremoved" : true,
34373         /**
34374          * @event beforecollapse
34375          * Fires when this region before collapse.
34376          * @param {Roo.LayoutRegion} this
34377          */
34378         "beforecollapse" : true,
34379         /**
34380          * @event collapsed
34381          * Fires when this region is collapsed.
34382          * @param {Roo.LayoutRegion} this
34383          */
34384         "collapsed" : true,
34385         /**
34386          * @event expanded
34387          * Fires when this region is expanded.
34388          * @param {Roo.LayoutRegion} this
34389          */
34390         "expanded" : true,
34391         /**
34392          * @event slideshow
34393          * Fires when this region is slid into view.
34394          * @param {Roo.LayoutRegion} this
34395          */
34396         "slideshow" : true,
34397         /**
34398          * @event slidehide
34399          * Fires when this region slides out of view. 
34400          * @param {Roo.LayoutRegion} this
34401          */
34402         "slidehide" : true,
34403         /**
34404          * @event panelactivated
34405          * Fires when a panel is activated. 
34406          * @param {Roo.LayoutRegion} this
34407          * @param {Roo.ContentPanel} panel The activated panel
34408          */
34409         "panelactivated" : true,
34410         /**
34411          * @event resized
34412          * Fires when the user resizes this region. 
34413          * @param {Roo.LayoutRegion} this
34414          * @param {Number} newSize The new size (width for east/west, height for north/south)
34415          */
34416         "resized" : true
34417     };
34418     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34419     this.panels = new Roo.util.MixedCollection();
34420     this.panels.getKey = this.getPanelId.createDelegate(this);
34421     this.box = null;
34422     this.activePanel = null;
34423     // ensure listeners are added...
34424     
34425     if (config.listeners || config.events) {
34426         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34427             listeners : config.listeners || {},
34428             events : config.events || {}
34429         });
34430     }
34431     
34432     if(skipConfig !== true){
34433         this.applyConfig(config);
34434     }
34435 };
34436
34437 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34438     getPanelId : function(p){
34439         return p.getId();
34440     },
34441     
34442     applyConfig : function(config){
34443         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34444         this.config = config;
34445         
34446     },
34447     
34448     /**
34449      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34450      * the width, for horizontal (north, south) the height.
34451      * @param {Number} newSize The new width or height
34452      */
34453     resizeTo : function(newSize){
34454         var el = this.el ? this.el :
34455                  (this.activePanel ? this.activePanel.getEl() : null);
34456         if(el){
34457             switch(this.position){
34458                 case "east":
34459                 case "west":
34460                     el.setWidth(newSize);
34461                     this.fireEvent("resized", this, newSize);
34462                 break;
34463                 case "north":
34464                 case "south":
34465                     el.setHeight(newSize);
34466                     this.fireEvent("resized", this, newSize);
34467                 break;                
34468             }
34469         }
34470     },
34471     
34472     getBox : function(){
34473         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34474     },
34475     
34476     getMargins : function(){
34477         return this.margins;
34478     },
34479     
34480     updateBox : function(box){
34481         this.box = box;
34482         var el = this.activePanel.getEl();
34483         el.dom.style.left = box.x + "px";
34484         el.dom.style.top = box.y + "px";
34485         this.activePanel.setSize(box.width, box.height);
34486     },
34487     
34488     /**
34489      * Returns the container element for this region.
34490      * @return {Roo.Element}
34491      */
34492     getEl : function(){
34493         return this.activePanel;
34494     },
34495     
34496     /**
34497      * Returns true if this region is currently visible.
34498      * @return {Boolean}
34499      */
34500     isVisible : function(){
34501         return this.activePanel ? true : false;
34502     },
34503     
34504     setActivePanel : function(panel){
34505         panel = this.getPanel(panel);
34506         if(this.activePanel && this.activePanel != panel){
34507             this.activePanel.setActiveState(false);
34508             this.activePanel.getEl().setLeftTop(-10000,-10000);
34509         }
34510         this.activePanel = panel;
34511         panel.setActiveState(true);
34512         if(this.box){
34513             panel.setSize(this.box.width, this.box.height);
34514         }
34515         this.fireEvent("panelactivated", this, panel);
34516         this.fireEvent("invalidated");
34517     },
34518     
34519     /**
34520      * Show the specified panel.
34521      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34522      * @return {Roo.ContentPanel} The shown panel or null
34523      */
34524     showPanel : function(panel){
34525         if(panel = this.getPanel(panel)){
34526             this.setActivePanel(panel);
34527         }
34528         return panel;
34529     },
34530     
34531     /**
34532      * Get the active panel for this region.
34533      * @return {Roo.ContentPanel} The active panel or null
34534      */
34535     getActivePanel : function(){
34536         return this.activePanel;
34537     },
34538     
34539     /**
34540      * Add the passed ContentPanel(s)
34541      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34542      * @return {Roo.ContentPanel} The panel added (if only one was added)
34543      */
34544     add : function(panel){
34545         if(arguments.length > 1){
34546             for(var i = 0, len = arguments.length; i < len; i++) {
34547                 this.add(arguments[i]);
34548             }
34549             return null;
34550         }
34551         if(this.hasPanel(panel)){
34552             this.showPanel(panel);
34553             return panel;
34554         }
34555         var el = panel.getEl();
34556         if(el.dom.parentNode != this.mgr.el.dom){
34557             this.mgr.el.dom.appendChild(el.dom);
34558         }
34559         if(panel.setRegion){
34560             panel.setRegion(this);
34561         }
34562         this.panels.add(panel);
34563         el.setStyle("position", "absolute");
34564         if(!panel.background){
34565             this.setActivePanel(panel);
34566             if(this.config.initialSize && this.panels.getCount()==1){
34567                 this.resizeTo(this.config.initialSize);
34568             }
34569         }
34570         this.fireEvent("paneladded", this, panel);
34571         return panel;
34572     },
34573     
34574     /**
34575      * Returns true if the panel is in this region.
34576      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34577      * @return {Boolean}
34578      */
34579     hasPanel : function(panel){
34580         if(typeof panel == "object"){ // must be panel obj
34581             panel = panel.getId();
34582         }
34583         return this.getPanel(panel) ? true : false;
34584     },
34585     
34586     /**
34587      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34588      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34589      * @param {Boolean} preservePanel Overrides the config preservePanel option
34590      * @return {Roo.ContentPanel} The panel that was removed
34591      */
34592     remove : function(panel, preservePanel){
34593         panel = this.getPanel(panel);
34594         if(!panel){
34595             return null;
34596         }
34597         var e = {};
34598         this.fireEvent("beforeremove", this, panel, e);
34599         if(e.cancel === true){
34600             return null;
34601         }
34602         var panelId = panel.getId();
34603         this.panels.removeKey(panelId);
34604         return panel;
34605     },
34606     
34607     /**
34608      * Returns the panel specified or null if it's not in this region.
34609      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34610      * @return {Roo.ContentPanel}
34611      */
34612     getPanel : function(id){
34613         if(typeof id == "object"){ // must be panel obj
34614             return id;
34615         }
34616         return this.panels.get(id);
34617     },
34618     
34619     /**
34620      * Returns this regions position (north/south/east/west/center).
34621      * @return {String} 
34622      */
34623     getPosition: function(){
34624         return this.position;    
34625     }
34626 });/*
34627  * Based on:
34628  * Ext JS Library 1.1.1
34629  * Copyright(c) 2006-2007, Ext JS, LLC.
34630  *
34631  * Originally Released Under LGPL - original licence link has changed is not relivant.
34632  *
34633  * Fork - LGPL
34634  * <script type="text/javascript">
34635  */
34636  
34637 /**
34638  * @class Roo.LayoutRegion
34639  * @extends Roo.BasicLayoutRegion
34640  * This class represents a region in a layout manager.
34641  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34642  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34643  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34644  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34645  * @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})
34646  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34647  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34648  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34649  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34650  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34651  * @cfg {String}    title           The title for the region (overrides panel titles)
34652  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34653  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34654  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34655  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34656  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34657  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34658  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34659  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34660  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34661  * @cfg {Boolean}   showPin         True to show a pin button
34662  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34663  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34664  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34665  * @cfg {Number}    width           For East/West panels
34666  * @cfg {Number}    height          For North/South panels
34667  * @cfg {Boolean}   split           To show the splitter
34668  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34669  */
34670 Roo.LayoutRegion = function(mgr, config, pos){
34671     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34672     var dh = Roo.DomHelper;
34673     /** This region's container element 
34674     * @type Roo.Element */
34675     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34676     /** This region's title element 
34677     * @type Roo.Element */
34678
34679     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34680         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34681         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34682     ]}, true);
34683     this.titleEl.enableDisplayMode();
34684     /** This region's title text element 
34685     * @type HTMLElement */
34686     this.titleTextEl = this.titleEl.dom.firstChild;
34687     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34688     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34689     this.closeBtn.enableDisplayMode();
34690     this.closeBtn.on("click", this.closeClicked, this);
34691     this.closeBtn.hide();
34692
34693     this.createBody(config);
34694     this.visible = true;
34695     this.collapsed = false;
34696
34697     if(config.hideWhenEmpty){
34698         this.hide();
34699         this.on("paneladded", this.validateVisibility, this);
34700         this.on("panelremoved", this.validateVisibility, this);
34701     }
34702     this.applyConfig(config);
34703 };
34704
34705 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34706
34707     createBody : function(){
34708         /** This region's body element 
34709         * @type Roo.Element */
34710         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34711     },
34712
34713     applyConfig : function(c){
34714         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34715             var dh = Roo.DomHelper;
34716             if(c.titlebar !== false){
34717                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34718                 this.collapseBtn.on("click", this.collapse, this);
34719                 this.collapseBtn.enableDisplayMode();
34720
34721                 if(c.showPin === true || this.showPin){
34722                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34723                     this.stickBtn.enableDisplayMode();
34724                     this.stickBtn.on("click", this.expand, this);
34725                     this.stickBtn.hide();
34726                 }
34727             }
34728             /** This region's collapsed element
34729             * @type Roo.Element */
34730             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34731                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34732             ]}, true);
34733             if(c.floatable !== false){
34734                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34735                this.collapsedEl.on("click", this.collapseClick, this);
34736             }
34737
34738             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34739                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34740                    id: "message", unselectable: "on", style:{"float":"left"}});
34741                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34742              }
34743             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34744             this.expandBtn.on("click", this.expand, this);
34745         }
34746         if(this.collapseBtn){
34747             this.collapseBtn.setVisible(c.collapsible == true);
34748         }
34749         this.cmargins = c.cmargins || this.cmargins ||
34750                          (this.position == "west" || this.position == "east" ?
34751                              {top: 0, left: 2, right:2, bottom: 0} :
34752                              {top: 2, left: 0, right:0, bottom: 2});
34753         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34754         this.bottomTabs = c.tabPosition != "top";
34755         this.autoScroll = c.autoScroll || false;
34756         if(this.autoScroll){
34757             this.bodyEl.setStyle("overflow", "auto");
34758         }else{
34759             this.bodyEl.setStyle("overflow", "hidden");
34760         }
34761         //if(c.titlebar !== false){
34762             if((!c.titlebar && !c.title) || c.titlebar === false){
34763                 this.titleEl.hide();
34764             }else{
34765                 this.titleEl.show();
34766                 if(c.title){
34767                     this.titleTextEl.innerHTML = c.title;
34768                 }
34769             }
34770         //}
34771         this.duration = c.duration || .30;
34772         this.slideDuration = c.slideDuration || .45;
34773         this.config = c;
34774         if(c.collapsed){
34775             this.collapse(true);
34776         }
34777         if(c.hidden){
34778             this.hide();
34779         }
34780     },
34781     /**
34782      * Returns true if this region is currently visible.
34783      * @return {Boolean}
34784      */
34785     isVisible : function(){
34786         return this.visible;
34787     },
34788
34789     /**
34790      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34791      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34792      */
34793     setCollapsedTitle : function(title){
34794         title = title || "&#160;";
34795         if(this.collapsedTitleTextEl){
34796             this.collapsedTitleTextEl.innerHTML = title;
34797         }
34798     },
34799
34800     getBox : function(){
34801         var b;
34802         if(!this.collapsed){
34803             b = this.el.getBox(false, true);
34804         }else{
34805             b = this.collapsedEl.getBox(false, true);
34806         }
34807         return b;
34808     },
34809
34810     getMargins : function(){
34811         return this.collapsed ? this.cmargins : this.margins;
34812     },
34813
34814     highlight : function(){
34815         this.el.addClass("x-layout-panel-dragover");
34816     },
34817
34818     unhighlight : function(){
34819         this.el.removeClass("x-layout-panel-dragover");
34820     },
34821
34822     updateBox : function(box){
34823         this.box = box;
34824         if(!this.collapsed){
34825             this.el.dom.style.left = box.x + "px";
34826             this.el.dom.style.top = box.y + "px";
34827             this.updateBody(box.width, box.height);
34828         }else{
34829             this.collapsedEl.dom.style.left = box.x + "px";
34830             this.collapsedEl.dom.style.top = box.y + "px";
34831             this.collapsedEl.setSize(box.width, box.height);
34832         }
34833         if(this.tabs){
34834             this.tabs.autoSizeTabs();
34835         }
34836     },
34837
34838     updateBody : function(w, h){
34839         if(w !== null){
34840             this.el.setWidth(w);
34841             w -= this.el.getBorderWidth("rl");
34842             if(this.config.adjustments){
34843                 w += this.config.adjustments[0];
34844             }
34845         }
34846         if(h !== null){
34847             this.el.setHeight(h);
34848             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34849             h -= this.el.getBorderWidth("tb");
34850             if(this.config.adjustments){
34851                 h += this.config.adjustments[1];
34852             }
34853             this.bodyEl.setHeight(h);
34854             if(this.tabs){
34855                 h = this.tabs.syncHeight(h);
34856             }
34857         }
34858         if(this.panelSize){
34859             w = w !== null ? w : this.panelSize.width;
34860             h = h !== null ? h : this.panelSize.height;
34861         }
34862         if(this.activePanel){
34863             var el = this.activePanel.getEl();
34864             w = w !== null ? w : el.getWidth();
34865             h = h !== null ? h : el.getHeight();
34866             this.panelSize = {width: w, height: h};
34867             this.activePanel.setSize(w, h);
34868         }
34869         if(Roo.isIE && this.tabs){
34870             this.tabs.el.repaint();
34871         }
34872     },
34873
34874     /**
34875      * Returns the container element for this region.
34876      * @return {Roo.Element}
34877      */
34878     getEl : function(){
34879         return this.el;
34880     },
34881
34882     /**
34883      * Hides this region.
34884      */
34885     hide : function(){
34886         if(!this.collapsed){
34887             this.el.dom.style.left = "-2000px";
34888             this.el.hide();
34889         }else{
34890             this.collapsedEl.dom.style.left = "-2000px";
34891             this.collapsedEl.hide();
34892         }
34893         this.visible = false;
34894         this.fireEvent("visibilitychange", this, false);
34895     },
34896
34897     /**
34898      * Shows this region if it was previously hidden.
34899      */
34900     show : function(){
34901         if(!this.collapsed){
34902             this.el.show();
34903         }else{
34904             this.collapsedEl.show();
34905         }
34906         this.visible = true;
34907         this.fireEvent("visibilitychange", this, true);
34908     },
34909
34910     closeClicked : function(){
34911         if(this.activePanel){
34912             this.remove(this.activePanel);
34913         }
34914     },
34915
34916     collapseClick : function(e){
34917         if(this.isSlid){
34918            e.stopPropagation();
34919            this.slideIn();
34920         }else{
34921            e.stopPropagation();
34922            this.slideOut();
34923         }
34924     },
34925
34926     /**
34927      * Collapses this region.
34928      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34929      */
34930     collapse : function(skipAnim, skipCheck){
34931         if(this.collapsed) {
34932             return;
34933         }
34934         
34935         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34936             
34937             this.collapsed = true;
34938             if(this.split){
34939                 this.split.el.hide();
34940             }
34941             if(this.config.animate && skipAnim !== true){
34942                 this.fireEvent("invalidated", this);
34943                 this.animateCollapse();
34944             }else{
34945                 this.el.setLocation(-20000,-20000);
34946                 this.el.hide();
34947                 this.collapsedEl.show();
34948                 this.fireEvent("collapsed", this);
34949                 this.fireEvent("invalidated", this);
34950             }
34951         }
34952         
34953     },
34954
34955     animateCollapse : function(){
34956         // overridden
34957     },
34958
34959     /**
34960      * Expands this region if it was previously collapsed.
34961      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34962      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34963      */
34964     expand : function(e, skipAnim){
34965         if(e) {
34966             e.stopPropagation();
34967         }
34968         if(!this.collapsed || this.el.hasActiveFx()) {
34969             return;
34970         }
34971         if(this.isSlid){
34972             this.afterSlideIn();
34973             skipAnim = true;
34974         }
34975         this.collapsed = false;
34976         if(this.config.animate && skipAnim !== true){
34977             this.animateExpand();
34978         }else{
34979             this.el.show();
34980             if(this.split){
34981                 this.split.el.show();
34982             }
34983             this.collapsedEl.setLocation(-2000,-2000);
34984             this.collapsedEl.hide();
34985             this.fireEvent("invalidated", this);
34986             this.fireEvent("expanded", this);
34987         }
34988     },
34989
34990     animateExpand : function(){
34991         // overridden
34992     },
34993
34994     initTabs : function()
34995     {
34996         this.bodyEl.setStyle("overflow", "hidden");
34997         var ts = new Roo.TabPanel(
34998                 this.bodyEl.dom,
34999                 {
35000                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
35001                     disableTooltips: this.config.disableTabTips,
35002                     toolbar : this.config.toolbar
35003                 }
35004         );
35005         if(this.config.hideTabs){
35006             ts.stripWrap.setDisplayed(false);
35007         }
35008         this.tabs = ts;
35009         ts.resizeTabs = this.config.resizeTabs === true;
35010         ts.minTabWidth = this.config.minTabWidth || 40;
35011         ts.maxTabWidth = this.config.maxTabWidth || 250;
35012         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35013         ts.monitorResize = false;
35014         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35015         ts.bodyEl.addClass('x-layout-tabs-body');
35016         this.panels.each(this.initPanelAsTab, this);
35017     },
35018
35019     initPanelAsTab : function(panel){
35020         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
35021                     this.config.closeOnTab && panel.isClosable());
35022         if(panel.tabTip !== undefined){
35023             ti.setTooltip(panel.tabTip);
35024         }
35025         ti.on("activate", function(){
35026               this.setActivePanel(panel);
35027         }, this);
35028         if(this.config.closeOnTab){
35029             ti.on("beforeclose", function(t, e){
35030                 e.cancel = true;
35031                 this.remove(panel);
35032             }, this);
35033         }
35034         return ti;
35035     },
35036
35037     updatePanelTitle : function(panel, title){
35038         if(this.activePanel == panel){
35039             this.updateTitle(title);
35040         }
35041         if(this.tabs){
35042             var ti = this.tabs.getTab(panel.getEl().id);
35043             ti.setText(title);
35044             if(panel.tabTip !== undefined){
35045                 ti.setTooltip(panel.tabTip);
35046             }
35047         }
35048     },
35049
35050     updateTitle : function(title){
35051         if(this.titleTextEl && !this.config.title){
35052             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35053         }
35054     },
35055
35056     setActivePanel : function(panel){
35057         panel = this.getPanel(panel);
35058         if(this.activePanel && this.activePanel != panel){
35059             this.activePanel.setActiveState(false);
35060         }
35061         this.activePanel = panel;
35062         panel.setActiveState(true);
35063         if(this.panelSize){
35064             panel.setSize(this.panelSize.width, this.panelSize.height);
35065         }
35066         if(this.closeBtn){
35067             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35068         }
35069         this.updateTitle(panel.getTitle());
35070         if(this.tabs){
35071             this.fireEvent("invalidated", this);
35072         }
35073         this.fireEvent("panelactivated", this, panel);
35074     },
35075
35076     /**
35077      * Shows the specified panel.
35078      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35079      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35080      */
35081     showPanel : function(panel)
35082     {
35083         panel = this.getPanel(panel);
35084         if(panel){
35085             if(this.tabs){
35086                 var tab = this.tabs.getTab(panel.getEl().id);
35087                 if(tab.isHidden()){
35088                     this.tabs.unhideTab(tab.id);
35089                 }
35090                 tab.activate();
35091             }else{
35092                 this.setActivePanel(panel);
35093             }
35094         }
35095         return panel;
35096     },
35097
35098     /**
35099      * Get the active panel for this region.
35100      * @return {Roo.ContentPanel} The active panel or null
35101      */
35102     getActivePanel : function(){
35103         return this.activePanel;
35104     },
35105
35106     validateVisibility : function(){
35107         if(this.panels.getCount() < 1){
35108             this.updateTitle("&#160;");
35109             this.closeBtn.hide();
35110             this.hide();
35111         }else{
35112             if(!this.isVisible()){
35113                 this.show();
35114             }
35115         }
35116     },
35117
35118     /**
35119      * Adds the passed ContentPanel(s) to this region.
35120      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35121      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35122      */
35123     add : function(panel){
35124         if(arguments.length > 1){
35125             for(var i = 0, len = arguments.length; i < len; i++) {
35126                 this.add(arguments[i]);
35127             }
35128             return null;
35129         }
35130         if(this.hasPanel(panel)){
35131             this.showPanel(panel);
35132             return panel;
35133         }
35134         panel.setRegion(this);
35135         this.panels.add(panel);
35136         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35137             this.bodyEl.dom.appendChild(panel.getEl().dom);
35138             if(panel.background !== true){
35139                 this.setActivePanel(panel);
35140             }
35141             this.fireEvent("paneladded", this, panel);
35142             return panel;
35143         }
35144         if(!this.tabs){
35145             this.initTabs();
35146         }else{
35147             this.initPanelAsTab(panel);
35148         }
35149         if(panel.background !== true){
35150             this.tabs.activate(panel.getEl().id);
35151         }
35152         this.fireEvent("paneladded", this, panel);
35153         return panel;
35154     },
35155
35156     /**
35157      * Hides the tab for the specified panel.
35158      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35159      */
35160     hidePanel : function(panel){
35161         if(this.tabs && (panel = this.getPanel(panel))){
35162             this.tabs.hideTab(panel.getEl().id);
35163         }
35164     },
35165
35166     /**
35167      * Unhides the tab for a previously hidden panel.
35168      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35169      */
35170     unhidePanel : function(panel){
35171         if(this.tabs && (panel = this.getPanel(panel))){
35172             this.tabs.unhideTab(panel.getEl().id);
35173         }
35174     },
35175
35176     clearPanels : function(){
35177         while(this.panels.getCount() > 0){
35178              this.remove(this.panels.first());
35179         }
35180     },
35181
35182     /**
35183      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35184      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35185      * @param {Boolean} preservePanel Overrides the config preservePanel option
35186      * @return {Roo.ContentPanel} The panel that was removed
35187      */
35188     remove : function(panel, preservePanel){
35189         panel = this.getPanel(panel);
35190         if(!panel){
35191             return null;
35192         }
35193         var e = {};
35194         this.fireEvent("beforeremove", this, panel, e);
35195         if(e.cancel === true){
35196             return null;
35197         }
35198         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35199         var panelId = panel.getId();
35200         this.panels.removeKey(panelId);
35201         if(preservePanel){
35202             document.body.appendChild(panel.getEl().dom);
35203         }
35204         if(this.tabs){
35205             this.tabs.removeTab(panel.getEl().id);
35206         }else if (!preservePanel){
35207             this.bodyEl.dom.removeChild(panel.getEl().dom);
35208         }
35209         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35210             var p = this.panels.first();
35211             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35212             tempEl.appendChild(p.getEl().dom);
35213             this.bodyEl.update("");
35214             this.bodyEl.dom.appendChild(p.getEl().dom);
35215             tempEl = null;
35216             this.updateTitle(p.getTitle());
35217             this.tabs = null;
35218             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35219             this.setActivePanel(p);
35220         }
35221         panel.setRegion(null);
35222         if(this.activePanel == panel){
35223             this.activePanel = null;
35224         }
35225         if(this.config.autoDestroy !== false && preservePanel !== true){
35226             try{panel.destroy();}catch(e){}
35227         }
35228         this.fireEvent("panelremoved", this, panel);
35229         return panel;
35230     },
35231
35232     /**
35233      * Returns the TabPanel component used by this region
35234      * @return {Roo.TabPanel}
35235      */
35236     getTabs : function(){
35237         return this.tabs;
35238     },
35239
35240     createTool : function(parentEl, className){
35241         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35242             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35243         btn.addClassOnOver("x-layout-tools-button-over");
35244         return btn;
35245     }
35246 });/*
35247  * Based on:
35248  * Ext JS Library 1.1.1
35249  * Copyright(c) 2006-2007, Ext JS, LLC.
35250  *
35251  * Originally Released Under LGPL - original licence link has changed is not relivant.
35252  *
35253  * Fork - LGPL
35254  * <script type="text/javascript">
35255  */
35256  
35257
35258
35259 /**
35260  * @class Roo.SplitLayoutRegion
35261  * @extends Roo.LayoutRegion
35262  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35263  */
35264 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35265     this.cursor = cursor;
35266     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35267 };
35268
35269 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35270     splitTip : "Drag to resize.",
35271     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35272     useSplitTips : false,
35273
35274     applyConfig : function(config){
35275         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35276         if(config.split){
35277             if(!this.split){
35278                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35279                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35280                 /** The SplitBar for this region 
35281                 * @type Roo.SplitBar */
35282                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35283                 this.split.on("moved", this.onSplitMove, this);
35284                 this.split.useShim = config.useShim === true;
35285                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35286                 if(this.useSplitTips){
35287                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35288                 }
35289                 if(config.collapsible){
35290                     this.split.el.on("dblclick", this.collapse,  this);
35291                 }
35292             }
35293             if(typeof config.minSize != "undefined"){
35294                 this.split.minSize = config.minSize;
35295             }
35296             if(typeof config.maxSize != "undefined"){
35297                 this.split.maxSize = config.maxSize;
35298             }
35299             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35300                 this.hideSplitter();
35301             }
35302         }
35303     },
35304
35305     getHMaxSize : function(){
35306          var cmax = this.config.maxSize || 10000;
35307          var center = this.mgr.getRegion("center");
35308          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35309     },
35310
35311     getVMaxSize : function(){
35312          var cmax = this.config.maxSize || 10000;
35313          var center = this.mgr.getRegion("center");
35314          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35315     },
35316
35317     onSplitMove : function(split, newSize){
35318         this.fireEvent("resized", this, newSize);
35319     },
35320     
35321     /** 
35322      * Returns the {@link Roo.SplitBar} for this region.
35323      * @return {Roo.SplitBar}
35324      */
35325     getSplitBar : function(){
35326         return this.split;
35327     },
35328     
35329     hide : function(){
35330         this.hideSplitter();
35331         Roo.SplitLayoutRegion.superclass.hide.call(this);
35332     },
35333
35334     hideSplitter : function(){
35335         if(this.split){
35336             this.split.el.setLocation(-2000,-2000);
35337             this.split.el.hide();
35338         }
35339     },
35340
35341     show : function(){
35342         if(this.split){
35343             this.split.el.show();
35344         }
35345         Roo.SplitLayoutRegion.superclass.show.call(this);
35346     },
35347     
35348     beforeSlide: function(){
35349         if(Roo.isGecko){// firefox overflow auto bug workaround
35350             this.bodyEl.clip();
35351             if(this.tabs) {
35352                 this.tabs.bodyEl.clip();
35353             }
35354             if(this.activePanel){
35355                 this.activePanel.getEl().clip();
35356                 
35357                 if(this.activePanel.beforeSlide){
35358                     this.activePanel.beforeSlide();
35359                 }
35360             }
35361         }
35362     },
35363     
35364     afterSlide : function(){
35365         if(Roo.isGecko){// firefox overflow auto bug workaround
35366             this.bodyEl.unclip();
35367             if(this.tabs) {
35368                 this.tabs.bodyEl.unclip();
35369             }
35370             if(this.activePanel){
35371                 this.activePanel.getEl().unclip();
35372                 if(this.activePanel.afterSlide){
35373                     this.activePanel.afterSlide();
35374                 }
35375             }
35376         }
35377     },
35378
35379     initAutoHide : function(){
35380         if(this.autoHide !== false){
35381             if(!this.autoHideHd){
35382                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35383                 this.autoHideHd = {
35384                     "mouseout": function(e){
35385                         if(!e.within(this.el, true)){
35386                             st.delay(500);
35387                         }
35388                     },
35389                     "mouseover" : function(e){
35390                         st.cancel();
35391                     },
35392                     scope : this
35393                 };
35394             }
35395             this.el.on(this.autoHideHd);
35396         }
35397     },
35398
35399     clearAutoHide : function(){
35400         if(this.autoHide !== false){
35401             this.el.un("mouseout", this.autoHideHd.mouseout);
35402             this.el.un("mouseover", this.autoHideHd.mouseover);
35403         }
35404     },
35405
35406     clearMonitor : function(){
35407         Roo.get(document).un("click", this.slideInIf, this);
35408     },
35409
35410     // these names are backwards but not changed for compat
35411     slideOut : function(){
35412         if(this.isSlid || this.el.hasActiveFx()){
35413             return;
35414         }
35415         this.isSlid = true;
35416         if(this.collapseBtn){
35417             this.collapseBtn.hide();
35418         }
35419         this.closeBtnState = this.closeBtn.getStyle('display');
35420         this.closeBtn.hide();
35421         if(this.stickBtn){
35422             this.stickBtn.show();
35423         }
35424         this.el.show();
35425         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35426         this.beforeSlide();
35427         this.el.setStyle("z-index", 10001);
35428         this.el.slideIn(this.getSlideAnchor(), {
35429             callback: function(){
35430                 this.afterSlide();
35431                 this.initAutoHide();
35432                 Roo.get(document).on("click", this.slideInIf, this);
35433                 this.fireEvent("slideshow", this);
35434             },
35435             scope: this,
35436             block: true
35437         });
35438     },
35439
35440     afterSlideIn : function(){
35441         this.clearAutoHide();
35442         this.isSlid = false;
35443         this.clearMonitor();
35444         this.el.setStyle("z-index", "");
35445         if(this.collapseBtn){
35446             this.collapseBtn.show();
35447         }
35448         this.closeBtn.setStyle('display', this.closeBtnState);
35449         if(this.stickBtn){
35450             this.stickBtn.hide();
35451         }
35452         this.fireEvent("slidehide", this);
35453     },
35454
35455     slideIn : function(cb){
35456         if(!this.isSlid || this.el.hasActiveFx()){
35457             Roo.callback(cb);
35458             return;
35459         }
35460         this.isSlid = false;
35461         this.beforeSlide();
35462         this.el.slideOut(this.getSlideAnchor(), {
35463             callback: function(){
35464                 this.el.setLeftTop(-10000, -10000);
35465                 this.afterSlide();
35466                 this.afterSlideIn();
35467                 Roo.callback(cb);
35468             },
35469             scope: this,
35470             block: true
35471         });
35472     },
35473     
35474     slideInIf : function(e){
35475         if(!e.within(this.el)){
35476             this.slideIn();
35477         }
35478     },
35479
35480     animateCollapse : function(){
35481         this.beforeSlide();
35482         this.el.setStyle("z-index", 20000);
35483         var anchor = this.getSlideAnchor();
35484         this.el.slideOut(anchor, {
35485             callback : function(){
35486                 this.el.setStyle("z-index", "");
35487                 this.collapsedEl.slideIn(anchor, {duration:.3});
35488                 this.afterSlide();
35489                 this.el.setLocation(-10000,-10000);
35490                 this.el.hide();
35491                 this.fireEvent("collapsed", this);
35492             },
35493             scope: this,
35494             block: true
35495         });
35496     },
35497
35498     animateExpand : function(){
35499         this.beforeSlide();
35500         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35501         this.el.setStyle("z-index", 20000);
35502         this.collapsedEl.hide({
35503             duration:.1
35504         });
35505         this.el.slideIn(this.getSlideAnchor(), {
35506             callback : function(){
35507                 this.el.setStyle("z-index", "");
35508                 this.afterSlide();
35509                 if(this.split){
35510                     this.split.el.show();
35511                 }
35512                 this.fireEvent("invalidated", this);
35513                 this.fireEvent("expanded", this);
35514             },
35515             scope: this,
35516             block: true
35517         });
35518     },
35519
35520     anchors : {
35521         "west" : "left",
35522         "east" : "right",
35523         "north" : "top",
35524         "south" : "bottom"
35525     },
35526
35527     sanchors : {
35528         "west" : "l",
35529         "east" : "r",
35530         "north" : "t",
35531         "south" : "b"
35532     },
35533
35534     canchors : {
35535         "west" : "tl-tr",
35536         "east" : "tr-tl",
35537         "north" : "tl-bl",
35538         "south" : "bl-tl"
35539     },
35540
35541     getAnchor : function(){
35542         return this.anchors[this.position];
35543     },
35544
35545     getCollapseAnchor : function(){
35546         return this.canchors[this.position];
35547     },
35548
35549     getSlideAnchor : function(){
35550         return this.sanchors[this.position];
35551     },
35552
35553     getAlignAdj : function(){
35554         var cm = this.cmargins;
35555         switch(this.position){
35556             case "west":
35557                 return [0, 0];
35558             break;
35559             case "east":
35560                 return [0, 0];
35561             break;
35562             case "north":
35563                 return [0, 0];
35564             break;
35565             case "south":
35566                 return [0, 0];
35567             break;
35568         }
35569     },
35570
35571     getExpandAdj : function(){
35572         var c = this.collapsedEl, cm = this.cmargins;
35573         switch(this.position){
35574             case "west":
35575                 return [-(cm.right+c.getWidth()+cm.left), 0];
35576             break;
35577             case "east":
35578                 return [cm.right+c.getWidth()+cm.left, 0];
35579             break;
35580             case "north":
35581                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35582             break;
35583             case "south":
35584                 return [0, cm.top+cm.bottom+c.getHeight()];
35585             break;
35586         }
35587     }
35588 });/*
35589  * Based on:
35590  * Ext JS Library 1.1.1
35591  * Copyright(c) 2006-2007, Ext JS, LLC.
35592  *
35593  * Originally Released Under LGPL - original licence link has changed is not relivant.
35594  *
35595  * Fork - LGPL
35596  * <script type="text/javascript">
35597  */
35598 /*
35599  * These classes are private internal classes
35600  */
35601 Roo.CenterLayoutRegion = function(mgr, config){
35602     Roo.LayoutRegion.call(this, mgr, config, "center");
35603     this.visible = true;
35604     this.minWidth = config.minWidth || 20;
35605     this.minHeight = config.minHeight || 20;
35606 };
35607
35608 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35609     hide : function(){
35610         // center panel can't be hidden
35611     },
35612     
35613     show : function(){
35614         // center panel can't be hidden
35615     },
35616     
35617     getMinWidth: function(){
35618         return this.minWidth;
35619     },
35620     
35621     getMinHeight: function(){
35622         return this.minHeight;
35623     }
35624 });
35625
35626
35627 Roo.NorthLayoutRegion = function(mgr, config){
35628     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35629     if(this.split){
35630         this.split.placement = Roo.SplitBar.TOP;
35631         this.split.orientation = Roo.SplitBar.VERTICAL;
35632         this.split.el.addClass("x-layout-split-v");
35633     }
35634     var size = config.initialSize || config.height;
35635     if(typeof size != "undefined"){
35636         this.el.setHeight(size);
35637     }
35638 };
35639 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35640     orientation: Roo.SplitBar.VERTICAL,
35641     getBox : function(){
35642         if(this.collapsed){
35643             return this.collapsedEl.getBox();
35644         }
35645         var box = this.el.getBox();
35646         if(this.split){
35647             box.height += this.split.el.getHeight();
35648         }
35649         return box;
35650     },
35651     
35652     updateBox : function(box){
35653         if(this.split && !this.collapsed){
35654             box.height -= this.split.el.getHeight();
35655             this.split.el.setLeft(box.x);
35656             this.split.el.setTop(box.y+box.height);
35657             this.split.el.setWidth(box.width);
35658         }
35659         if(this.collapsed){
35660             this.updateBody(box.width, null);
35661         }
35662         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35663     }
35664 });
35665
35666 Roo.SouthLayoutRegion = function(mgr, config){
35667     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35668     if(this.split){
35669         this.split.placement = Roo.SplitBar.BOTTOM;
35670         this.split.orientation = Roo.SplitBar.VERTICAL;
35671         this.split.el.addClass("x-layout-split-v");
35672     }
35673     var size = config.initialSize || config.height;
35674     if(typeof size != "undefined"){
35675         this.el.setHeight(size);
35676     }
35677 };
35678 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35679     orientation: Roo.SplitBar.VERTICAL,
35680     getBox : function(){
35681         if(this.collapsed){
35682             return this.collapsedEl.getBox();
35683         }
35684         var box = this.el.getBox();
35685         if(this.split){
35686             var sh = this.split.el.getHeight();
35687             box.height += sh;
35688             box.y -= sh;
35689         }
35690         return box;
35691     },
35692     
35693     updateBox : function(box){
35694         if(this.split && !this.collapsed){
35695             var sh = this.split.el.getHeight();
35696             box.height -= sh;
35697             box.y += sh;
35698             this.split.el.setLeft(box.x);
35699             this.split.el.setTop(box.y-sh);
35700             this.split.el.setWidth(box.width);
35701         }
35702         if(this.collapsed){
35703             this.updateBody(box.width, null);
35704         }
35705         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35706     }
35707 });
35708
35709 Roo.EastLayoutRegion = function(mgr, config){
35710     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35711     if(this.split){
35712         this.split.placement = Roo.SplitBar.RIGHT;
35713         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35714         this.split.el.addClass("x-layout-split-h");
35715     }
35716     var size = config.initialSize || config.width;
35717     if(typeof size != "undefined"){
35718         this.el.setWidth(size);
35719     }
35720 };
35721 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35722     orientation: Roo.SplitBar.HORIZONTAL,
35723     getBox : function(){
35724         if(this.collapsed){
35725             return this.collapsedEl.getBox();
35726         }
35727         var box = this.el.getBox();
35728         if(this.split){
35729             var sw = this.split.el.getWidth();
35730             box.width += sw;
35731             box.x -= sw;
35732         }
35733         return box;
35734     },
35735
35736     updateBox : function(box){
35737         if(this.split && !this.collapsed){
35738             var sw = this.split.el.getWidth();
35739             box.width -= sw;
35740             this.split.el.setLeft(box.x);
35741             this.split.el.setTop(box.y);
35742             this.split.el.setHeight(box.height);
35743             box.x += sw;
35744         }
35745         if(this.collapsed){
35746             this.updateBody(null, box.height);
35747         }
35748         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35749     }
35750 });
35751
35752 Roo.WestLayoutRegion = function(mgr, config){
35753     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35754     if(this.split){
35755         this.split.placement = Roo.SplitBar.LEFT;
35756         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35757         this.split.el.addClass("x-layout-split-h");
35758     }
35759     var size = config.initialSize || config.width;
35760     if(typeof size != "undefined"){
35761         this.el.setWidth(size);
35762     }
35763 };
35764 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35765     orientation: Roo.SplitBar.HORIZONTAL,
35766     getBox : function(){
35767         if(this.collapsed){
35768             return this.collapsedEl.getBox();
35769         }
35770         var box = this.el.getBox();
35771         if(this.split){
35772             box.width += this.split.el.getWidth();
35773         }
35774         return box;
35775     },
35776     
35777     updateBox : function(box){
35778         if(this.split && !this.collapsed){
35779             var sw = this.split.el.getWidth();
35780             box.width -= sw;
35781             this.split.el.setLeft(box.x+box.width);
35782             this.split.el.setTop(box.y);
35783             this.split.el.setHeight(box.height);
35784         }
35785         if(this.collapsed){
35786             this.updateBody(null, box.height);
35787         }
35788         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35789     }
35790 });
35791 /*
35792  * Based on:
35793  * Ext JS Library 1.1.1
35794  * Copyright(c) 2006-2007, Ext JS, LLC.
35795  *
35796  * Originally Released Under LGPL - original licence link has changed is not relivant.
35797  *
35798  * Fork - LGPL
35799  * <script type="text/javascript">
35800  */
35801  
35802  
35803 /*
35804  * Private internal class for reading and applying state
35805  */
35806 Roo.LayoutStateManager = function(layout){
35807      // default empty state
35808      this.state = {
35809         north: {},
35810         south: {},
35811         east: {},
35812         west: {}       
35813     };
35814 };
35815
35816 Roo.LayoutStateManager.prototype = {
35817     init : function(layout, provider){
35818         this.provider = provider;
35819         var state = provider.get(layout.id+"-layout-state");
35820         if(state){
35821             var wasUpdating = layout.isUpdating();
35822             if(!wasUpdating){
35823                 layout.beginUpdate();
35824             }
35825             for(var key in state){
35826                 if(typeof state[key] != "function"){
35827                     var rstate = state[key];
35828                     var r = layout.getRegion(key);
35829                     if(r && rstate){
35830                         if(rstate.size){
35831                             r.resizeTo(rstate.size);
35832                         }
35833                         if(rstate.collapsed == true){
35834                             r.collapse(true);
35835                         }else{
35836                             r.expand(null, true);
35837                         }
35838                     }
35839                 }
35840             }
35841             if(!wasUpdating){
35842                 layout.endUpdate();
35843             }
35844             this.state = state; 
35845         }
35846         this.layout = layout;
35847         layout.on("regionresized", this.onRegionResized, this);
35848         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35849         layout.on("regionexpanded", this.onRegionExpanded, this);
35850     },
35851     
35852     storeState : function(){
35853         this.provider.set(this.layout.id+"-layout-state", this.state);
35854     },
35855     
35856     onRegionResized : function(region, newSize){
35857         this.state[region.getPosition()].size = newSize;
35858         this.storeState();
35859     },
35860     
35861     onRegionCollapsed : function(region){
35862         this.state[region.getPosition()].collapsed = true;
35863         this.storeState();
35864     },
35865     
35866     onRegionExpanded : function(region){
35867         this.state[region.getPosition()].collapsed = false;
35868         this.storeState();
35869     }
35870 };/*
35871  * Based on:
35872  * Ext JS Library 1.1.1
35873  * Copyright(c) 2006-2007, Ext JS, LLC.
35874  *
35875  * Originally Released Under LGPL - original licence link has changed is not relivant.
35876  *
35877  * Fork - LGPL
35878  * <script type="text/javascript">
35879  */
35880 /**
35881  * @class Roo.ContentPanel
35882  * @extends Roo.util.Observable
35883  * @children Roo.form.Form Roo.JsonView Roo.View
35884  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35885  * A basic ContentPanel element.
35886  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35887  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35888  * @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
35889  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35890  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35891  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35892  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35893  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35894  * @cfg {String} title          The title for this panel
35895  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35896  * @cfg {String} url            Calls {@link #setUrl} with this value
35897  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35898  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35899  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35900  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35901  * @cfg {String}    style  Extra style to add to the content panel
35902  * @cfg {Roo.menu.Menu} menu  popup menu
35903
35904  * @constructor
35905  * Create a new ContentPanel.
35906  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35907  * @param {String/Object} config A string to set only the title or a config object
35908  * @param {String} content (optional) Set the HTML content for this panel
35909  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35910  */
35911 Roo.ContentPanel = function(el, config, content){
35912     
35913      
35914     /*
35915     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35916         config = el;
35917         el = Roo.id();
35918     }
35919     if (config && config.parentLayout) { 
35920         el = config.parentLayout.el.createChild(); 
35921     }
35922     */
35923     if(el.autoCreate){ // xtype is available if this is called from factory
35924         config = el;
35925         el = Roo.id();
35926     }
35927     this.el = Roo.get(el);
35928     if(!this.el && config && config.autoCreate){
35929         if(typeof config.autoCreate == "object"){
35930             if(!config.autoCreate.id){
35931                 config.autoCreate.id = config.id||el;
35932             }
35933             this.el = Roo.DomHelper.append(document.body,
35934                         config.autoCreate, true);
35935         }else{
35936             this.el = Roo.DomHelper.append(document.body,
35937                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35938         }
35939     }
35940     
35941     
35942     this.closable = false;
35943     this.loaded = false;
35944     this.active = false;
35945     if(typeof config == "string"){
35946         this.title = config;
35947     }else{
35948         Roo.apply(this, config);
35949     }
35950     
35951     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35952         this.wrapEl = this.el.wrap();
35953         this.toolbar.container = this.el.insertSibling(false, 'before');
35954         this.toolbar = new Roo.Toolbar(this.toolbar);
35955     }
35956     
35957     // xtype created footer. - not sure if will work as we normally have to render first..
35958     if (this.footer && !this.footer.el && this.footer.xtype) {
35959         if (!this.wrapEl) {
35960             this.wrapEl = this.el.wrap();
35961         }
35962     
35963         this.footer.container = this.wrapEl.createChild();
35964          
35965         this.footer = Roo.factory(this.footer, Roo);
35966         
35967     }
35968     
35969     if(this.resizeEl){
35970         this.resizeEl = Roo.get(this.resizeEl, true);
35971     }else{
35972         this.resizeEl = this.el;
35973     }
35974     // handle view.xtype
35975     
35976  
35977     
35978     
35979     this.addEvents({
35980         /**
35981          * @event activate
35982          * Fires when this panel is activated. 
35983          * @param {Roo.ContentPanel} this
35984          */
35985         "activate" : true,
35986         /**
35987          * @event deactivate
35988          * Fires when this panel is activated. 
35989          * @param {Roo.ContentPanel} this
35990          */
35991         "deactivate" : true,
35992
35993         /**
35994          * @event resize
35995          * Fires when this panel is resized if fitToFrame is true.
35996          * @param {Roo.ContentPanel} this
35997          * @param {Number} width The width after any component adjustments
35998          * @param {Number} height The height after any component adjustments
35999          */
36000         "resize" : true,
36001         
36002          /**
36003          * @event render
36004          * Fires when this tab is created
36005          * @param {Roo.ContentPanel} this
36006          */
36007         "render" : true
36008          
36009         
36010     });
36011     
36012
36013     
36014     
36015     if(this.autoScroll){
36016         this.resizeEl.setStyle("overflow", "auto");
36017     } else {
36018         // fix randome scrolling
36019         this.el.on('scroll', function() {
36020             Roo.log('fix random scolling');
36021             this.scrollTo('top',0); 
36022         });
36023     }
36024     content = content || this.content;
36025     if(content){
36026         this.setContent(content);
36027     }
36028     if(config && config.url){
36029         this.setUrl(this.url, this.params, this.loadOnce);
36030     }
36031     
36032     
36033     
36034     Roo.ContentPanel.superclass.constructor.call(this);
36035     
36036     if (this.view && typeof(this.view.xtype) != 'undefined') {
36037         this.view.el = this.el.appendChild(document.createElement("div"));
36038         this.view = Roo.factory(this.view); 
36039         this.view.render  &&  this.view.render(false, '');  
36040     }
36041     
36042     
36043     this.fireEvent('render', this);
36044 };
36045
36046 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
36047     tabTip:'',
36048     setRegion : function(region){
36049         this.region = region;
36050         if(region){
36051            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
36052         }else{
36053            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
36054         } 
36055     },
36056     
36057     /**
36058      * Returns the toolbar for this Panel if one was configured. 
36059      * @return {Roo.Toolbar} 
36060      */
36061     getToolbar : function(){
36062         return this.toolbar;
36063     },
36064     
36065     setActiveState : function(active){
36066         this.active = active;
36067         if(!active){
36068             this.fireEvent("deactivate", this);
36069         }else{
36070             this.fireEvent("activate", this);
36071         }
36072     },
36073     /**
36074      * Updates this panel's element
36075      * @param {String} content The new content
36076      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36077     */
36078     setContent : function(content, loadScripts){
36079         this.el.update(content, loadScripts);
36080     },
36081
36082     ignoreResize : function(w, h){
36083         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36084             return true;
36085         }else{
36086             this.lastSize = {width: w, height: h};
36087             return false;
36088         }
36089     },
36090     /**
36091      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36092      * @return {Roo.UpdateManager} The UpdateManager
36093      */
36094     getUpdateManager : function(){
36095         return this.el.getUpdateManager();
36096     },
36097      /**
36098      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36099      * @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:
36100 <pre><code>
36101 panel.load({
36102     url: "your-url.php",
36103     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36104     callback: yourFunction,
36105     scope: yourObject, //(optional scope)
36106     discardUrl: false,
36107     nocache: false,
36108     text: "Loading...",
36109     timeout: 30,
36110     scripts: false
36111 });
36112 </code></pre>
36113      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36114      * 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.
36115      * @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}
36116      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36117      * @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.
36118      * @return {Roo.ContentPanel} this
36119      */
36120     load : function(){
36121         var um = this.el.getUpdateManager();
36122         um.update.apply(um, arguments);
36123         return this;
36124     },
36125
36126
36127     /**
36128      * 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.
36129      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36130      * @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)
36131      * @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)
36132      * @return {Roo.UpdateManager} The UpdateManager
36133      */
36134     setUrl : function(url, params, loadOnce){
36135         if(this.refreshDelegate){
36136             this.removeListener("activate", this.refreshDelegate);
36137         }
36138         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36139         this.on("activate", this.refreshDelegate);
36140         return this.el.getUpdateManager();
36141     },
36142     
36143     _handleRefresh : function(url, params, loadOnce){
36144         if(!loadOnce || !this.loaded){
36145             var updater = this.el.getUpdateManager();
36146             updater.update(url, params, this._setLoaded.createDelegate(this));
36147         }
36148     },
36149     
36150     _setLoaded : function(){
36151         this.loaded = true;
36152     }, 
36153     
36154     /**
36155      * Returns this panel's id
36156      * @return {String} 
36157      */
36158     getId : function(){
36159         return this.el.id;
36160     },
36161     
36162     /** 
36163      * Returns this panel's element - used by regiosn to add.
36164      * @return {Roo.Element} 
36165      */
36166     getEl : function(){
36167         return this.wrapEl || this.el;
36168     },
36169     
36170     adjustForComponents : function(width, height)
36171     {
36172         //Roo.log('adjustForComponents ');
36173         if(this.resizeEl != this.el){
36174             width -= this.el.getFrameWidth('lr');
36175             height -= this.el.getFrameWidth('tb');
36176         }
36177         if(this.toolbar){
36178             var te = this.toolbar.getEl();
36179             height -= te.getHeight();
36180             te.setWidth(width);
36181         }
36182         if(this.footer){
36183             var te = this.footer.getEl();
36184             //Roo.log("footer:" + te.getHeight());
36185             
36186             height -= te.getHeight();
36187             te.setWidth(width);
36188         }
36189         
36190         
36191         if(this.adjustments){
36192             width += this.adjustments[0];
36193             height += this.adjustments[1];
36194         }
36195         return {"width": width, "height": height};
36196     },
36197     
36198     setSize : function(width, height){
36199         if(this.fitToFrame && !this.ignoreResize(width, height)){
36200             if(this.fitContainer && this.resizeEl != this.el){
36201                 this.el.setSize(width, height);
36202             }
36203             var size = this.adjustForComponents(width, height);
36204             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36205             this.fireEvent('resize', this, size.width, size.height);
36206         }
36207     },
36208     
36209     /**
36210      * Returns this panel's title
36211      * @return {String} 
36212      */
36213     getTitle : function(){
36214         return this.title;
36215     },
36216     
36217     /**
36218      * Set this panel's title
36219      * @param {String} title
36220      */
36221     setTitle : function(title){
36222         this.title = title;
36223         if(this.region){
36224             this.region.updatePanelTitle(this, title);
36225         }
36226     },
36227     
36228     /**
36229      * Returns true is this panel was configured to be closable
36230      * @return {Boolean} 
36231      */
36232     isClosable : function(){
36233         return this.closable;
36234     },
36235     
36236     beforeSlide : function(){
36237         this.el.clip();
36238         this.resizeEl.clip();
36239     },
36240     
36241     afterSlide : function(){
36242         this.el.unclip();
36243         this.resizeEl.unclip();
36244     },
36245     
36246     /**
36247      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36248      *   Will fail silently if the {@link #setUrl} method has not been called.
36249      *   This does not activate the panel, just updates its content.
36250      */
36251     refresh : function(){
36252         if(this.refreshDelegate){
36253            this.loaded = false;
36254            this.refreshDelegate();
36255         }
36256     },
36257     
36258     /**
36259      * Destroys this panel
36260      */
36261     destroy : function(){
36262         this.el.removeAllListeners();
36263         var tempEl = document.createElement("span");
36264         tempEl.appendChild(this.el.dom);
36265         tempEl.innerHTML = "";
36266         this.el.remove();
36267         this.el = null;
36268     },
36269     
36270     /**
36271      * form - if the content panel contains a form - this is a reference to it.
36272      * @type {Roo.form.Form}
36273      */
36274     form : false,
36275     /**
36276      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36277      *    This contains a reference to it.
36278      * @type {Roo.View}
36279      */
36280     view : false,
36281     
36282       /**
36283      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36284      * <pre><code>
36285
36286 layout.addxtype({
36287        xtype : 'Form',
36288        items: [ .... ]
36289    }
36290 );
36291
36292 </code></pre>
36293      * @param {Object} cfg Xtype definition of item to add.
36294      */
36295     
36296     addxtype : function(cfg) {
36297         // add form..
36298         if (cfg.xtype.match(/^Form$/)) {
36299             
36300             var el;
36301             //if (this.footer) {
36302             //    el = this.footer.container.insertSibling(false, 'before');
36303             //} else {
36304                 el = this.el.createChild();
36305             //}
36306
36307             this.form = new  Roo.form.Form(cfg);
36308             
36309             
36310             if ( this.form.allItems.length) {
36311                 this.form.render(el.dom);
36312             }
36313             return this.form;
36314         }
36315         // should only have one of theses..
36316         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36317             // views.. should not be just added - used named prop 'view''
36318             
36319             cfg.el = this.el.appendChild(document.createElement("div"));
36320             // factory?
36321             
36322             var ret = new Roo.factory(cfg);
36323              
36324              ret.render && ret.render(false, ''); // render blank..
36325             this.view = ret;
36326             return ret;
36327         }
36328         return false;
36329     }
36330 });
36331
36332
36333
36334
36335
36336
36337
36338
36339
36340
36341
36342
36343 /**
36344  * @class Roo.GridPanel
36345  * @extends Roo.ContentPanel
36346  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36347  * @constructor
36348  * Create a new GridPanel.
36349  * @cfg {Roo.grid.Grid} grid The grid for this panel
36350  */
36351 Roo.GridPanel = function(grid, config){
36352     
36353     // universal ctor...
36354     if (typeof(grid.grid) != 'undefined') {
36355         config = grid;
36356         grid = config.grid;
36357     }
36358     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36359         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36360         
36361     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36362     
36363     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36364     
36365     if(this.toolbar){
36366         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36367     }
36368     // xtype created footer. - not sure if will work as we normally have to render first..
36369     if (this.footer && !this.footer.el && this.footer.xtype) {
36370         
36371         this.footer.container = this.grid.getView().getFooterPanel(true);
36372         this.footer.dataSource = this.grid.dataSource;
36373         this.footer = Roo.factory(this.footer, Roo);
36374         
36375     }
36376     
36377     grid.monitorWindowResize = false; // turn off autosizing
36378     grid.autoHeight = false;
36379     grid.autoWidth = false;
36380     this.grid = grid;
36381     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36382 };
36383
36384 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36385     getId : function(){
36386         return this.grid.id;
36387     },
36388     
36389     /**
36390      * Returns the grid for this panel
36391      * @return {Roo.grid.Grid} 
36392      */
36393     getGrid : function(){
36394         return this.grid;    
36395     },
36396     
36397     setSize : function(width, height){
36398         if(!this.ignoreResize(width, height)){
36399             var grid = this.grid;
36400             var size = this.adjustForComponents(width, height);
36401             grid.getGridEl().setSize(size.width, size.height);
36402             grid.autoSize();
36403         }
36404     },
36405     
36406     beforeSlide : function(){
36407         this.grid.getView().scroller.clip();
36408     },
36409     
36410     afterSlide : function(){
36411         this.grid.getView().scroller.unclip();
36412     },
36413     
36414     destroy : function(){
36415         this.grid.destroy();
36416         delete this.grid;
36417         Roo.GridPanel.superclass.destroy.call(this); 
36418     }
36419 });
36420
36421
36422 /**
36423  * @class Roo.NestedLayoutPanel
36424  * @extends Roo.ContentPanel
36425  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36426  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
36427  *
36428  * 
36429  * @constructor
36430  * Create a new NestedLayoutPanel.
36431  * 
36432  * 
36433  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36434  * @param {String/Object} config A string to set only the title or a config object
36435  */
36436 Roo.NestedLayoutPanel = function(layout, config)
36437 {
36438     // construct with only one argument..
36439     /* FIXME - implement nicer consturctors
36440     if (layout.layout) {
36441         config = layout;
36442         layout = config.layout;
36443         delete config.layout;
36444     }
36445     if (layout.xtype && !layout.getEl) {
36446         // then layout needs constructing..
36447         layout = Roo.factory(layout, Roo);
36448     }
36449     */
36450     
36451     
36452     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36453     
36454     layout.monitorWindowResize = false; // turn off autosizing
36455     this.layout = layout;
36456     this.layout.getEl().addClass("x-layout-nested-layout");
36457     
36458     
36459     
36460     
36461 };
36462
36463 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36464
36465     layout : false,
36466
36467     setSize : function(width, height){
36468         if(!this.ignoreResize(width, height)){
36469             var size = this.adjustForComponents(width, height);
36470             var el = this.layout.getEl();
36471             el.setSize(size.width, size.height);
36472             var touch = el.dom.offsetWidth;
36473             this.layout.layout();
36474             // ie requires a double layout on the first pass
36475             if(Roo.isIE && !this.initialized){
36476                 this.initialized = true;
36477                 this.layout.layout();
36478             }
36479         }
36480     },
36481     
36482     // activate all subpanels if not currently active..
36483     
36484     setActiveState : function(active){
36485         this.active = active;
36486         if(!active){
36487             this.fireEvent("deactivate", this);
36488             return;
36489         }
36490         
36491         this.fireEvent("activate", this);
36492         // not sure if this should happen before or after..
36493         if (!this.layout) {
36494             return; // should not happen..
36495         }
36496         var reg = false;
36497         for (var r in this.layout.regions) {
36498             reg = this.layout.getRegion(r);
36499             if (reg.getActivePanel()) {
36500                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36501                 reg.setActivePanel(reg.getActivePanel());
36502                 continue;
36503             }
36504             if (!reg.panels.length) {
36505                 continue;
36506             }
36507             reg.showPanel(reg.getPanel(0));
36508         }
36509         
36510         
36511         
36512         
36513     },
36514     
36515     /**
36516      * Returns the nested BorderLayout for this panel
36517      * @return {Roo.BorderLayout}
36518      */
36519     getLayout : function(){
36520         return this.layout;
36521     },
36522     
36523      /**
36524      * Adds a xtype elements to the layout of the nested panel
36525      * <pre><code>
36526
36527 panel.addxtype({
36528        xtype : 'ContentPanel',
36529        region: 'west',
36530        items: [ .... ]
36531    }
36532 );
36533
36534 panel.addxtype({
36535         xtype : 'NestedLayoutPanel',
36536         region: 'west',
36537         layout: {
36538            center: { },
36539            west: { }   
36540         },
36541         items : [ ... list of content panels or nested layout panels.. ]
36542    }
36543 );
36544 </code></pre>
36545      * @param {Object} cfg Xtype definition of item to add.
36546      */
36547     addxtype : function(cfg) {
36548         return this.layout.addxtype(cfg);
36549     
36550     }
36551 });
36552
36553 Roo.ScrollPanel = function(el, config, content){
36554     config = config || {};
36555     config.fitToFrame = true;
36556     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36557     
36558     this.el.dom.style.overflow = "hidden";
36559     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36560     this.el.removeClass("x-layout-inactive-content");
36561     this.el.on("mousewheel", this.onWheel, this);
36562
36563     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36564     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36565     up.unselectable(); down.unselectable();
36566     up.on("click", this.scrollUp, this);
36567     down.on("click", this.scrollDown, this);
36568     up.addClassOnOver("x-scroller-btn-over");
36569     down.addClassOnOver("x-scroller-btn-over");
36570     up.addClassOnClick("x-scroller-btn-click");
36571     down.addClassOnClick("x-scroller-btn-click");
36572     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36573
36574     this.resizeEl = this.el;
36575     this.el = wrap; this.up = up; this.down = down;
36576 };
36577
36578 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36579     increment : 100,
36580     wheelIncrement : 5,
36581     scrollUp : function(){
36582         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36583     },
36584
36585     scrollDown : function(){
36586         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36587     },
36588
36589     afterScroll : function(){
36590         var el = this.resizeEl;
36591         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36592         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36593         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36594     },
36595
36596     setSize : function(){
36597         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36598         this.afterScroll();
36599     },
36600
36601     onWheel : function(e){
36602         var d = e.getWheelDelta();
36603         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36604         this.afterScroll();
36605         e.stopEvent();
36606     },
36607
36608     setContent : function(content, loadScripts){
36609         this.resizeEl.update(content, loadScripts);
36610     }
36611
36612 });
36613
36614
36615
36616 /**
36617  * @class Roo.TreePanel
36618  * @extends Roo.ContentPanel
36619  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36620  * Treepanel component
36621  * 
36622  * @constructor
36623  * Create a new TreePanel. - defaults to fit/scoll contents.
36624  * @param {String/Object} config A string to set only the panel's title, or a config object
36625  */
36626 Roo.TreePanel = function(config){
36627     var el = config.el;
36628     var tree = config.tree;
36629     delete config.tree; 
36630     delete config.el; // hopefull!
36631     
36632     // wrapper for IE7 strict & safari scroll issue
36633     
36634     var treeEl = el.createChild();
36635     config.resizeEl = treeEl;
36636     
36637     
36638     
36639     Roo.TreePanel.superclass.constructor.call(this, el, config);
36640  
36641  
36642     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36643     //console.log(tree);
36644     this.on('activate', function()
36645     {
36646         if (this.tree.rendered) {
36647             return;
36648         }
36649         //console.log('render tree');
36650         this.tree.render();
36651     });
36652     // this should not be needed.. - it's actually the 'el' that resizes?
36653     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36654     
36655     //this.on('resize',  function (cp, w, h) {
36656     //        this.tree.innerCt.setWidth(w);
36657     //        this.tree.innerCt.setHeight(h);
36658     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36659     //});
36660
36661         
36662     
36663 };
36664
36665 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36666     fitToFrame : true,
36667     autoScroll : true,
36668     /*
36669      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36670      */
36671     tree : false
36672
36673 });
36674 /*
36675  * Based on:
36676  * Ext JS Library 1.1.1
36677  * Copyright(c) 2006-2007, Ext JS, LLC.
36678  *
36679  * Originally Released Under LGPL - original licence link has changed is not relivant.
36680  *
36681  * Fork - LGPL
36682  * <script type="text/javascript">
36683  */
36684  
36685
36686 /**
36687  * @class Roo.ReaderLayout
36688  * @extends Roo.BorderLayout
36689  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36690  * center region containing two nested regions (a top one for a list view and one for item preview below),
36691  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36692  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36693  * expedites the setup of the overall layout and regions for this common application style.
36694  * Example:
36695  <pre><code>
36696 var reader = new Roo.ReaderLayout();
36697 var CP = Roo.ContentPanel;  // shortcut for adding
36698
36699 reader.beginUpdate();
36700 reader.add("north", new CP("north", "North"));
36701 reader.add("west", new CP("west", {title: "West"}));
36702 reader.add("east", new CP("east", {title: "East"}));
36703
36704 reader.regions.listView.add(new CP("listView", "List"));
36705 reader.regions.preview.add(new CP("preview", "Preview"));
36706 reader.endUpdate();
36707 </code></pre>
36708 * @constructor
36709 * Create a new ReaderLayout
36710 * @param {Object} config Configuration options
36711 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36712 * document.body if omitted)
36713 */
36714 Roo.ReaderLayout = function(config, renderTo){
36715     var c = config || {size:{}};
36716     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36717         north: c.north !== false ? Roo.apply({
36718             split:false,
36719             initialSize: 32,
36720             titlebar: false
36721         }, c.north) : false,
36722         west: c.west !== false ? Roo.apply({
36723             split:true,
36724             initialSize: 200,
36725             minSize: 175,
36726             maxSize: 400,
36727             titlebar: true,
36728             collapsible: true,
36729             animate: true,
36730             margins:{left:5,right:0,bottom:5,top:5},
36731             cmargins:{left:5,right:5,bottom:5,top:5}
36732         }, c.west) : false,
36733         east: c.east !== false ? Roo.apply({
36734             split:true,
36735             initialSize: 200,
36736             minSize: 175,
36737             maxSize: 400,
36738             titlebar: true,
36739             collapsible: true,
36740             animate: true,
36741             margins:{left:0,right:5,bottom:5,top:5},
36742             cmargins:{left:5,right:5,bottom:5,top:5}
36743         }, c.east) : false,
36744         center: Roo.apply({
36745             tabPosition: 'top',
36746             autoScroll:false,
36747             closeOnTab: true,
36748             titlebar:false,
36749             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36750         }, c.center)
36751     });
36752
36753     this.el.addClass('x-reader');
36754
36755     this.beginUpdate();
36756
36757     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36758         south: c.preview !== false ? Roo.apply({
36759             split:true,
36760             initialSize: 200,
36761             minSize: 100,
36762             autoScroll:true,
36763             collapsible:true,
36764             titlebar: true,
36765             cmargins:{top:5,left:0, right:0, bottom:0}
36766         }, c.preview) : false,
36767         center: Roo.apply({
36768             autoScroll:false,
36769             titlebar:false,
36770             minHeight:200
36771         }, c.listView)
36772     });
36773     this.add('center', new Roo.NestedLayoutPanel(inner,
36774             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36775
36776     this.endUpdate();
36777
36778     this.regions.preview = inner.getRegion('south');
36779     this.regions.listView = inner.getRegion('center');
36780 };
36781
36782 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36783  * Based on:
36784  * Ext JS Library 1.1.1
36785  * Copyright(c) 2006-2007, Ext JS, LLC.
36786  *
36787  * Originally Released Under LGPL - original licence link has changed is not relivant.
36788  *
36789  * Fork - LGPL
36790  * <script type="text/javascript">
36791  */
36792  
36793 /**
36794  * @class Roo.grid.Grid
36795  * @extends Roo.util.Observable
36796  * This class represents the primary interface of a component based grid control.
36797  * <br><br>Usage:<pre><code>
36798  var grid = new Roo.grid.Grid("my-container-id", {
36799      ds: myDataStore,
36800      cm: myColModel,
36801      selModel: mySelectionModel,
36802      autoSizeColumns: true,
36803      monitorWindowResize: false,
36804      trackMouseOver: true
36805  });
36806  // set any options
36807  grid.render();
36808  * </code></pre>
36809  * <b>Common Problems:</b><br/>
36810  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36811  * element will correct this<br/>
36812  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36813  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36814  * are unpredictable.<br/>
36815  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36816  * grid to calculate dimensions/offsets.<br/>
36817   * @constructor
36818  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36819  * The container MUST have some type of size defined for the grid to fill. The container will be
36820  * automatically set to position relative if it isn't already.
36821  * @param {Object} config A config object that sets properties on this grid.
36822  */
36823 Roo.grid.Grid = function(container, config){
36824         // initialize the container
36825         this.container = Roo.get(container);
36826         this.container.update("");
36827         this.container.setStyle("overflow", "hidden");
36828     this.container.addClass('x-grid-container');
36829
36830     this.id = this.container.id;
36831
36832     Roo.apply(this, config);
36833     // check and correct shorthanded configs
36834     if(this.ds){
36835         this.dataSource = this.ds;
36836         delete this.ds;
36837     }
36838     if(this.cm){
36839         this.colModel = this.cm;
36840         delete this.cm;
36841     }
36842     if(this.sm){
36843         this.selModel = this.sm;
36844         delete this.sm;
36845     }
36846
36847     if (this.selModel) {
36848         this.selModel = Roo.factory(this.selModel, Roo.grid);
36849         this.sm = this.selModel;
36850         this.sm.xmodule = this.xmodule || false;
36851     }
36852     if (typeof(this.colModel.config) == 'undefined') {
36853         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36854         this.cm = this.colModel;
36855         this.cm.xmodule = this.xmodule || false;
36856     }
36857     if (this.dataSource) {
36858         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36859         this.ds = this.dataSource;
36860         this.ds.xmodule = this.xmodule || false;
36861          
36862     }
36863     
36864     
36865     
36866     if(this.width){
36867         this.container.setWidth(this.width);
36868     }
36869
36870     if(this.height){
36871         this.container.setHeight(this.height);
36872     }
36873     /** @private */
36874         this.addEvents({
36875         // raw events
36876         /**
36877          * @event click
36878          * The raw click event for the entire grid.
36879          * @param {Roo.EventObject} e
36880          */
36881         "click" : true,
36882         /**
36883          * @event dblclick
36884          * The raw dblclick event for the entire grid.
36885          * @param {Roo.EventObject} e
36886          */
36887         "dblclick" : true,
36888         /**
36889          * @event contextmenu
36890          * The raw contextmenu event for the entire grid.
36891          * @param {Roo.EventObject} e
36892          */
36893         "contextmenu" : true,
36894         /**
36895          * @event mousedown
36896          * The raw mousedown event for the entire grid.
36897          * @param {Roo.EventObject} e
36898          */
36899         "mousedown" : true,
36900         /**
36901          * @event mouseup
36902          * The raw mouseup event for the entire grid.
36903          * @param {Roo.EventObject} e
36904          */
36905         "mouseup" : true,
36906         /**
36907          * @event mouseover
36908          * The raw mouseover event for the entire grid.
36909          * @param {Roo.EventObject} e
36910          */
36911         "mouseover" : true,
36912         /**
36913          * @event mouseout
36914          * The raw mouseout event for the entire grid.
36915          * @param {Roo.EventObject} e
36916          */
36917         "mouseout" : true,
36918         /**
36919          * @event keypress
36920          * The raw keypress event for the entire grid.
36921          * @param {Roo.EventObject} e
36922          */
36923         "keypress" : true,
36924         /**
36925          * @event keydown
36926          * The raw keydown event for the entire grid.
36927          * @param {Roo.EventObject} e
36928          */
36929         "keydown" : true,
36930
36931         // custom events
36932
36933         /**
36934          * @event cellclick
36935          * Fires when a cell is clicked
36936          * @param {Grid} this
36937          * @param {Number} rowIndex
36938          * @param {Number} columnIndex
36939          * @param {Roo.EventObject} e
36940          */
36941         "cellclick" : true,
36942         /**
36943          * @event celldblclick
36944          * Fires when a cell is double clicked
36945          * @param {Grid} this
36946          * @param {Number} rowIndex
36947          * @param {Number} columnIndex
36948          * @param {Roo.EventObject} e
36949          */
36950         "celldblclick" : true,
36951         /**
36952          * @event rowclick
36953          * Fires when a row is clicked
36954          * @param {Grid} this
36955          * @param {Number} rowIndex
36956          * @param {Roo.EventObject} e
36957          */
36958         "rowclick" : true,
36959         /**
36960          * @event rowdblclick
36961          * Fires when a row is double clicked
36962          * @param {Grid} this
36963          * @param {Number} rowIndex
36964          * @param {Roo.EventObject} e
36965          */
36966         "rowdblclick" : true,
36967         /**
36968          * @event headerclick
36969          * Fires when a header is clicked
36970          * @param {Grid} this
36971          * @param {Number} columnIndex
36972          * @param {Roo.EventObject} e
36973          */
36974         "headerclick" : true,
36975         /**
36976          * @event headerdblclick
36977          * Fires when a header cell is double clicked
36978          * @param {Grid} this
36979          * @param {Number} columnIndex
36980          * @param {Roo.EventObject} e
36981          */
36982         "headerdblclick" : true,
36983         /**
36984          * @event rowcontextmenu
36985          * Fires when a row is right clicked
36986          * @param {Grid} this
36987          * @param {Number} rowIndex
36988          * @param {Roo.EventObject} e
36989          */
36990         "rowcontextmenu" : true,
36991         /**
36992          * @event cellcontextmenu
36993          * Fires when a cell is right clicked
36994          * @param {Grid} this
36995          * @param {Number} rowIndex
36996          * @param {Number} cellIndex
36997          * @param {Roo.EventObject} e
36998          */
36999          "cellcontextmenu" : true,
37000         /**
37001          * @event headercontextmenu
37002          * Fires when a header is right clicked
37003          * @param {Grid} this
37004          * @param {Number} columnIndex
37005          * @param {Roo.EventObject} e
37006          */
37007         "headercontextmenu" : true,
37008         /**
37009          * @event bodyscroll
37010          * Fires when the body element is scrolled
37011          * @param {Number} scrollLeft
37012          * @param {Number} scrollTop
37013          */
37014         "bodyscroll" : true,
37015         /**
37016          * @event columnresize
37017          * Fires when the user resizes a column
37018          * @param {Number} columnIndex
37019          * @param {Number} newSize
37020          */
37021         "columnresize" : true,
37022         /**
37023          * @event columnmove
37024          * Fires when the user moves a column
37025          * @param {Number} oldIndex
37026          * @param {Number} newIndex
37027          */
37028         "columnmove" : true,
37029         /**
37030          * @event startdrag
37031          * Fires when row(s) start being dragged
37032          * @param {Grid} this
37033          * @param {Roo.GridDD} dd The drag drop object
37034          * @param {event} e The raw browser event
37035          */
37036         "startdrag" : true,
37037         /**
37038          * @event enddrag
37039          * Fires when a drag operation is complete
37040          * @param {Grid} this
37041          * @param {Roo.GridDD} dd The drag drop object
37042          * @param {event} e The raw browser event
37043          */
37044         "enddrag" : true,
37045         /**
37046          * @event dragdrop
37047          * Fires when dragged row(s) are dropped on a valid DD target
37048          * @param {Grid} this
37049          * @param {Roo.GridDD} dd The drag drop object
37050          * @param {String} targetId The target drag drop object
37051          * @param {event} e The raw browser event
37052          */
37053         "dragdrop" : true,
37054         /**
37055          * @event dragover
37056          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37057          * @param {Grid} this
37058          * @param {Roo.GridDD} dd The drag drop object
37059          * @param {String} targetId The target drag drop object
37060          * @param {event} e The raw browser event
37061          */
37062         "dragover" : true,
37063         /**
37064          * @event dragenter
37065          *  Fires when the dragged row(s) first cross another DD target while being dragged
37066          * @param {Grid} this
37067          * @param {Roo.GridDD} dd The drag drop object
37068          * @param {String} targetId The target drag drop object
37069          * @param {event} e The raw browser event
37070          */
37071         "dragenter" : true,
37072         /**
37073          * @event dragout
37074          * Fires when the dragged row(s) leave another DD target while being dragged
37075          * @param {Grid} this
37076          * @param {Roo.GridDD} dd The drag drop object
37077          * @param {String} targetId The target drag drop object
37078          * @param {event} e The raw browser event
37079          */
37080         "dragout" : true,
37081         /**
37082          * @event rowclass
37083          * Fires when a row is rendered, so you can change add a style to it.
37084          * @param {GridView} gridview   The grid view
37085          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37086          */
37087         'rowclass' : true,
37088
37089         /**
37090          * @event render
37091          * Fires when the grid is rendered
37092          * @param {Grid} grid
37093          */
37094         'render' : true
37095     });
37096
37097     Roo.grid.Grid.superclass.constructor.call(this);
37098 };
37099 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
37100     
37101     /**
37102          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
37103          */
37104         /**
37105          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
37106          */
37107         /**
37108          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
37109          */
37110         /**
37111          * @cfg {Roo.data.Store} ds The data store for the grid
37112          */
37113         /**
37114          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
37115          */
37116         /**
37117      * @cfg {String} ddGroup - drag drop group.
37118      */
37119       /**
37120      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
37121      */
37122
37123     /**
37124      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
37125      */
37126     minColumnWidth : 25,
37127
37128     /**
37129      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
37130      * <b>on initial render.</b> It is more efficient to explicitly size the columns
37131      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
37132      */
37133     autoSizeColumns : false,
37134
37135     /**
37136      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
37137      */
37138     autoSizeHeaders : true,
37139
37140     /**
37141      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
37142      */
37143     monitorWindowResize : true,
37144
37145     /**
37146      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
37147      * rows measured to get a columns size. Default is 0 (all rows).
37148      */
37149     maxRowsToMeasure : 0,
37150
37151     /**
37152      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
37153      */
37154     trackMouseOver : true,
37155
37156     /**
37157     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
37158     */
37159       /**
37160     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
37161     */
37162     
37163     /**
37164     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37165     */
37166     enableDragDrop : false,
37167     
37168     /**
37169     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37170     */
37171     enableColumnMove : true,
37172     
37173     /**
37174     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37175     */
37176     enableColumnHide : true,
37177     
37178     /**
37179     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37180     */
37181     enableRowHeightSync : false,
37182     
37183     /**
37184     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
37185     */
37186     stripeRows : true,
37187     
37188     /**
37189     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37190     */
37191     autoHeight : false,
37192
37193     /**
37194      * @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.
37195      */
37196     autoExpandColumn : false,
37197
37198     /**
37199     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37200     * Default is 50.
37201     */
37202     autoExpandMin : 50,
37203
37204     /**
37205     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37206     */
37207     autoExpandMax : 1000,
37208
37209     /**
37210     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37211     */
37212     view : null,
37213
37214     /**
37215     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37216     */
37217     loadMask : false,
37218     /**
37219     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37220     */
37221     dropTarget: false,
37222      /**
37223     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37224     */ 
37225     sortColMenu : false,
37226     
37227     // private
37228     rendered : false,
37229
37230     /**
37231     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37232     * of a fixed width. Default is false.
37233     */
37234     /**
37235     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37236     */
37237     
37238     
37239     /**
37240     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37241     * %0 is replaced with the number of selected rows.
37242     */
37243     ddText : "{0} selected row{1}",
37244     
37245     
37246     /**
37247      * Called once after all setup has been completed and the grid is ready to be rendered.
37248      * @return {Roo.grid.Grid} this
37249      */
37250     render : function()
37251     {
37252         var c = this.container;
37253         // try to detect autoHeight/width mode
37254         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37255             this.autoHeight = true;
37256         }
37257         var view = this.getView();
37258         view.init(this);
37259
37260         c.on("click", this.onClick, this);
37261         c.on("dblclick", this.onDblClick, this);
37262         c.on("contextmenu", this.onContextMenu, this);
37263         c.on("keydown", this.onKeyDown, this);
37264         if (Roo.isTouch) {
37265             c.on("touchstart", this.onTouchStart, this);
37266         }
37267
37268         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37269
37270         this.getSelectionModel().init(this);
37271
37272         view.render();
37273
37274         if(this.loadMask){
37275             this.loadMask = new Roo.LoadMask(this.container,
37276                     Roo.apply({store:this.dataSource}, this.loadMask));
37277         }
37278         
37279         
37280         if (this.toolbar && this.toolbar.xtype) {
37281             this.toolbar.container = this.getView().getHeaderPanel(true);
37282             this.toolbar = new Roo.Toolbar(this.toolbar);
37283         }
37284         if (this.footer && this.footer.xtype) {
37285             this.footer.dataSource = this.getDataSource();
37286             this.footer.container = this.getView().getFooterPanel(true);
37287             this.footer = Roo.factory(this.footer, Roo);
37288         }
37289         if (this.dropTarget && this.dropTarget.xtype) {
37290             delete this.dropTarget.xtype;
37291             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37292         }
37293         
37294         
37295         this.rendered = true;
37296         this.fireEvent('render', this);
37297         return this;
37298     },
37299
37300     /**
37301      * Reconfigures the grid to use a different Store and Column Model.
37302      * The View will be bound to the new objects and refreshed.
37303      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37304      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37305      */
37306     reconfigure : function(dataSource, colModel){
37307         if(this.loadMask){
37308             this.loadMask.destroy();
37309             this.loadMask = new Roo.LoadMask(this.container,
37310                     Roo.apply({store:dataSource}, this.loadMask));
37311         }
37312         this.view.bind(dataSource, colModel);
37313         this.dataSource = dataSource;
37314         this.colModel = colModel;
37315         this.view.refresh(true);
37316     },
37317     /**
37318      * addColumns
37319      * Add's a column, default at the end..
37320      
37321      * @param {int} position to add (default end)
37322      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37323      */
37324     addColumns : function(pos, ar)
37325     {
37326         
37327         for (var i =0;i< ar.length;i++) {
37328             var cfg = ar[i];
37329             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37330             this.cm.lookup[cfg.id] = cfg;
37331         }
37332         
37333         
37334         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37335             pos = this.cm.config.length; //this.cm.config.push(cfg);
37336         } 
37337         pos = Math.max(0,pos);
37338         ar.unshift(0);
37339         ar.unshift(pos);
37340         this.cm.config.splice.apply(this.cm.config, ar);
37341         
37342         
37343         
37344         this.view.generateRules(this.cm);
37345         this.view.refresh(true);
37346         
37347     },
37348     
37349     
37350     
37351     
37352     // private
37353     onKeyDown : function(e){
37354         this.fireEvent("keydown", e);
37355     },
37356
37357     /**
37358      * Destroy this grid.
37359      * @param {Boolean} removeEl True to remove the element
37360      */
37361     destroy : function(removeEl, keepListeners){
37362         if(this.loadMask){
37363             this.loadMask.destroy();
37364         }
37365         var c = this.container;
37366         c.removeAllListeners();
37367         this.view.destroy();
37368         this.colModel.purgeListeners();
37369         if(!keepListeners){
37370             this.purgeListeners();
37371         }
37372         c.update("");
37373         if(removeEl === true){
37374             c.remove();
37375         }
37376     },
37377
37378     // private
37379     processEvent : function(name, e){
37380         // does this fire select???
37381         //Roo.log('grid:processEvent '  + name);
37382         
37383         if (name != 'touchstart' ) {
37384             this.fireEvent(name, e);    
37385         }
37386         
37387         var t = e.getTarget();
37388         var v = this.view;
37389         var header = v.findHeaderIndex(t);
37390         if(header !== false){
37391             var ename = name == 'touchstart' ? 'click' : name;
37392              
37393             this.fireEvent("header" + ename, this, header, e);
37394         }else{
37395             var row = v.findRowIndex(t);
37396             var cell = v.findCellIndex(t);
37397             if (name == 'touchstart') {
37398                 // first touch is always a click.
37399                 // hopefull this happens after selection is updated.?
37400                 name = false;
37401                 
37402                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37403                     var cs = this.selModel.getSelectedCell();
37404                     if (row == cs[0] && cell == cs[1]){
37405                         name = 'dblclick';
37406                     }
37407                 }
37408                 if (typeof(this.selModel.getSelections) != 'undefined') {
37409                     var cs = this.selModel.getSelections();
37410                     var ds = this.dataSource;
37411                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37412                         name = 'dblclick';
37413                     }
37414                 }
37415                 if (!name) {
37416                     return;
37417                 }
37418             }
37419             
37420             
37421             if(row !== false){
37422                 this.fireEvent("row" + name, this, row, e);
37423                 if(cell !== false){
37424                     this.fireEvent("cell" + name, this, row, cell, e);
37425                 }
37426             }
37427         }
37428     },
37429
37430     // private
37431     onClick : function(e){
37432         this.processEvent("click", e);
37433     },
37434    // private
37435     onTouchStart : function(e){
37436         this.processEvent("touchstart", e);
37437     },
37438
37439     // private
37440     onContextMenu : function(e, t){
37441         this.processEvent("contextmenu", e);
37442     },
37443
37444     // private
37445     onDblClick : function(e){
37446         this.processEvent("dblclick", e);
37447     },
37448
37449     // private
37450     walkCells : function(row, col, step, fn, scope){
37451         var cm = this.colModel, clen = cm.getColumnCount();
37452         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37453         if(step < 0){
37454             if(col < 0){
37455                 row--;
37456                 first = false;
37457             }
37458             while(row >= 0){
37459                 if(!first){
37460                     col = clen-1;
37461                 }
37462                 first = false;
37463                 while(col >= 0){
37464                     if(fn.call(scope || this, row, col, cm) === true){
37465                         return [row, col];
37466                     }
37467                     col--;
37468                 }
37469                 row--;
37470             }
37471         } else {
37472             if(col >= clen){
37473                 row++;
37474                 first = false;
37475             }
37476             while(row < rlen){
37477                 if(!first){
37478                     col = 0;
37479                 }
37480                 first = false;
37481                 while(col < clen){
37482                     if(fn.call(scope || this, row, col, cm) === true){
37483                         return [row, col];
37484                     }
37485                     col++;
37486                 }
37487                 row++;
37488             }
37489         }
37490         return null;
37491     },
37492
37493     // private
37494     getSelections : function(){
37495         return this.selModel.getSelections();
37496     },
37497
37498     /**
37499      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37500      * but if manual update is required this method will initiate it.
37501      */
37502     autoSize : function(){
37503         if(this.rendered){
37504             this.view.layout();
37505             if(this.view.adjustForScroll){
37506                 this.view.adjustForScroll();
37507             }
37508         }
37509     },
37510
37511     /**
37512      * Returns the grid's underlying element.
37513      * @return {Element} The element
37514      */
37515     getGridEl : function(){
37516         return this.container;
37517     },
37518
37519     // private for compatibility, overridden by editor grid
37520     stopEditing : function(){},
37521
37522     /**
37523      * Returns the grid's SelectionModel.
37524      * @return {SelectionModel}
37525      */
37526     getSelectionModel : function(){
37527         if(!this.selModel){
37528             this.selModel = new Roo.grid.RowSelectionModel();
37529         }
37530         return this.selModel;
37531     },
37532
37533     /**
37534      * Returns the grid's DataSource.
37535      * @return {DataSource}
37536      */
37537     getDataSource : function(){
37538         return this.dataSource;
37539     },
37540
37541     /**
37542      * Returns the grid's ColumnModel.
37543      * @return {ColumnModel}
37544      */
37545     getColumnModel : function(){
37546         return this.colModel;
37547     },
37548
37549     /**
37550      * Returns the grid's GridView object.
37551      * @return {GridView}
37552      */
37553     getView : function(){
37554         if(!this.view){
37555             this.view = new Roo.grid.GridView(this.viewConfig);
37556             this.relayEvents(this.view, [
37557                 "beforerowremoved", "beforerowsinserted",
37558                 "beforerefresh", "rowremoved",
37559                 "rowsinserted", "rowupdated" ,"refresh"
37560             ]);
37561         }
37562         return this.view;
37563     },
37564     /**
37565      * Called to get grid's drag proxy text, by default returns this.ddText.
37566      * Override this to put something different in the dragged text.
37567      * @return {String}
37568      */
37569     getDragDropText : function(){
37570         var count = this.selModel.getCount();
37571         return String.format(this.ddText, count, count == 1 ? '' : 's');
37572     }
37573 });
37574 /*
37575  * Based on:
37576  * Ext JS Library 1.1.1
37577  * Copyright(c) 2006-2007, Ext JS, LLC.
37578  *
37579  * Originally Released Under LGPL - original licence link has changed is not relivant.
37580  *
37581  * Fork - LGPL
37582  * <script type="text/javascript">
37583  */
37584  /**
37585  * @class Roo.grid.AbstractGridView
37586  * @extends Roo.util.Observable
37587  * @abstract
37588  * Abstract base class for grid Views
37589  * @constructor
37590  */
37591 Roo.grid.AbstractGridView = function(){
37592         this.grid = null;
37593         
37594         this.events = {
37595             "beforerowremoved" : true,
37596             "beforerowsinserted" : true,
37597             "beforerefresh" : true,
37598             "rowremoved" : true,
37599             "rowsinserted" : true,
37600             "rowupdated" : true,
37601             "refresh" : true
37602         };
37603     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37604 };
37605
37606 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37607     rowClass : "x-grid-row",
37608     cellClass : "x-grid-cell",
37609     tdClass : "x-grid-td",
37610     hdClass : "x-grid-hd",
37611     splitClass : "x-grid-hd-split",
37612     
37613     init: function(grid){
37614         this.grid = grid;
37615                 var cid = this.grid.getGridEl().id;
37616         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37617         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37618         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37619         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37620         },
37621         
37622     getColumnRenderers : function(){
37623         var renderers = [];
37624         var cm = this.grid.colModel;
37625         var colCount = cm.getColumnCount();
37626         for(var i = 0; i < colCount; i++){
37627             renderers[i] = cm.getRenderer(i);
37628         }
37629         return renderers;
37630     },
37631     
37632     getColumnIds : function(){
37633         var ids = [];
37634         var cm = this.grid.colModel;
37635         var colCount = cm.getColumnCount();
37636         for(var i = 0; i < colCount; i++){
37637             ids[i] = cm.getColumnId(i);
37638         }
37639         return ids;
37640     },
37641     
37642     getDataIndexes : function(){
37643         if(!this.indexMap){
37644             this.indexMap = this.buildIndexMap();
37645         }
37646         return this.indexMap.colToData;
37647     },
37648     
37649     getColumnIndexByDataIndex : function(dataIndex){
37650         if(!this.indexMap){
37651             this.indexMap = this.buildIndexMap();
37652         }
37653         return this.indexMap.dataToCol[dataIndex];
37654     },
37655     
37656     /**
37657      * Set a css style for a column dynamically. 
37658      * @param {Number} colIndex The index of the column
37659      * @param {String} name The css property name
37660      * @param {String} value The css value
37661      */
37662     setCSSStyle : function(colIndex, name, value){
37663         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37664         Roo.util.CSS.updateRule(selector, name, value);
37665     },
37666     
37667     generateRules : function(cm){
37668         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37669         Roo.util.CSS.removeStyleSheet(rulesId);
37670         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37671             var cid = cm.getColumnId(i);
37672             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37673                          this.tdSelector, cid, " {\n}\n",
37674                          this.hdSelector, cid, " {\n}\n",
37675                          this.splitSelector, cid, " {\n}\n");
37676         }
37677         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37678     }
37679 });/*
37680  * Based on:
37681  * Ext JS Library 1.1.1
37682  * Copyright(c) 2006-2007, Ext JS, LLC.
37683  *
37684  * Originally Released Under LGPL - original licence link has changed is not relivant.
37685  *
37686  * Fork - LGPL
37687  * <script type="text/javascript">
37688  */
37689
37690 // private
37691 // This is a support class used internally by the Grid components
37692 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37693     this.grid = grid;
37694     this.view = grid.getView();
37695     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37696     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37697     if(hd2){
37698         this.setHandleElId(Roo.id(hd));
37699         this.setOuterHandleElId(Roo.id(hd2));
37700     }
37701     this.scroll = false;
37702 };
37703 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37704     maxDragWidth: 120,
37705     getDragData : function(e){
37706         var t = Roo.lib.Event.getTarget(e);
37707         var h = this.view.findHeaderCell(t);
37708         if(h){
37709             return {ddel: h.firstChild, header:h};
37710         }
37711         return false;
37712     },
37713
37714     onInitDrag : function(e){
37715         this.view.headersDisabled = true;
37716         var clone = this.dragData.ddel.cloneNode(true);
37717         clone.id = Roo.id();
37718         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37719         this.proxy.update(clone);
37720         return true;
37721     },
37722
37723     afterValidDrop : function(){
37724         var v = this.view;
37725         setTimeout(function(){
37726             v.headersDisabled = false;
37727         }, 50);
37728     },
37729
37730     afterInvalidDrop : function(){
37731         var v = this.view;
37732         setTimeout(function(){
37733             v.headersDisabled = false;
37734         }, 50);
37735     }
37736 });
37737 /*
37738  * Based on:
37739  * Ext JS Library 1.1.1
37740  * Copyright(c) 2006-2007, Ext JS, LLC.
37741  *
37742  * Originally Released Under LGPL - original licence link has changed is not relivant.
37743  *
37744  * Fork - LGPL
37745  * <script type="text/javascript">
37746  */
37747 // private
37748 // This is a support class used internally by the Grid components
37749 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37750     this.grid = grid;
37751     this.view = grid.getView();
37752     // split the proxies so they don't interfere with mouse events
37753     this.proxyTop = Roo.DomHelper.append(document.body, {
37754         cls:"col-move-top", html:"&#160;"
37755     }, true);
37756     this.proxyBottom = Roo.DomHelper.append(document.body, {
37757         cls:"col-move-bottom", html:"&#160;"
37758     }, true);
37759     this.proxyTop.hide = this.proxyBottom.hide = function(){
37760         this.setLeftTop(-100,-100);
37761         this.setStyle("visibility", "hidden");
37762     };
37763     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37764     // temporarily disabled
37765     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37766     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37767 };
37768 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37769     proxyOffsets : [-4, -9],
37770     fly: Roo.Element.fly,
37771
37772     getTargetFromEvent : function(e){
37773         var t = Roo.lib.Event.getTarget(e);
37774         var cindex = this.view.findCellIndex(t);
37775         if(cindex !== false){
37776             return this.view.getHeaderCell(cindex);
37777         }
37778         return null;
37779     },
37780
37781     nextVisible : function(h){
37782         var v = this.view, cm = this.grid.colModel;
37783         h = h.nextSibling;
37784         while(h){
37785             if(!cm.isHidden(v.getCellIndex(h))){
37786                 return h;
37787             }
37788             h = h.nextSibling;
37789         }
37790         return null;
37791     },
37792
37793     prevVisible : function(h){
37794         var v = this.view, cm = this.grid.colModel;
37795         h = h.prevSibling;
37796         while(h){
37797             if(!cm.isHidden(v.getCellIndex(h))){
37798                 return h;
37799             }
37800             h = h.prevSibling;
37801         }
37802         return null;
37803     },
37804
37805     positionIndicator : function(h, n, e){
37806         var x = Roo.lib.Event.getPageX(e);
37807         var r = Roo.lib.Dom.getRegion(n.firstChild);
37808         var px, pt, py = r.top + this.proxyOffsets[1];
37809         if((r.right - x) <= (r.right-r.left)/2){
37810             px = r.right+this.view.borderWidth;
37811             pt = "after";
37812         }else{
37813             px = r.left;
37814             pt = "before";
37815         }
37816         var oldIndex = this.view.getCellIndex(h);
37817         var newIndex = this.view.getCellIndex(n);
37818
37819         if(this.grid.colModel.isFixed(newIndex)){
37820             return false;
37821         }
37822
37823         var locked = this.grid.colModel.isLocked(newIndex);
37824
37825         if(pt == "after"){
37826             newIndex++;
37827         }
37828         if(oldIndex < newIndex){
37829             newIndex--;
37830         }
37831         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37832             return false;
37833         }
37834         px +=  this.proxyOffsets[0];
37835         this.proxyTop.setLeftTop(px, py);
37836         this.proxyTop.show();
37837         if(!this.bottomOffset){
37838             this.bottomOffset = this.view.mainHd.getHeight();
37839         }
37840         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37841         this.proxyBottom.show();
37842         return pt;
37843     },
37844
37845     onNodeEnter : function(n, dd, e, data){
37846         if(data.header != n){
37847             this.positionIndicator(data.header, n, e);
37848         }
37849     },
37850
37851     onNodeOver : function(n, dd, e, data){
37852         var result = false;
37853         if(data.header != n){
37854             result = this.positionIndicator(data.header, n, e);
37855         }
37856         if(!result){
37857             this.proxyTop.hide();
37858             this.proxyBottom.hide();
37859         }
37860         return result ? this.dropAllowed : this.dropNotAllowed;
37861     },
37862
37863     onNodeOut : function(n, dd, e, data){
37864         this.proxyTop.hide();
37865         this.proxyBottom.hide();
37866     },
37867
37868     onNodeDrop : function(n, dd, e, data){
37869         var h = data.header;
37870         if(h != n){
37871             var cm = this.grid.colModel;
37872             var x = Roo.lib.Event.getPageX(e);
37873             var r = Roo.lib.Dom.getRegion(n.firstChild);
37874             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37875             var oldIndex = this.view.getCellIndex(h);
37876             var newIndex = this.view.getCellIndex(n);
37877             var locked = cm.isLocked(newIndex);
37878             if(pt == "after"){
37879                 newIndex++;
37880             }
37881             if(oldIndex < newIndex){
37882                 newIndex--;
37883             }
37884             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37885                 return false;
37886             }
37887             cm.setLocked(oldIndex, locked, true);
37888             cm.moveColumn(oldIndex, newIndex);
37889             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37890             return true;
37891         }
37892         return false;
37893     }
37894 });
37895 /*
37896  * Based on:
37897  * Ext JS Library 1.1.1
37898  * Copyright(c) 2006-2007, Ext JS, LLC.
37899  *
37900  * Originally Released Under LGPL - original licence link has changed is not relivant.
37901  *
37902  * Fork - LGPL
37903  * <script type="text/javascript">
37904  */
37905   
37906 /**
37907  * @class Roo.grid.GridView
37908  * @extends Roo.util.Observable
37909  *
37910  * @constructor
37911  * @param {Object} config
37912  */
37913 Roo.grid.GridView = function(config){
37914     Roo.grid.GridView.superclass.constructor.call(this);
37915     this.el = null;
37916
37917     Roo.apply(this, config);
37918 };
37919
37920 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37921
37922     unselectable :  'unselectable="on"',
37923     unselectableCls :  'x-unselectable',
37924     
37925     
37926     rowClass : "x-grid-row",
37927
37928     cellClass : "x-grid-col",
37929
37930     tdClass : "x-grid-td",
37931
37932     hdClass : "x-grid-hd",
37933
37934     splitClass : "x-grid-split",
37935
37936     sortClasses : ["sort-asc", "sort-desc"],
37937
37938     enableMoveAnim : false,
37939
37940     hlColor: "C3DAF9",
37941
37942     dh : Roo.DomHelper,
37943
37944     fly : Roo.Element.fly,
37945
37946     css : Roo.util.CSS,
37947
37948     borderWidth: 1,
37949
37950     splitOffset: 3,
37951
37952     scrollIncrement : 22,
37953
37954     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37955
37956     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37957
37958     bind : function(ds, cm){
37959         if(this.ds){
37960             this.ds.un("load", this.onLoad, this);
37961             this.ds.un("datachanged", this.onDataChange, this);
37962             this.ds.un("add", this.onAdd, this);
37963             this.ds.un("remove", this.onRemove, this);
37964             this.ds.un("update", this.onUpdate, this);
37965             this.ds.un("clear", this.onClear, this);
37966         }
37967         if(ds){
37968             ds.on("load", this.onLoad, this);
37969             ds.on("datachanged", this.onDataChange, this);
37970             ds.on("add", this.onAdd, this);
37971             ds.on("remove", this.onRemove, this);
37972             ds.on("update", this.onUpdate, this);
37973             ds.on("clear", this.onClear, this);
37974         }
37975         this.ds = ds;
37976
37977         if(this.cm){
37978             this.cm.un("widthchange", this.onColWidthChange, this);
37979             this.cm.un("headerchange", this.onHeaderChange, this);
37980             this.cm.un("hiddenchange", this.onHiddenChange, this);
37981             this.cm.un("columnmoved", this.onColumnMove, this);
37982             this.cm.un("columnlockchange", this.onColumnLock, this);
37983         }
37984         if(cm){
37985             this.generateRules(cm);
37986             cm.on("widthchange", this.onColWidthChange, this);
37987             cm.on("headerchange", this.onHeaderChange, this);
37988             cm.on("hiddenchange", this.onHiddenChange, this);
37989             cm.on("columnmoved", this.onColumnMove, this);
37990             cm.on("columnlockchange", this.onColumnLock, this);
37991         }
37992         this.cm = cm;
37993     },
37994
37995     init: function(grid){
37996         Roo.grid.GridView.superclass.init.call(this, grid);
37997
37998         this.bind(grid.dataSource, grid.colModel);
37999
38000         grid.on("headerclick", this.handleHeaderClick, this);
38001
38002         if(grid.trackMouseOver){
38003             grid.on("mouseover", this.onRowOver, this);
38004             grid.on("mouseout", this.onRowOut, this);
38005         }
38006         grid.cancelTextSelection = function(){};
38007         this.gridId = grid.id;
38008
38009         var tpls = this.templates || {};
38010
38011         if(!tpls.master){
38012             tpls.master = new Roo.Template(
38013                '<div class="x-grid" hidefocus="true">',
38014                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
38015                   '<div class="x-grid-topbar"></div>',
38016                   '<div class="x-grid-scroller"><div></div></div>',
38017                   '<div class="x-grid-locked">',
38018                       '<div class="x-grid-header">{lockedHeader}</div>',
38019                       '<div class="x-grid-body">{lockedBody}</div>',
38020                   "</div>",
38021                   '<div class="x-grid-viewport">',
38022                       '<div class="x-grid-header">{header}</div>',
38023                       '<div class="x-grid-body">{body}</div>',
38024                   "</div>",
38025                   '<div class="x-grid-bottombar"></div>',
38026                  
38027                   '<div class="x-grid-resize-proxy">&#160;</div>',
38028                "</div>"
38029             );
38030             tpls.master.disableformats = true;
38031         }
38032
38033         if(!tpls.header){
38034             tpls.header = new Roo.Template(
38035                '<table border="0" cellspacing="0" cellpadding="0">',
38036                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
38037                "</table>{splits}"
38038             );
38039             tpls.header.disableformats = true;
38040         }
38041         tpls.header.compile();
38042
38043         if(!tpls.hcell){
38044             tpls.hcell = new Roo.Template(
38045                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
38046                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
38047                 "</div></td>"
38048              );
38049              tpls.hcell.disableFormats = true;
38050         }
38051         tpls.hcell.compile();
38052
38053         if(!tpls.hsplit){
38054             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
38055                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
38056             tpls.hsplit.disableFormats = true;
38057         }
38058         tpls.hsplit.compile();
38059
38060         if(!tpls.body){
38061             tpls.body = new Roo.Template(
38062                '<table border="0" cellspacing="0" cellpadding="0">',
38063                "<tbody>{rows}</tbody>",
38064                "</table>"
38065             );
38066             tpls.body.disableFormats = true;
38067         }
38068         tpls.body.compile();
38069
38070         if(!tpls.row){
38071             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
38072             tpls.row.disableFormats = true;
38073         }
38074         tpls.row.compile();
38075
38076         if(!tpls.cell){
38077             tpls.cell = new Roo.Template(
38078                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
38079                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
38080                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
38081                 "</td>"
38082             );
38083             tpls.cell.disableFormats = true;
38084         }
38085         tpls.cell.compile();
38086
38087         this.templates = tpls;
38088     },
38089
38090     // remap these for backwards compat
38091     onColWidthChange : function(){
38092         this.updateColumns.apply(this, arguments);
38093     },
38094     onHeaderChange : function(){
38095         this.updateHeaders.apply(this, arguments);
38096     }, 
38097     onHiddenChange : function(){
38098         this.handleHiddenChange.apply(this, arguments);
38099     },
38100     onColumnMove : function(){
38101         this.handleColumnMove.apply(this, arguments);
38102     },
38103     onColumnLock : function(){
38104         this.handleLockChange.apply(this, arguments);
38105     },
38106
38107     onDataChange : function(){
38108         this.refresh();
38109         this.updateHeaderSortState();
38110     },
38111
38112     onClear : function(){
38113         this.refresh();
38114     },
38115
38116     onUpdate : function(ds, record){
38117         this.refreshRow(record);
38118     },
38119
38120     refreshRow : function(record){
38121         var ds = this.ds, index;
38122         if(typeof record == 'number'){
38123             index = record;
38124             record = ds.getAt(index);
38125         }else{
38126             index = ds.indexOf(record);
38127         }
38128         this.insertRows(ds, index, index, true);
38129         this.onRemove(ds, record, index+1, true);
38130         this.syncRowHeights(index, index);
38131         this.layout();
38132         this.fireEvent("rowupdated", this, index, record);
38133     },
38134
38135     onAdd : function(ds, records, index){
38136         this.insertRows(ds, index, index + (records.length-1));
38137     },
38138
38139     onRemove : function(ds, record, index, isUpdate){
38140         if(isUpdate !== true){
38141             this.fireEvent("beforerowremoved", this, index, record);
38142         }
38143         var bt = this.getBodyTable(), lt = this.getLockedTable();
38144         if(bt.rows[index]){
38145             bt.firstChild.removeChild(bt.rows[index]);
38146         }
38147         if(lt.rows[index]){
38148             lt.firstChild.removeChild(lt.rows[index]);
38149         }
38150         if(isUpdate !== true){
38151             this.stripeRows(index);
38152             this.syncRowHeights(index, index);
38153             this.layout();
38154             this.fireEvent("rowremoved", this, index, record);
38155         }
38156     },
38157
38158     onLoad : function(){
38159         this.scrollToTop();
38160     },
38161
38162     /**
38163      * Scrolls the grid to the top
38164      */
38165     scrollToTop : function(){
38166         if(this.scroller){
38167             this.scroller.dom.scrollTop = 0;
38168             this.syncScroll();
38169         }
38170     },
38171
38172     /**
38173      * Gets a panel in the header of the grid that can be used for toolbars etc.
38174      * After modifying the contents of this panel a call to grid.autoSize() may be
38175      * required to register any changes in size.
38176      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38177      * @return Roo.Element
38178      */
38179     getHeaderPanel : function(doShow){
38180         if(doShow){
38181             this.headerPanel.show();
38182         }
38183         return this.headerPanel;
38184     },
38185
38186     /**
38187      * Gets a panel in the footer of the grid that can be used for toolbars etc.
38188      * After modifying the contents of this panel a call to grid.autoSize() may be
38189      * required to register any changes in size.
38190      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38191      * @return Roo.Element
38192      */
38193     getFooterPanel : function(doShow){
38194         if(doShow){
38195             this.footerPanel.show();
38196         }
38197         return this.footerPanel;
38198     },
38199
38200     initElements : function(){
38201         var E = Roo.Element;
38202         var el = this.grid.getGridEl().dom.firstChild;
38203         var cs = el.childNodes;
38204
38205         this.el = new E(el);
38206         
38207          this.focusEl = new E(el.firstChild);
38208         this.focusEl.swallowEvent("click", true);
38209         
38210         this.headerPanel = new E(cs[1]);
38211         this.headerPanel.enableDisplayMode("block");
38212
38213         this.scroller = new E(cs[2]);
38214         this.scrollSizer = new E(this.scroller.dom.firstChild);
38215
38216         this.lockedWrap = new E(cs[3]);
38217         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38218         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38219
38220         this.mainWrap = new E(cs[4]);
38221         this.mainHd = new E(this.mainWrap.dom.firstChild);
38222         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38223
38224         this.footerPanel = new E(cs[5]);
38225         this.footerPanel.enableDisplayMode("block");
38226
38227         this.resizeProxy = new E(cs[6]);
38228
38229         this.headerSelector = String.format(
38230            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38231            this.lockedHd.id, this.mainHd.id
38232         );
38233
38234         this.splitterSelector = String.format(
38235            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38236            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38237         );
38238     },
38239     idToCssName : function(s)
38240     {
38241         return s.replace(/[^a-z0-9]+/ig, '-');
38242     },
38243
38244     getHeaderCell : function(index){
38245         return Roo.DomQuery.select(this.headerSelector)[index];
38246     },
38247
38248     getHeaderCellMeasure : function(index){
38249         return this.getHeaderCell(index).firstChild;
38250     },
38251
38252     getHeaderCellText : function(index){
38253         return this.getHeaderCell(index).firstChild.firstChild;
38254     },
38255
38256     getLockedTable : function(){
38257         return this.lockedBody.dom.firstChild;
38258     },
38259
38260     getBodyTable : function(){
38261         return this.mainBody.dom.firstChild;
38262     },
38263
38264     getLockedRow : function(index){
38265         return this.getLockedTable().rows[index];
38266     },
38267
38268     getRow : function(index){
38269         return this.getBodyTable().rows[index];
38270     },
38271
38272     getRowComposite : function(index){
38273         if(!this.rowEl){
38274             this.rowEl = new Roo.CompositeElementLite();
38275         }
38276         var els = [], lrow, mrow;
38277         if(lrow = this.getLockedRow(index)){
38278             els.push(lrow);
38279         }
38280         if(mrow = this.getRow(index)){
38281             els.push(mrow);
38282         }
38283         this.rowEl.elements = els;
38284         return this.rowEl;
38285     },
38286     /**
38287      * Gets the 'td' of the cell
38288      * 
38289      * @param {Integer} rowIndex row to select
38290      * @param {Integer} colIndex column to select
38291      * 
38292      * @return {Object} 
38293      */
38294     getCell : function(rowIndex, colIndex){
38295         var locked = this.cm.getLockedCount();
38296         var source;
38297         if(colIndex < locked){
38298             source = this.lockedBody.dom.firstChild;
38299         }else{
38300             source = this.mainBody.dom.firstChild;
38301             colIndex -= locked;
38302         }
38303         return source.rows[rowIndex].childNodes[colIndex];
38304     },
38305
38306     getCellText : function(rowIndex, colIndex){
38307         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38308     },
38309
38310     getCellBox : function(cell){
38311         var b = this.fly(cell).getBox();
38312         if(Roo.isOpera){ // opera fails to report the Y
38313             b.y = cell.offsetTop + this.mainBody.getY();
38314         }
38315         return b;
38316     },
38317
38318     getCellIndex : function(cell){
38319         var id = String(cell.className).match(this.cellRE);
38320         if(id){
38321             return parseInt(id[1], 10);
38322         }
38323         return 0;
38324     },
38325
38326     findHeaderIndex : function(n){
38327         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38328         return r ? this.getCellIndex(r) : false;
38329     },
38330
38331     findHeaderCell : function(n){
38332         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38333         return r ? r : false;
38334     },
38335
38336     findRowIndex : function(n){
38337         if(!n){
38338             return false;
38339         }
38340         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38341         return r ? r.rowIndex : false;
38342     },
38343
38344     findCellIndex : function(node){
38345         var stop = this.el.dom;
38346         while(node && node != stop){
38347             if(this.findRE.test(node.className)){
38348                 return this.getCellIndex(node);
38349             }
38350             node = node.parentNode;
38351         }
38352         return false;
38353     },
38354
38355     getColumnId : function(index){
38356         return this.cm.getColumnId(index);
38357     },
38358
38359     getSplitters : function()
38360     {
38361         if(this.splitterSelector){
38362            return Roo.DomQuery.select(this.splitterSelector);
38363         }else{
38364             return null;
38365       }
38366     },
38367
38368     getSplitter : function(index){
38369         return this.getSplitters()[index];
38370     },
38371
38372     onRowOver : function(e, t){
38373         var row;
38374         if((row = this.findRowIndex(t)) !== false){
38375             this.getRowComposite(row).addClass("x-grid-row-over");
38376         }
38377     },
38378
38379     onRowOut : function(e, t){
38380         var row;
38381         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38382             this.getRowComposite(row).removeClass("x-grid-row-over");
38383         }
38384     },
38385
38386     renderHeaders : function(){
38387         var cm = this.cm;
38388         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38389         var cb = [], lb = [], sb = [], lsb = [], p = {};
38390         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38391             p.cellId = "x-grid-hd-0-" + i;
38392             p.splitId = "x-grid-csplit-0-" + i;
38393             p.id = cm.getColumnId(i);
38394             p.value = cm.getColumnHeader(i) || "";
38395             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38396             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38397             if(!cm.isLocked(i)){
38398                 cb[cb.length] = ct.apply(p);
38399                 sb[sb.length] = st.apply(p);
38400             }else{
38401                 lb[lb.length] = ct.apply(p);
38402                 lsb[lsb.length] = st.apply(p);
38403             }
38404         }
38405         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38406                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38407     },
38408
38409     updateHeaders : function(){
38410         var html = this.renderHeaders();
38411         this.lockedHd.update(html[0]);
38412         this.mainHd.update(html[1]);
38413     },
38414
38415     /**
38416      * Focuses the specified row.
38417      * @param {Number} row The row index
38418      */
38419     focusRow : function(row)
38420     {
38421         //Roo.log('GridView.focusRow');
38422         var x = this.scroller.dom.scrollLeft;
38423         this.focusCell(row, 0, false);
38424         this.scroller.dom.scrollLeft = x;
38425     },
38426
38427     /**
38428      * Focuses the specified cell.
38429      * @param {Number} row The row index
38430      * @param {Number} col The column index
38431      * @param {Boolean} hscroll false to disable horizontal scrolling
38432      */
38433     focusCell : function(row, col, hscroll)
38434     {
38435         //Roo.log('GridView.focusCell');
38436         var el = this.ensureVisible(row, col, hscroll);
38437         this.focusEl.alignTo(el, "tl-tl");
38438         if(Roo.isGecko){
38439             this.focusEl.focus();
38440         }else{
38441             this.focusEl.focus.defer(1, this.focusEl);
38442         }
38443     },
38444
38445     /**
38446      * Scrolls the specified cell into view
38447      * @param {Number} row The row index
38448      * @param {Number} col The column index
38449      * @param {Boolean} hscroll false to disable horizontal scrolling
38450      */
38451     ensureVisible : function(row, col, hscroll)
38452     {
38453         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38454         //return null; //disable for testing.
38455         if(typeof row != "number"){
38456             row = row.rowIndex;
38457         }
38458         if(row < 0 && row >= this.ds.getCount()){
38459             return  null;
38460         }
38461         col = (col !== undefined ? col : 0);
38462         var cm = this.grid.colModel;
38463         while(cm.isHidden(col)){
38464             col++;
38465         }
38466
38467         var el = this.getCell(row, col);
38468         if(!el){
38469             return null;
38470         }
38471         var c = this.scroller.dom;
38472
38473         var ctop = parseInt(el.offsetTop, 10);
38474         var cleft = parseInt(el.offsetLeft, 10);
38475         var cbot = ctop + el.offsetHeight;
38476         var cright = cleft + el.offsetWidth;
38477         
38478         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38479         var stop = parseInt(c.scrollTop, 10);
38480         var sleft = parseInt(c.scrollLeft, 10);
38481         var sbot = stop + ch;
38482         var sright = sleft + c.clientWidth;
38483         /*
38484         Roo.log('GridView.ensureVisible:' +
38485                 ' ctop:' + ctop +
38486                 ' c.clientHeight:' + c.clientHeight +
38487                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38488                 ' stop:' + stop +
38489                 ' cbot:' + cbot +
38490                 ' sbot:' + sbot +
38491                 ' ch:' + ch  
38492                 );
38493         */
38494         if(ctop < stop){
38495             c.scrollTop = ctop;
38496             //Roo.log("set scrolltop to ctop DISABLE?");
38497         }else if(cbot > sbot){
38498             //Roo.log("set scrolltop to cbot-ch");
38499             c.scrollTop = cbot-ch;
38500         }
38501         
38502         if(hscroll !== false){
38503             if(cleft < sleft){
38504                 c.scrollLeft = cleft;
38505             }else if(cright > sright){
38506                 c.scrollLeft = cright-c.clientWidth;
38507             }
38508         }
38509          
38510         return el;
38511     },
38512
38513     updateColumns : function(){
38514         this.grid.stopEditing();
38515         var cm = this.grid.colModel, colIds = this.getColumnIds();
38516         //var totalWidth = cm.getTotalWidth();
38517         var pos = 0;
38518         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38519             //if(cm.isHidden(i)) continue;
38520             var w = cm.getColumnWidth(i);
38521             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38522             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38523         }
38524         this.updateSplitters();
38525     },
38526
38527     generateRules : function(cm){
38528         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38529         Roo.util.CSS.removeStyleSheet(rulesId);
38530         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38531             var cid = cm.getColumnId(i);
38532             var align = '';
38533             if(cm.config[i].align){
38534                 align = 'text-align:'+cm.config[i].align+';';
38535             }
38536             var hidden = '';
38537             if(cm.isHidden(i)){
38538                 hidden = 'display:none;';
38539             }
38540             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38541             ruleBuf.push(
38542                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38543                     this.hdSelector, cid, " {\n", align, width, "}\n",
38544                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38545                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38546         }
38547         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38548     },
38549
38550     updateSplitters : function(){
38551         var cm = this.cm, s = this.getSplitters();
38552         if(s){ // splitters not created yet
38553             var pos = 0, locked = true;
38554             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38555                 if(cm.isHidden(i)) {
38556                     continue;
38557                 }
38558                 var w = cm.getColumnWidth(i); // make sure it's a number
38559                 if(!cm.isLocked(i) && locked){
38560                     pos = 0;
38561                     locked = false;
38562                 }
38563                 pos += w;
38564                 s[i].style.left = (pos-this.splitOffset) + "px";
38565             }
38566         }
38567     },
38568
38569     handleHiddenChange : function(colModel, colIndex, hidden){
38570         if(hidden){
38571             this.hideColumn(colIndex);
38572         }else{
38573             this.unhideColumn(colIndex);
38574         }
38575     },
38576
38577     hideColumn : function(colIndex){
38578         var cid = this.getColumnId(colIndex);
38579         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38580         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38581         if(Roo.isSafari){
38582             this.updateHeaders();
38583         }
38584         this.updateSplitters();
38585         this.layout();
38586     },
38587
38588     unhideColumn : function(colIndex){
38589         var cid = this.getColumnId(colIndex);
38590         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38591         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38592
38593         if(Roo.isSafari){
38594             this.updateHeaders();
38595         }
38596         this.updateSplitters();
38597         this.layout();
38598     },
38599
38600     insertRows : function(dm, firstRow, lastRow, isUpdate){
38601         if(firstRow == 0 && lastRow == dm.getCount()-1){
38602             this.refresh();
38603         }else{
38604             if(!isUpdate){
38605                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38606             }
38607             var s = this.getScrollState();
38608             var markup = this.renderRows(firstRow, lastRow);
38609             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38610             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38611             this.restoreScroll(s);
38612             if(!isUpdate){
38613                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38614                 this.syncRowHeights(firstRow, lastRow);
38615                 this.stripeRows(firstRow);
38616                 this.layout();
38617             }
38618         }
38619     },
38620
38621     bufferRows : function(markup, target, index){
38622         var before = null, trows = target.rows, tbody = target.tBodies[0];
38623         if(index < trows.length){
38624             before = trows[index];
38625         }
38626         var b = document.createElement("div");
38627         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38628         var rows = b.firstChild.rows;
38629         for(var i = 0, len = rows.length; i < len; i++){
38630             if(before){
38631                 tbody.insertBefore(rows[0], before);
38632             }else{
38633                 tbody.appendChild(rows[0]);
38634             }
38635         }
38636         b.innerHTML = "";
38637         b = null;
38638     },
38639
38640     deleteRows : function(dm, firstRow, lastRow){
38641         if(dm.getRowCount()<1){
38642             this.fireEvent("beforerefresh", this);
38643             this.mainBody.update("");
38644             this.lockedBody.update("");
38645             this.fireEvent("refresh", this);
38646         }else{
38647             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38648             var bt = this.getBodyTable();
38649             var tbody = bt.firstChild;
38650             var rows = bt.rows;
38651             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38652                 tbody.removeChild(rows[firstRow]);
38653             }
38654             this.stripeRows(firstRow);
38655             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38656         }
38657     },
38658
38659     updateRows : function(dataSource, firstRow, lastRow){
38660         var s = this.getScrollState();
38661         this.refresh();
38662         this.restoreScroll(s);
38663     },
38664
38665     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38666         if(!noRefresh){
38667            this.refresh();
38668         }
38669         this.updateHeaderSortState();
38670     },
38671
38672     getScrollState : function(){
38673         
38674         var sb = this.scroller.dom;
38675         return {left: sb.scrollLeft, top: sb.scrollTop};
38676     },
38677
38678     stripeRows : function(startRow){
38679         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38680             return;
38681         }
38682         startRow = startRow || 0;
38683         var rows = this.getBodyTable().rows;
38684         var lrows = this.getLockedTable().rows;
38685         var cls = ' x-grid-row-alt ';
38686         for(var i = startRow, len = rows.length; i < len; i++){
38687             var row = rows[i], lrow = lrows[i];
38688             var isAlt = ((i+1) % 2 == 0);
38689             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38690             if(isAlt == hasAlt){
38691                 continue;
38692             }
38693             if(isAlt){
38694                 row.className += " x-grid-row-alt";
38695             }else{
38696                 row.className = row.className.replace("x-grid-row-alt", "");
38697             }
38698             if(lrow){
38699                 lrow.className = row.className;
38700             }
38701         }
38702     },
38703
38704     restoreScroll : function(state){
38705         //Roo.log('GridView.restoreScroll');
38706         var sb = this.scroller.dom;
38707         sb.scrollLeft = state.left;
38708         sb.scrollTop = state.top;
38709         this.syncScroll();
38710     },
38711
38712     syncScroll : function(){
38713         //Roo.log('GridView.syncScroll');
38714         var sb = this.scroller.dom;
38715         var sh = this.mainHd.dom;
38716         var bs = this.mainBody.dom;
38717         var lv = this.lockedBody.dom;
38718         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38719         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38720     },
38721
38722     handleScroll : function(e){
38723         this.syncScroll();
38724         var sb = this.scroller.dom;
38725         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38726         e.stopEvent();
38727     },
38728
38729     handleWheel : function(e){
38730         var d = e.getWheelDelta();
38731         this.scroller.dom.scrollTop -= d*22;
38732         // set this here to prevent jumpy scrolling on large tables
38733         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38734         e.stopEvent();
38735     },
38736
38737     renderRows : function(startRow, endRow){
38738         // pull in all the crap needed to render rows
38739         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38740         var colCount = cm.getColumnCount();
38741
38742         if(ds.getCount() < 1){
38743             return ["", ""];
38744         }
38745
38746         // build a map for all the columns
38747         var cs = [];
38748         for(var i = 0; i < colCount; i++){
38749             var name = cm.getDataIndex(i);
38750             cs[i] = {
38751                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38752                 renderer : cm.getRenderer(i),
38753                 id : cm.getColumnId(i),
38754                 locked : cm.isLocked(i),
38755                 has_editor : cm.isCellEditable(i)
38756             };
38757         }
38758
38759         startRow = startRow || 0;
38760         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38761
38762         // records to render
38763         var rs = ds.getRange(startRow, endRow);
38764
38765         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38766     },
38767
38768     // As much as I hate to duplicate code, this was branched because FireFox really hates
38769     // [].join("") on strings. The performance difference was substantial enough to
38770     // branch this function
38771     doRender : Roo.isGecko ?
38772             function(cs, rs, ds, startRow, colCount, stripe){
38773                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38774                 // buffers
38775                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38776                 
38777                 var hasListener = this.grid.hasListener('rowclass');
38778                 var rowcfg = {};
38779                 for(var j = 0, len = rs.length; j < len; j++){
38780                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38781                     for(var i = 0; i < colCount; i++){
38782                         c = cs[i];
38783                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38784                         p.id = c.id;
38785                         p.css = p.attr = "";
38786                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38787                         if(p.value == undefined || p.value === "") {
38788                             p.value = "&#160;";
38789                         }
38790                         if(c.has_editor){
38791                             p.css += ' x-grid-editable-cell';
38792                         }
38793                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38794                             p.css +=  ' x-grid-dirty-cell';
38795                         }
38796                         var markup = ct.apply(p);
38797                         if(!c.locked){
38798                             cb+= markup;
38799                         }else{
38800                             lcb+= markup;
38801                         }
38802                     }
38803                     var alt = [];
38804                     if(stripe && ((rowIndex+1) % 2 == 0)){
38805                         alt.push("x-grid-row-alt")
38806                     }
38807                     if(r.dirty){
38808                         alt.push(  " x-grid-dirty-row");
38809                     }
38810                     rp.cells = lcb;
38811                     if(this.getRowClass){
38812                         alt.push(this.getRowClass(r, rowIndex));
38813                     }
38814                     if (hasListener) {
38815                         rowcfg = {
38816                              
38817                             record: r,
38818                             rowIndex : rowIndex,
38819                             rowClass : ''
38820                         };
38821                         this.grid.fireEvent('rowclass', this, rowcfg);
38822                         alt.push(rowcfg.rowClass);
38823                     }
38824                     rp.alt = alt.join(" ");
38825                     lbuf+= rt.apply(rp);
38826                     rp.cells = cb;
38827                     buf+=  rt.apply(rp);
38828                 }
38829                 return [lbuf, buf];
38830             } :
38831             function(cs, rs, ds, startRow, colCount, stripe){
38832                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38833                 // buffers
38834                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38835                 var hasListener = this.grid.hasListener('rowclass');
38836  
38837                 var rowcfg = {};
38838                 for(var j = 0, len = rs.length; j < len; j++){
38839                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38840                     for(var i = 0; i < colCount; i++){
38841                         c = cs[i];
38842                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38843                         p.id = c.id;
38844                         p.css = p.attr = "";
38845                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38846                         if(p.value == undefined || p.value === "") {
38847                             p.value = "&#160;";
38848                         }
38849                         //Roo.log(c);
38850                          if(c.has_editor){
38851                             p.css += ' x-grid-editable-cell';
38852                         }
38853                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38854                             p.css += ' x-grid-dirty-cell' 
38855                         }
38856                         
38857                         var markup = ct.apply(p);
38858                         if(!c.locked){
38859                             cb[cb.length] = markup;
38860                         }else{
38861                             lcb[lcb.length] = markup;
38862                         }
38863                     }
38864                     var alt = [];
38865                     if(stripe && ((rowIndex+1) % 2 == 0)){
38866                         alt.push( "x-grid-row-alt");
38867                     }
38868                     if(r.dirty){
38869                         alt.push(" x-grid-dirty-row");
38870                     }
38871                     rp.cells = lcb;
38872                     if(this.getRowClass){
38873                         alt.push( this.getRowClass(r, rowIndex));
38874                     }
38875                     if (hasListener) {
38876                         rowcfg = {
38877                              
38878                             record: r,
38879                             rowIndex : rowIndex,
38880                             rowClass : ''
38881                         };
38882                         this.grid.fireEvent('rowclass', this, rowcfg);
38883                         alt.push(rowcfg.rowClass);
38884                     }
38885                     
38886                     rp.alt = alt.join(" ");
38887                     rp.cells = lcb.join("");
38888                     lbuf[lbuf.length] = rt.apply(rp);
38889                     rp.cells = cb.join("");
38890                     buf[buf.length] =  rt.apply(rp);
38891                 }
38892                 return [lbuf.join(""), buf.join("")];
38893             },
38894
38895     renderBody : function(){
38896         var markup = this.renderRows();
38897         var bt = this.templates.body;
38898         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38899     },
38900
38901     /**
38902      * Refreshes the grid
38903      * @param {Boolean} headersToo
38904      */
38905     refresh : function(headersToo){
38906         this.fireEvent("beforerefresh", this);
38907         this.grid.stopEditing();
38908         var result = this.renderBody();
38909         this.lockedBody.update(result[0]);
38910         this.mainBody.update(result[1]);
38911         if(headersToo === true){
38912             this.updateHeaders();
38913             this.updateColumns();
38914             this.updateSplitters();
38915             this.updateHeaderSortState();
38916         }
38917         this.syncRowHeights();
38918         this.layout();
38919         this.fireEvent("refresh", this);
38920     },
38921
38922     handleColumnMove : function(cm, oldIndex, newIndex){
38923         this.indexMap = null;
38924         var s = this.getScrollState();
38925         this.refresh(true);
38926         this.restoreScroll(s);
38927         this.afterMove(newIndex);
38928     },
38929
38930     afterMove : function(colIndex){
38931         if(this.enableMoveAnim && Roo.enableFx){
38932             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38933         }
38934         // if multisort - fix sortOrder, and reload..
38935         if (this.grid.dataSource.multiSort) {
38936             // the we can call sort again..
38937             var dm = this.grid.dataSource;
38938             var cm = this.grid.colModel;
38939             var so = [];
38940             for(var i = 0; i < cm.config.length; i++ ) {
38941                 
38942                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38943                     continue; // dont' bother, it's not in sort list or being set.
38944                 }
38945                 
38946                 so.push(cm.config[i].dataIndex);
38947             };
38948             dm.sortOrder = so;
38949             dm.load(dm.lastOptions);
38950             
38951             
38952         }
38953         
38954     },
38955
38956     updateCell : function(dm, rowIndex, dataIndex){
38957         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38958         if(typeof colIndex == "undefined"){ // not present in grid
38959             return;
38960         }
38961         var cm = this.grid.colModel;
38962         var cell = this.getCell(rowIndex, colIndex);
38963         var cellText = this.getCellText(rowIndex, colIndex);
38964
38965         var p = {
38966             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38967             id : cm.getColumnId(colIndex),
38968             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38969         };
38970         var renderer = cm.getRenderer(colIndex);
38971         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38972         if(typeof val == "undefined" || val === "") {
38973             val = "&#160;";
38974         }
38975         cellText.innerHTML = val;
38976         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38977         this.syncRowHeights(rowIndex, rowIndex);
38978     },
38979
38980     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38981         var maxWidth = 0;
38982         if(this.grid.autoSizeHeaders){
38983             var h = this.getHeaderCellMeasure(colIndex);
38984             maxWidth = Math.max(maxWidth, h.scrollWidth);
38985         }
38986         var tb, index;
38987         if(this.cm.isLocked(colIndex)){
38988             tb = this.getLockedTable();
38989             index = colIndex;
38990         }else{
38991             tb = this.getBodyTable();
38992             index = colIndex - this.cm.getLockedCount();
38993         }
38994         if(tb && tb.rows){
38995             var rows = tb.rows;
38996             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38997             for(var i = 0; i < stopIndex; i++){
38998                 var cell = rows[i].childNodes[index].firstChild;
38999                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
39000             }
39001         }
39002         return maxWidth + /*margin for error in IE*/ 5;
39003     },
39004     /**
39005      * Autofit a column to its content.
39006      * @param {Number} colIndex
39007      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
39008      */
39009      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
39010          if(this.cm.isHidden(colIndex)){
39011              return; // can't calc a hidden column
39012          }
39013         if(forceMinSize){
39014             var cid = this.cm.getColumnId(colIndex);
39015             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
39016            if(this.grid.autoSizeHeaders){
39017                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
39018            }
39019         }
39020         var newWidth = this.calcColumnWidth(colIndex);
39021         this.cm.setColumnWidth(colIndex,
39022             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
39023         if(!suppressEvent){
39024             this.grid.fireEvent("columnresize", colIndex, newWidth);
39025         }
39026     },
39027
39028     /**
39029      * Autofits all columns to their content and then expands to fit any extra space in the grid
39030      */
39031      autoSizeColumns : function(){
39032         var cm = this.grid.colModel;
39033         var colCount = cm.getColumnCount();
39034         for(var i = 0; i < colCount; i++){
39035             this.autoSizeColumn(i, true, true);
39036         }
39037         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
39038             this.fitColumns();
39039         }else{
39040             this.updateColumns();
39041             this.layout();
39042         }
39043     },
39044
39045     /**
39046      * Autofits all columns to the grid's width proportionate with their current size
39047      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
39048      */
39049     fitColumns : function(reserveScrollSpace){
39050         var cm = this.grid.colModel;
39051         var colCount = cm.getColumnCount();
39052         var cols = [];
39053         var width = 0;
39054         var i, w;
39055         for (i = 0; i < colCount; i++){
39056             if(!cm.isHidden(i) && !cm.isFixed(i)){
39057                 w = cm.getColumnWidth(i);
39058                 cols.push(i);
39059                 cols.push(w);
39060                 width += w;
39061             }
39062         }
39063         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
39064         if(reserveScrollSpace){
39065             avail -= 17;
39066         }
39067         var frac = (avail - cm.getTotalWidth())/width;
39068         while (cols.length){
39069             w = cols.pop();
39070             i = cols.pop();
39071             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
39072         }
39073         this.updateColumns();
39074         this.layout();
39075     },
39076
39077     onRowSelect : function(rowIndex){
39078         var row = this.getRowComposite(rowIndex);
39079         row.addClass("x-grid-row-selected");
39080     },
39081
39082     onRowDeselect : function(rowIndex){
39083         var row = this.getRowComposite(rowIndex);
39084         row.removeClass("x-grid-row-selected");
39085     },
39086
39087     onCellSelect : function(row, col){
39088         var cell = this.getCell(row, col);
39089         if(cell){
39090             Roo.fly(cell).addClass("x-grid-cell-selected");
39091         }
39092     },
39093
39094     onCellDeselect : function(row, col){
39095         var cell = this.getCell(row, col);
39096         if(cell){
39097             Roo.fly(cell).removeClass("x-grid-cell-selected");
39098         }
39099     },
39100
39101     updateHeaderSortState : function(){
39102         
39103         // sort state can be single { field: xxx, direction : yyy}
39104         // or   { xxx=>ASC , yyy : DESC ..... }
39105         
39106         var mstate = {};
39107         if (!this.ds.multiSort) { 
39108             var state = this.ds.getSortState();
39109             if(!state){
39110                 return;
39111             }
39112             mstate[state.field] = state.direction;
39113             // FIXME... - this is not used here.. but might be elsewhere..
39114             this.sortState = state;
39115             
39116         } else {
39117             mstate = this.ds.sortToggle;
39118         }
39119         //remove existing sort classes..
39120         
39121         var sc = this.sortClasses;
39122         var hds = this.el.select(this.headerSelector).removeClass(sc);
39123         
39124         for(var f in mstate) {
39125         
39126             var sortColumn = this.cm.findColumnIndex(f);
39127             
39128             if(sortColumn != -1){
39129                 var sortDir = mstate[f];        
39130                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
39131             }
39132         }
39133         
39134          
39135         
39136     },
39137
39138
39139     handleHeaderClick : function(g, index,e){
39140         
39141         Roo.log("header click");
39142         
39143         if (Roo.isTouch) {
39144             // touch events on header are handled by context
39145             this.handleHdCtx(g,index,e);
39146             return;
39147         }
39148         
39149         
39150         if(this.headersDisabled){
39151             return;
39152         }
39153         var dm = g.dataSource, cm = g.colModel;
39154         if(!cm.isSortable(index)){
39155             return;
39156         }
39157         g.stopEditing();
39158         
39159         if (dm.multiSort) {
39160             // update the sortOrder
39161             var so = [];
39162             for(var i = 0; i < cm.config.length; i++ ) {
39163                 
39164                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39165                     continue; // dont' bother, it's not in sort list or being set.
39166                 }
39167                 
39168                 so.push(cm.config[i].dataIndex);
39169             };
39170             dm.sortOrder = so;
39171         }
39172         
39173         
39174         dm.sort(cm.getDataIndex(index));
39175     },
39176
39177
39178     destroy : function(){
39179         if(this.colMenu){
39180             this.colMenu.removeAll();
39181             Roo.menu.MenuMgr.unregister(this.colMenu);
39182             this.colMenu.getEl().remove();
39183             delete this.colMenu;
39184         }
39185         if(this.hmenu){
39186             this.hmenu.removeAll();
39187             Roo.menu.MenuMgr.unregister(this.hmenu);
39188             this.hmenu.getEl().remove();
39189             delete this.hmenu;
39190         }
39191         if(this.grid.enableColumnMove){
39192             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39193             if(dds){
39194                 for(var dd in dds){
39195                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
39196                         var elid = dds[dd].dragElId;
39197                         dds[dd].unreg();
39198                         Roo.get(elid).remove();
39199                     } else if(dds[dd].config.isTarget){
39200                         dds[dd].proxyTop.remove();
39201                         dds[dd].proxyBottom.remove();
39202                         dds[dd].unreg();
39203                     }
39204                     if(Roo.dd.DDM.locationCache[dd]){
39205                         delete Roo.dd.DDM.locationCache[dd];
39206                     }
39207                 }
39208                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39209             }
39210         }
39211         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39212         this.bind(null, null);
39213         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39214     },
39215
39216     handleLockChange : function(){
39217         this.refresh(true);
39218     },
39219
39220     onDenyColumnLock : function(){
39221
39222     },
39223
39224     onDenyColumnHide : function(){
39225
39226     },
39227
39228     handleHdMenuClick : function(item){
39229         var index = this.hdCtxIndex;
39230         var cm = this.cm, ds = this.ds;
39231         switch(item.id){
39232             case "asc":
39233                 ds.sort(cm.getDataIndex(index), "ASC");
39234                 break;
39235             case "desc":
39236                 ds.sort(cm.getDataIndex(index), "DESC");
39237                 break;
39238             case "lock":
39239                 var lc = cm.getLockedCount();
39240                 if(cm.getColumnCount(true) <= lc+1){
39241                     this.onDenyColumnLock();
39242                     return;
39243                 }
39244                 if(lc != index){
39245                     cm.setLocked(index, true, true);
39246                     cm.moveColumn(index, lc);
39247                     this.grid.fireEvent("columnmove", index, lc);
39248                 }else{
39249                     cm.setLocked(index, true);
39250                 }
39251             break;
39252             case "unlock":
39253                 var lc = cm.getLockedCount();
39254                 if((lc-1) != index){
39255                     cm.setLocked(index, false, true);
39256                     cm.moveColumn(index, lc-1);
39257                     this.grid.fireEvent("columnmove", index, lc-1);
39258                 }else{
39259                     cm.setLocked(index, false);
39260                 }
39261             break;
39262             case 'wider': // used to expand cols on touch..
39263             case 'narrow':
39264                 var cw = cm.getColumnWidth(index);
39265                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39266                 cw = Math.max(0, cw);
39267                 cw = Math.min(cw,4000);
39268                 cm.setColumnWidth(index, cw);
39269                 break;
39270                 
39271             default:
39272                 index = cm.getIndexById(item.id.substr(4));
39273                 if(index != -1){
39274                     if(item.checked && cm.getColumnCount(true) <= 1){
39275                         this.onDenyColumnHide();
39276                         return false;
39277                     }
39278                     cm.setHidden(index, item.checked);
39279                 }
39280         }
39281         return true;
39282     },
39283
39284     beforeColMenuShow : function(){
39285         var cm = this.cm,  colCount = cm.getColumnCount();
39286         this.colMenu.removeAll();
39287         
39288         var items = [];
39289         for(var i = 0; i < colCount; i++){
39290             items.push({
39291                 id: "col-"+cm.getColumnId(i),
39292                 text: cm.getColumnHeader(i),
39293                 checked: !cm.isHidden(i),
39294                 hideOnClick:false
39295             });
39296         }
39297         
39298         if (this.grid.sortColMenu) {
39299             items.sort(function(a,b) {
39300                 if (a.text == b.text) {
39301                     return 0;
39302                 }
39303                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39304             });
39305         }
39306         
39307         for(var i = 0; i < colCount; i++){
39308             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39309         }
39310     },
39311
39312     handleHdCtx : function(g, index, e){
39313         e.stopEvent();
39314         var hd = this.getHeaderCell(index);
39315         this.hdCtxIndex = index;
39316         var ms = this.hmenu.items, cm = this.cm;
39317         ms.get("asc").setDisabled(!cm.isSortable(index));
39318         ms.get("desc").setDisabled(!cm.isSortable(index));
39319         if(this.grid.enableColLock !== false){
39320             ms.get("lock").setDisabled(cm.isLocked(index));
39321             ms.get("unlock").setDisabled(!cm.isLocked(index));
39322         }
39323         this.hmenu.show(hd, "tl-bl");
39324     },
39325
39326     handleHdOver : function(e){
39327         var hd = this.findHeaderCell(e.getTarget());
39328         if(hd && !this.headersDisabled){
39329             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39330                this.fly(hd).addClass("x-grid-hd-over");
39331             }
39332         }
39333     },
39334
39335     handleHdOut : function(e){
39336         var hd = this.findHeaderCell(e.getTarget());
39337         if(hd){
39338             this.fly(hd).removeClass("x-grid-hd-over");
39339         }
39340     },
39341
39342     handleSplitDblClick : function(e, t){
39343         var i = this.getCellIndex(t);
39344         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39345             this.autoSizeColumn(i, true);
39346             this.layout();
39347         }
39348     },
39349
39350     render : function(){
39351
39352         var cm = this.cm;
39353         var colCount = cm.getColumnCount();
39354
39355         if(this.grid.monitorWindowResize === true){
39356             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39357         }
39358         var header = this.renderHeaders();
39359         var body = this.templates.body.apply({rows:""});
39360         var html = this.templates.master.apply({
39361             lockedBody: body,
39362             body: body,
39363             lockedHeader: header[0],
39364             header: header[1]
39365         });
39366
39367         //this.updateColumns();
39368
39369         this.grid.getGridEl().dom.innerHTML = html;
39370
39371         this.initElements();
39372         
39373         // a kludge to fix the random scolling effect in webkit
39374         this.el.on("scroll", function() {
39375             this.el.dom.scrollTop=0; // hopefully not recursive..
39376         },this);
39377
39378         this.scroller.on("scroll", this.handleScroll, this);
39379         this.lockedBody.on("mousewheel", this.handleWheel, this);
39380         this.mainBody.on("mousewheel", this.handleWheel, this);
39381
39382         this.mainHd.on("mouseover", this.handleHdOver, this);
39383         this.mainHd.on("mouseout", this.handleHdOut, this);
39384         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39385                 {delegate: "."+this.splitClass});
39386
39387         this.lockedHd.on("mouseover", this.handleHdOver, this);
39388         this.lockedHd.on("mouseout", this.handleHdOut, this);
39389         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39390                 {delegate: "."+this.splitClass});
39391
39392         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39393             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39394         }
39395
39396         this.updateSplitters();
39397
39398         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39399             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39400             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39401         }
39402
39403         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39404             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39405             this.hmenu.add(
39406                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39407                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39408             );
39409             if(this.grid.enableColLock !== false){
39410                 this.hmenu.add('-',
39411                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39412                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39413                 );
39414             }
39415             if (Roo.isTouch) {
39416                  this.hmenu.add('-',
39417                     {id:"wider", text: this.columnsWiderText},
39418                     {id:"narrow", text: this.columnsNarrowText }
39419                 );
39420                 
39421                  
39422             }
39423             
39424             if(this.grid.enableColumnHide !== false){
39425
39426                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39427                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39428                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39429
39430                 this.hmenu.add('-',
39431                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39432                 );
39433             }
39434             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39435
39436             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39437         }
39438
39439         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39440             this.dd = new Roo.grid.GridDragZone(this.grid, {
39441                 ddGroup : this.grid.ddGroup || 'GridDD'
39442             });
39443             
39444         }
39445
39446         /*
39447         for(var i = 0; i < colCount; i++){
39448             if(cm.isHidden(i)){
39449                 this.hideColumn(i);
39450             }
39451             if(cm.config[i].align){
39452                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39453                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39454             }
39455         }*/
39456         
39457         this.updateHeaderSortState();
39458
39459         this.beforeInitialResize();
39460         this.layout(true);
39461
39462         // two part rendering gives faster view to the user
39463         this.renderPhase2.defer(1, this);
39464     },
39465
39466     renderPhase2 : function(){
39467         // render the rows now
39468         this.refresh();
39469         if(this.grid.autoSizeColumns){
39470             this.autoSizeColumns();
39471         }
39472     },
39473
39474     beforeInitialResize : function(){
39475
39476     },
39477
39478     onColumnSplitterMoved : function(i, w){
39479         this.userResized = true;
39480         var cm = this.grid.colModel;
39481         cm.setColumnWidth(i, w, true);
39482         var cid = cm.getColumnId(i);
39483         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39484         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39485         this.updateSplitters();
39486         this.layout();
39487         this.grid.fireEvent("columnresize", i, w);
39488     },
39489
39490     syncRowHeights : function(startIndex, endIndex){
39491         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39492             startIndex = startIndex || 0;
39493             var mrows = this.getBodyTable().rows;
39494             var lrows = this.getLockedTable().rows;
39495             var len = mrows.length-1;
39496             endIndex = Math.min(endIndex || len, len);
39497             for(var i = startIndex; i <= endIndex; i++){
39498                 var m = mrows[i], l = lrows[i];
39499                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39500                 m.style.height = l.style.height = h + "px";
39501             }
39502         }
39503     },
39504
39505     layout : function(initialRender, is2ndPass)
39506     {
39507         var g = this.grid;
39508         var auto = g.autoHeight;
39509         var scrollOffset = 16;
39510         var c = g.getGridEl(), cm = this.cm,
39511                 expandCol = g.autoExpandColumn,
39512                 gv = this;
39513         //c.beginMeasure();
39514
39515         if(!c.dom.offsetWidth){ // display:none?
39516             if(initialRender){
39517                 this.lockedWrap.show();
39518                 this.mainWrap.show();
39519             }
39520             return;
39521         }
39522
39523         var hasLock = this.cm.isLocked(0);
39524
39525         var tbh = this.headerPanel.getHeight();
39526         var bbh = this.footerPanel.getHeight();
39527
39528         if(auto){
39529             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39530             var newHeight = ch + c.getBorderWidth("tb");
39531             if(g.maxHeight){
39532                 newHeight = Math.min(g.maxHeight, newHeight);
39533             }
39534             c.setHeight(newHeight);
39535         }
39536
39537         if(g.autoWidth){
39538             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39539         }
39540
39541         var s = this.scroller;
39542
39543         var csize = c.getSize(true);
39544
39545         this.el.setSize(csize.width, csize.height);
39546
39547         this.headerPanel.setWidth(csize.width);
39548         this.footerPanel.setWidth(csize.width);
39549
39550         var hdHeight = this.mainHd.getHeight();
39551         var vw = csize.width;
39552         var vh = csize.height - (tbh + bbh);
39553
39554         s.setSize(vw, vh);
39555
39556         var bt = this.getBodyTable();
39557         
39558         if(cm.getLockedCount() == cm.config.length){
39559             bt = this.getLockedTable();
39560         }
39561         
39562         var ltWidth = hasLock ?
39563                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39564
39565         var scrollHeight = bt.offsetHeight;
39566         var scrollWidth = ltWidth + bt.offsetWidth;
39567         var vscroll = false, hscroll = false;
39568
39569         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39570
39571         var lw = this.lockedWrap, mw = this.mainWrap;
39572         var lb = this.lockedBody, mb = this.mainBody;
39573
39574         setTimeout(function(){
39575             var t = s.dom.offsetTop;
39576             var w = s.dom.clientWidth,
39577                 h = s.dom.clientHeight;
39578
39579             lw.setTop(t);
39580             lw.setSize(ltWidth, h);
39581
39582             mw.setLeftTop(ltWidth, t);
39583             mw.setSize(w-ltWidth, h);
39584
39585             lb.setHeight(h-hdHeight);
39586             mb.setHeight(h-hdHeight);
39587
39588             if(is2ndPass !== true && !gv.userResized && expandCol){
39589                 // high speed resize without full column calculation
39590                 
39591                 var ci = cm.getIndexById(expandCol);
39592                 if (ci < 0) {
39593                     ci = cm.findColumnIndex(expandCol);
39594                 }
39595                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39596                 var expandId = cm.getColumnId(ci);
39597                 var  tw = cm.getTotalWidth(false);
39598                 var currentWidth = cm.getColumnWidth(ci);
39599                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39600                 if(currentWidth != cw){
39601                     cm.setColumnWidth(ci, cw, true);
39602                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39603                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39604                     gv.updateSplitters();
39605                     gv.layout(false, true);
39606                 }
39607             }
39608
39609             if(initialRender){
39610                 lw.show();
39611                 mw.show();
39612             }
39613             //c.endMeasure();
39614         }, 10);
39615     },
39616
39617     onWindowResize : function(){
39618         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39619             return;
39620         }
39621         this.layout();
39622     },
39623
39624     appendFooter : function(parentEl){
39625         return null;
39626     },
39627
39628     sortAscText : "Sort Ascending",
39629     sortDescText : "Sort Descending",
39630     lockText : "Lock Column",
39631     unlockText : "Unlock Column",
39632     columnsText : "Columns",
39633  
39634     columnsWiderText : "Wider",
39635     columnsNarrowText : "Thinner"
39636 });
39637
39638
39639 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39640     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39641     this.proxy.el.addClass('x-grid3-col-dd');
39642 };
39643
39644 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39645     handleMouseDown : function(e){
39646
39647     },
39648
39649     callHandleMouseDown : function(e){
39650         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39651     }
39652 });
39653 /*
39654  * Based on:
39655  * Ext JS Library 1.1.1
39656  * Copyright(c) 2006-2007, Ext JS, LLC.
39657  *
39658  * Originally Released Under LGPL - original licence link has changed is not relivant.
39659  *
39660  * Fork - LGPL
39661  * <script type="text/javascript">
39662  */
39663  /**
39664  * @extends Roo.dd.DDProxy
39665  * @class Roo.grid.SplitDragZone
39666  * Support for Column Header resizing
39667  * @constructor
39668  * @param {Object} config
39669  */
39670 // private
39671 // This is a support class used internally by the Grid components
39672 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39673     this.grid = grid;
39674     this.view = grid.getView();
39675     this.proxy = this.view.resizeProxy;
39676     Roo.grid.SplitDragZone.superclass.constructor.call(
39677         this,
39678         hd, // ID
39679         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39680         {  // CONFIG
39681             dragElId : Roo.id(this.proxy.dom),
39682             resizeFrame:false
39683         }
39684     );
39685     
39686     this.setHandleElId(Roo.id(hd));
39687     if (hd2 !== false) {
39688         this.setOuterHandleElId(Roo.id(hd2));
39689     }
39690     
39691     this.scroll = false;
39692 };
39693 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39694     fly: Roo.Element.fly,
39695
39696     b4StartDrag : function(x, y){
39697         this.view.headersDisabled = true;
39698         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39699                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39700         );
39701         this.proxy.setHeight(h);
39702         
39703         // for old system colWidth really stored the actual width?
39704         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39705         // which in reality did not work.. - it worked only for fixed sizes
39706         // for resizable we need to use actual sizes.
39707         var w = this.cm.getColumnWidth(this.cellIndex);
39708         if (!this.view.mainWrap) {
39709             // bootstrap.
39710             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39711         }
39712         
39713         
39714         
39715         // this was w-this.grid.minColumnWidth;
39716         // doesnt really make sense? - w = thie curren width or the rendered one?
39717         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39718         this.resetConstraints();
39719         this.setXConstraint(minw, 1000);
39720         this.setYConstraint(0, 0);
39721         this.minX = x - minw;
39722         this.maxX = x + 1000;
39723         this.startPos = x;
39724         if (!this.view.mainWrap) { // this is Bootstrap code..
39725             this.getDragEl().style.display='block';
39726         }
39727         
39728         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39729     },
39730
39731
39732     handleMouseDown : function(e){
39733         ev = Roo.EventObject.setEvent(e);
39734         var t = this.fly(ev.getTarget());
39735         if(t.hasClass("x-grid-split")){
39736             this.cellIndex = this.view.getCellIndex(t.dom);
39737             this.split = t.dom;
39738             this.cm = this.grid.colModel;
39739             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39740                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39741             }
39742         }
39743     },
39744
39745     endDrag : function(e){
39746         this.view.headersDisabled = false;
39747         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39748         var diff = endX - this.startPos;
39749         // 
39750         var w = this.cm.getColumnWidth(this.cellIndex);
39751         if (!this.view.mainWrap) {
39752             w = 0;
39753         }
39754         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39755     },
39756
39757     autoOffset : function(){
39758         this.setDelta(0,0);
39759     }
39760 });/*
39761  * Based on:
39762  * Ext JS Library 1.1.1
39763  * Copyright(c) 2006-2007, Ext JS, LLC.
39764  *
39765  * Originally Released Under LGPL - original licence link has changed is not relivant.
39766  *
39767  * Fork - LGPL
39768  * <script type="text/javascript">
39769  */
39770  
39771 // private
39772 // This is a support class used internally by the Grid components
39773 Roo.grid.GridDragZone = function(grid, config){
39774     this.view = grid.getView();
39775     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39776     if(this.view.lockedBody){
39777         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39778         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39779     }
39780     this.scroll = false;
39781     this.grid = grid;
39782     this.ddel = document.createElement('div');
39783     this.ddel.className = 'x-grid-dd-wrap';
39784 };
39785
39786 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39787     ddGroup : "GridDD",
39788
39789     getDragData : function(e){
39790         var t = Roo.lib.Event.getTarget(e);
39791         var rowIndex = this.view.findRowIndex(t);
39792         var sm = this.grid.selModel;
39793             
39794         //Roo.log(rowIndex);
39795         
39796         if (sm.getSelectedCell) {
39797             // cell selection..
39798             if (!sm.getSelectedCell()) {
39799                 return false;
39800             }
39801             if (rowIndex != sm.getSelectedCell()[0]) {
39802                 return false;
39803             }
39804         
39805         }
39806         if (sm.getSelections && sm.getSelections().length < 1) {
39807             return false;
39808         }
39809         
39810         
39811         // before it used to all dragging of unseleted... - now we dont do that.
39812         if(rowIndex !== false){
39813             
39814             // if editorgrid.. 
39815             
39816             
39817             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39818                
39819             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39820               //  
39821             //}
39822             if (e.hasModifier()){
39823                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39824             }
39825             
39826             Roo.log("getDragData");
39827             
39828             return {
39829                 grid: this.grid,
39830                 ddel: this.ddel,
39831                 rowIndex: rowIndex,
39832                 selections: sm.getSelections ? sm.getSelections() : (
39833                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39834             };
39835         }
39836         return false;
39837     },
39838     
39839     
39840     onInitDrag : function(e){
39841         var data = this.dragData;
39842         this.ddel.innerHTML = this.grid.getDragDropText();
39843         this.proxy.update(this.ddel);
39844         // fire start drag?
39845     },
39846
39847     afterRepair : function(){
39848         this.dragging = false;
39849     },
39850
39851     getRepairXY : function(e, data){
39852         return false;
39853     },
39854
39855     onEndDrag : function(data, e){
39856         // fire end drag?
39857     },
39858
39859     onValidDrop : function(dd, e, id){
39860         // fire drag drop?
39861         this.hideProxy();
39862     },
39863
39864     beforeInvalidDrop : function(e, id){
39865
39866     }
39867 });/*
39868  * Based on:
39869  * Ext JS Library 1.1.1
39870  * Copyright(c) 2006-2007, Ext JS, LLC.
39871  *
39872  * Originally Released Under LGPL - original licence link has changed is not relivant.
39873  *
39874  * Fork - LGPL
39875  * <script type="text/javascript">
39876  */
39877  
39878
39879 /**
39880  * @class Roo.grid.ColumnModel
39881  * @extends Roo.util.Observable
39882  * This is the default implementation of a ColumnModel used by the Grid. It defines
39883  * the columns in the grid.
39884  * <br>Usage:<br>
39885  <pre><code>
39886  var colModel = new Roo.grid.ColumnModel([
39887         {header: "Ticker", width: 60, sortable: true, locked: true},
39888         {header: "Company Name", width: 150, sortable: true},
39889         {header: "Market Cap.", width: 100, sortable: true},
39890         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39891         {header: "Employees", width: 100, sortable: true, resizable: false}
39892  ]);
39893  </code></pre>
39894  * <p>
39895  
39896  * The config options listed for this class are options which may appear in each
39897  * individual column definition.
39898  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39899  * @constructor
39900  * @param {Object} config An Array of column config objects. See this class's
39901  * config objects for details.
39902 */
39903 Roo.grid.ColumnModel = function(config){
39904         /**
39905      * The config passed into the constructor
39906      */
39907     this.config = []; //config;
39908     this.lookup = {};
39909
39910     // if no id, create one
39911     // if the column does not have a dataIndex mapping,
39912     // map it to the order it is in the config
39913     for(var i = 0, len = config.length; i < len; i++){
39914         this.addColumn(config[i]);
39915         
39916     }
39917
39918     /**
39919      * The width of columns which have no width specified (defaults to 100)
39920      * @type Number
39921      */
39922     this.defaultWidth = 100;
39923
39924     /**
39925      * Default sortable of columns which have no sortable specified (defaults to false)
39926      * @type Boolean
39927      */
39928     this.defaultSortable = false;
39929
39930     this.addEvents({
39931         /**
39932              * @event widthchange
39933              * Fires when the width of a column changes.
39934              * @param {ColumnModel} this
39935              * @param {Number} columnIndex The column index
39936              * @param {Number} newWidth The new width
39937              */
39938             "widthchange": true,
39939         /**
39940              * @event headerchange
39941              * Fires when the text of a header changes.
39942              * @param {ColumnModel} this
39943              * @param {Number} columnIndex The column index
39944              * @param {Number} newText The new header text
39945              */
39946             "headerchange": true,
39947         /**
39948              * @event hiddenchange
39949              * Fires when a column is hidden or "unhidden".
39950              * @param {ColumnModel} this
39951              * @param {Number} columnIndex The column index
39952              * @param {Boolean} hidden true if hidden, false otherwise
39953              */
39954             "hiddenchange": true,
39955             /**
39956          * @event columnmoved
39957          * Fires when a column is moved.
39958          * @param {ColumnModel} this
39959          * @param {Number} oldIndex
39960          * @param {Number} newIndex
39961          */
39962         "columnmoved" : true,
39963         /**
39964          * @event columlockchange
39965          * Fires when a column's locked state is changed
39966          * @param {ColumnModel} this
39967          * @param {Number} colIndex
39968          * @param {Boolean} locked true if locked
39969          */
39970         "columnlockchange" : true
39971     });
39972     Roo.grid.ColumnModel.superclass.constructor.call(this);
39973 };
39974 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39975     /**
39976      * @cfg {String} header [required] The header text to display in the Grid view.
39977      */
39978         /**
39979      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39980      */
39981         /**
39982      * @cfg {String} smHeader Header at Bootsrap Small width
39983      */
39984         /**
39985      * @cfg {String} mdHeader Header at Bootsrap Medium width
39986      */
39987         /**
39988      * @cfg {String} lgHeader Header at Bootsrap Large width
39989      */
39990         /**
39991      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39992      */
39993     /**
39994      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
39995      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39996      * specified, the column's index is used as an index into the Record's data Array.
39997      */
39998     /**
39999      * @cfg {Number} width  The initial width in pixels of the column. Using this
40000      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
40001      */
40002     /**
40003      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
40004      * Defaults to the value of the {@link #defaultSortable} property.
40005      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
40006      */
40007     /**
40008      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
40009      */
40010     /**
40011      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
40012      */
40013     /**
40014      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
40015      */
40016     /**
40017      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
40018      */
40019     /**
40020      * @cfg {Function} renderer A function used to generate HTML markup for a cell
40021      * given the cell's data value. See {@link #setRenderer}. If not specified, the
40022      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
40023      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
40024      */
40025        /**
40026      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
40027      */
40028     /**
40029      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
40030      */
40031     /**
40032      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
40033      */
40034     /**
40035      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
40036      */
40037     /**
40038      * @cfg {String} tooltip mouse over tooltip text
40039      */
40040     /**
40041      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
40042      */
40043     /**
40044      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
40045      */
40046     /**
40047      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
40048      */
40049     /**
40050      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
40051      */
40052         /**
40053      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
40054      */
40055     /**
40056      * Returns the id of the column at the specified index.
40057      * @param {Number} index The column index
40058      * @return {String} the id
40059      */
40060     getColumnId : function(index){
40061         return this.config[index].id;
40062     },
40063
40064     /**
40065      * Returns the column for a specified id.
40066      * @param {String} id The column id
40067      * @return {Object} the column
40068      */
40069     getColumnById : function(id){
40070         return this.lookup[id];
40071     },
40072
40073     
40074     /**
40075      * Returns the column Object for a specified dataIndex.
40076      * @param {String} dataIndex The column dataIndex
40077      * @return {Object|Boolean} the column or false if not found
40078      */
40079     getColumnByDataIndex: function(dataIndex){
40080         var index = this.findColumnIndex(dataIndex);
40081         return index > -1 ? this.config[index] : false;
40082     },
40083     
40084     /**
40085      * Returns the index for a specified column id.
40086      * @param {String} id The column id
40087      * @return {Number} the index, or -1 if not found
40088      */
40089     getIndexById : function(id){
40090         for(var i = 0, len = this.config.length; i < len; i++){
40091             if(this.config[i].id == id){
40092                 return i;
40093             }
40094         }
40095         return -1;
40096     },
40097     
40098     /**
40099      * Returns the index for a specified column dataIndex.
40100      * @param {String} dataIndex The column dataIndex
40101      * @return {Number} the index, or -1 if not found
40102      */
40103     
40104     findColumnIndex : function(dataIndex){
40105         for(var i = 0, len = this.config.length; i < len; i++){
40106             if(this.config[i].dataIndex == dataIndex){
40107                 return i;
40108             }
40109         }
40110         return -1;
40111     },
40112     
40113     
40114     moveColumn : function(oldIndex, newIndex){
40115         var c = this.config[oldIndex];
40116         this.config.splice(oldIndex, 1);
40117         this.config.splice(newIndex, 0, c);
40118         this.dataMap = null;
40119         this.fireEvent("columnmoved", this, oldIndex, newIndex);
40120     },
40121
40122     isLocked : function(colIndex){
40123         return this.config[colIndex].locked === true;
40124     },
40125
40126     setLocked : function(colIndex, value, suppressEvent){
40127         if(this.isLocked(colIndex) == value){
40128             return;
40129         }
40130         this.config[colIndex].locked = value;
40131         if(!suppressEvent){
40132             this.fireEvent("columnlockchange", this, colIndex, value);
40133         }
40134     },
40135
40136     getTotalLockedWidth : function(){
40137         var totalWidth = 0;
40138         for(var i = 0; i < this.config.length; i++){
40139             if(this.isLocked(i) && !this.isHidden(i)){
40140                 this.totalWidth += this.getColumnWidth(i);
40141             }
40142         }
40143         return totalWidth;
40144     },
40145
40146     getLockedCount : function(){
40147         for(var i = 0, len = this.config.length; i < len; i++){
40148             if(!this.isLocked(i)){
40149                 return i;
40150             }
40151         }
40152         
40153         return this.config.length;
40154     },
40155
40156     /**
40157      * Returns the number of columns.
40158      * @return {Number}
40159      */
40160     getColumnCount : function(visibleOnly){
40161         if(visibleOnly === true){
40162             var c = 0;
40163             for(var i = 0, len = this.config.length; i < len; i++){
40164                 if(!this.isHidden(i)){
40165                     c++;
40166                 }
40167             }
40168             return c;
40169         }
40170         return this.config.length;
40171     },
40172
40173     /**
40174      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40175      * @param {Function} fn
40176      * @param {Object} scope (optional)
40177      * @return {Array} result
40178      */
40179     getColumnsBy : function(fn, scope){
40180         var r = [];
40181         for(var i = 0, len = this.config.length; i < len; i++){
40182             var c = this.config[i];
40183             if(fn.call(scope||this, c, i) === true){
40184                 r[r.length] = c;
40185             }
40186         }
40187         return r;
40188     },
40189
40190     /**
40191      * Returns true if the specified column is sortable.
40192      * @param {Number} col The column index
40193      * @return {Boolean}
40194      */
40195     isSortable : function(col){
40196         if(typeof this.config[col].sortable == "undefined"){
40197             return this.defaultSortable;
40198         }
40199         return this.config[col].sortable;
40200     },
40201
40202     /**
40203      * Returns the rendering (formatting) function defined for the column.
40204      * @param {Number} col The column index.
40205      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40206      */
40207     getRenderer : function(col){
40208         if(!this.config[col].renderer){
40209             return Roo.grid.ColumnModel.defaultRenderer;
40210         }
40211         return this.config[col].renderer;
40212     },
40213
40214     /**
40215      * Sets the rendering (formatting) function for a column.
40216      * @param {Number} col The column index
40217      * @param {Function} fn The function to use to process the cell's raw data
40218      * to return HTML markup for the grid view. The render function is called with
40219      * the following parameters:<ul>
40220      * <li>Data value.</li>
40221      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40222      * <li>css A CSS style string to apply to the table cell.</li>
40223      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40224      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40225      * <li>Row index</li>
40226      * <li>Column index</li>
40227      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40228      */
40229     setRenderer : function(col, fn){
40230         this.config[col].renderer = fn;
40231     },
40232
40233     /**
40234      * Returns the width for the specified column.
40235      * @param {Number} col The column index
40236      * @param (optional) {String} gridSize bootstrap width size.
40237      * @return {Number}
40238      */
40239     getColumnWidth : function(col, gridSize)
40240         {
40241                 var cfg = this.config[col];
40242                 
40243                 if (typeof(gridSize) == 'undefined') {
40244                         return cfg.width * 1 || this.defaultWidth;
40245                 }
40246                 if (gridSize === false) { // if we set it..
40247                         return cfg.width || false;
40248                 }
40249                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40250                 
40251                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40252                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40253                                 continue;
40254                         }
40255                         return cfg[ sizes[i] ];
40256                 }
40257                 return 1;
40258                 
40259     },
40260
40261     /**
40262      * Sets the width for a column.
40263      * @param {Number} col The column index
40264      * @param {Number} width The new width
40265      */
40266     setColumnWidth : function(col, width, suppressEvent){
40267         this.config[col].width = width;
40268         this.totalWidth = null;
40269         if(!suppressEvent){
40270              this.fireEvent("widthchange", this, col, width);
40271         }
40272     },
40273
40274     /**
40275      * Returns the total width of all columns.
40276      * @param {Boolean} includeHidden True to include hidden column widths
40277      * @return {Number}
40278      */
40279     getTotalWidth : function(includeHidden){
40280         if(!this.totalWidth){
40281             this.totalWidth = 0;
40282             for(var i = 0, len = this.config.length; i < len; i++){
40283                 if(includeHidden || !this.isHidden(i)){
40284                     this.totalWidth += this.getColumnWidth(i);
40285                 }
40286             }
40287         }
40288         return this.totalWidth;
40289     },
40290
40291     /**
40292      * Returns the header for the specified column.
40293      * @param {Number} col The column index
40294      * @return {String}
40295      */
40296     getColumnHeader : function(col){
40297         return this.config[col].header;
40298     },
40299
40300     /**
40301      * Sets the header for a column.
40302      * @param {Number} col The column index
40303      * @param {String} header The new header
40304      */
40305     setColumnHeader : function(col, header){
40306         this.config[col].header = header;
40307         this.fireEvent("headerchange", this, col, header);
40308     },
40309
40310     /**
40311      * Returns the tooltip for the specified column.
40312      * @param {Number} col The column index
40313      * @return {String}
40314      */
40315     getColumnTooltip : function(col){
40316             return this.config[col].tooltip;
40317     },
40318     /**
40319      * Sets the tooltip for a column.
40320      * @param {Number} col The column index
40321      * @param {String} tooltip The new tooltip
40322      */
40323     setColumnTooltip : function(col, tooltip){
40324             this.config[col].tooltip = tooltip;
40325     },
40326
40327     /**
40328      * Returns the dataIndex for the specified column.
40329      * @param {Number} col The column index
40330      * @return {Number}
40331      */
40332     getDataIndex : function(col){
40333         return this.config[col].dataIndex;
40334     },
40335
40336     /**
40337      * Sets the dataIndex for a column.
40338      * @param {Number} col The column index
40339      * @param {Number} dataIndex The new dataIndex
40340      */
40341     setDataIndex : function(col, dataIndex){
40342         this.config[col].dataIndex = dataIndex;
40343     },
40344
40345     
40346     
40347     /**
40348      * Returns true if the cell is editable.
40349      * @param {Number} colIndex The column index
40350      * @param {Number} rowIndex The row index - this is nto actually used..?
40351      * @return {Boolean}
40352      */
40353     isCellEditable : function(colIndex, rowIndex){
40354         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40355     },
40356
40357     /**
40358      * Returns the editor defined for the cell/column.
40359      * return false or null to disable editing.
40360      * @param {Number} colIndex The column index
40361      * @param {Number} rowIndex The row index
40362      * @return {Object}
40363      */
40364     getCellEditor : function(colIndex, rowIndex){
40365         return this.config[colIndex].editor;
40366     },
40367
40368     /**
40369      * Sets if a column is editable.
40370      * @param {Number} col The column index
40371      * @param {Boolean} editable True if the column is editable
40372      */
40373     setEditable : function(col, editable){
40374         this.config[col].editable = editable;
40375     },
40376
40377
40378     /**
40379      * Returns true if the column is hidden.
40380      * @param {Number} colIndex The column index
40381      * @return {Boolean}
40382      */
40383     isHidden : function(colIndex){
40384         return this.config[colIndex].hidden;
40385     },
40386
40387
40388     /**
40389      * Returns true if the column width cannot be changed
40390      */
40391     isFixed : function(colIndex){
40392         return this.config[colIndex].fixed;
40393     },
40394
40395     /**
40396      * Returns true if the column can be resized
40397      * @return {Boolean}
40398      */
40399     isResizable : function(colIndex){
40400         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40401     },
40402     /**
40403      * Sets if a column is hidden.
40404      * @param {Number} colIndex The column index
40405      * @param {Boolean} hidden True if the column is hidden
40406      */
40407     setHidden : function(colIndex, hidden){
40408         this.config[colIndex].hidden = hidden;
40409         this.totalWidth = null;
40410         this.fireEvent("hiddenchange", this, colIndex, hidden);
40411     },
40412
40413     /**
40414      * Sets the editor for a column.
40415      * @param {Number} col The column index
40416      * @param {Object} editor The editor object
40417      */
40418     setEditor : function(col, editor){
40419         this.config[col].editor = editor;
40420     },
40421     /**
40422      * Add a column (experimental...) - defaults to adding to the end..
40423      * @param {Object} config 
40424     */
40425     addColumn : function(c)
40426     {
40427     
40428         var i = this.config.length;
40429         this.config[i] = c;
40430         
40431         if(typeof c.dataIndex == "undefined"){
40432             c.dataIndex = i;
40433         }
40434         if(typeof c.renderer == "string"){
40435             c.renderer = Roo.util.Format[c.renderer];
40436         }
40437         if(typeof c.id == "undefined"){
40438             c.id = Roo.id();
40439         }
40440         if(c.editor && c.editor.xtype){
40441             c.editor  = Roo.factory(c.editor, Roo.grid);
40442         }
40443         if(c.editor && c.editor.isFormField){
40444             c.editor = new Roo.grid.GridEditor(c.editor);
40445         }
40446         this.lookup[c.id] = c;
40447     }
40448     
40449 });
40450
40451 Roo.grid.ColumnModel.defaultRenderer = function(value)
40452 {
40453     if(typeof value == "object") {
40454         return value;
40455     }
40456         if(typeof value == "string" && value.length < 1){
40457             return "&#160;";
40458         }
40459     
40460         return String.format("{0}", value);
40461 };
40462
40463 // Alias for backwards compatibility
40464 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40465 /*
40466  * Based on:
40467  * Ext JS Library 1.1.1
40468  * Copyright(c) 2006-2007, Ext JS, LLC.
40469  *
40470  * Originally Released Under LGPL - original licence link has changed is not relivant.
40471  *
40472  * Fork - LGPL
40473  * <script type="text/javascript">
40474  */
40475
40476 /**
40477  * @class Roo.grid.AbstractSelectionModel
40478  * @extends Roo.util.Observable
40479  * @abstract
40480  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40481  * implemented by descendant classes.  This class should not be directly instantiated.
40482  * @constructor
40483  */
40484 Roo.grid.AbstractSelectionModel = function(){
40485     this.locked = false;
40486     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40487 };
40488
40489 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40490     /** @ignore Called by the grid automatically. Do not call directly. */
40491     init : function(grid){
40492         this.grid = grid;
40493         this.initEvents();
40494     },
40495
40496     /**
40497      * Locks the selections.
40498      */
40499     lock : function(){
40500         this.locked = true;
40501     },
40502
40503     /**
40504      * Unlocks the selections.
40505      */
40506     unlock : function(){
40507         this.locked = false;
40508     },
40509
40510     /**
40511      * Returns true if the selections are locked.
40512      * @return {Boolean}
40513      */
40514     isLocked : function(){
40515         return this.locked;
40516     }
40517 });/*
40518  * Based on:
40519  * Ext JS Library 1.1.1
40520  * Copyright(c) 2006-2007, Ext JS, LLC.
40521  *
40522  * Originally Released Under LGPL - original licence link has changed is not relivant.
40523  *
40524  * Fork - LGPL
40525  * <script type="text/javascript">
40526  */
40527 /**
40528  * @extends Roo.grid.AbstractSelectionModel
40529  * @class Roo.grid.RowSelectionModel
40530  * The default SelectionModel used by {@link Roo.grid.Grid}.
40531  * It supports multiple selections and keyboard selection/navigation. 
40532  * @constructor
40533  * @param {Object} config
40534  */
40535 Roo.grid.RowSelectionModel = function(config){
40536     Roo.apply(this, config);
40537     this.selections = new Roo.util.MixedCollection(false, function(o){
40538         return o.id;
40539     });
40540
40541     this.last = false;
40542     this.lastActive = false;
40543
40544     this.addEvents({
40545         /**
40546         * @event selectionchange
40547         * Fires when the selection changes
40548         * @param {SelectionModel} this
40549         */
40550        "selectionchange" : true,
40551        /**
40552         * @event afterselectionchange
40553         * Fires after the selection changes (eg. by key press or clicking)
40554         * @param {SelectionModel} this
40555         */
40556        "afterselectionchange" : true,
40557        /**
40558         * @event beforerowselect
40559         * Fires when a row is selected being selected, return false to cancel.
40560         * @param {SelectionModel} this
40561         * @param {Number} rowIndex The selected index
40562         * @param {Boolean} keepExisting False if other selections will be cleared
40563         */
40564        "beforerowselect" : true,
40565        /**
40566         * @event rowselect
40567         * Fires when a row is selected.
40568         * @param {SelectionModel} this
40569         * @param {Number} rowIndex The selected index
40570         * @param {Roo.data.Record} r The record
40571         */
40572        "rowselect" : true,
40573        /**
40574         * @event rowdeselect
40575         * Fires when a row is deselected.
40576         * @param {SelectionModel} this
40577         * @param {Number} rowIndex The selected index
40578         */
40579         "rowdeselect" : true
40580     });
40581     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40582     this.locked = false;
40583 };
40584
40585 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40586     /**
40587      * @cfg {Boolean} singleSelect
40588      * True to allow selection of only one row at a time (defaults to false)
40589      */
40590     singleSelect : false,
40591
40592     // private
40593     initEvents : function(){
40594
40595         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40596             this.grid.on("mousedown", this.handleMouseDown, this);
40597         }else{ // allow click to work like normal
40598             this.grid.on("rowclick", this.handleDragableRowClick, this);
40599         }
40600         // bootstrap does not have a view..
40601         var view = this.grid.view ? this.grid.view : this.grid;
40602         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40603             "up" : function(e){
40604                 if(!e.shiftKey){
40605                     this.selectPrevious(e.shiftKey);
40606                 }else if(this.last !== false && this.lastActive !== false){
40607                     var last = this.last;
40608                     this.selectRange(this.last,  this.lastActive-1);
40609                     view.focusRow(this.lastActive);
40610                     if(last !== false){
40611                         this.last = last;
40612                     }
40613                 }else{
40614                     this.selectFirstRow();
40615                 }
40616                 this.fireEvent("afterselectionchange", this);
40617             },
40618             "down" : function(e){
40619                 if(!e.shiftKey){
40620                     this.selectNext(e.shiftKey);
40621                 }else if(this.last !== false && this.lastActive !== false){
40622                     var last = this.last;
40623                     this.selectRange(this.last,  this.lastActive+1);
40624                     view.focusRow(this.lastActive);
40625                     if(last !== false){
40626                         this.last = last;
40627                     }
40628                 }else{
40629                     this.selectFirstRow();
40630                 }
40631                 this.fireEvent("afterselectionchange", this);
40632             },
40633             scope: this
40634         });
40635
40636          
40637         view.on("refresh", this.onRefresh, this);
40638         view.on("rowupdated", this.onRowUpdated, this);
40639         view.on("rowremoved", this.onRemove, this);
40640     },
40641
40642     // private
40643     onRefresh : function(){
40644         var ds = this.grid.ds, i, v = this.grid.view;
40645         var s = this.selections;
40646         s.each(function(r){
40647             if((i = ds.indexOfId(r.id)) != -1){
40648                 v.onRowSelect(i);
40649                 s.add(ds.getAt(i)); // updating the selection relate data
40650             }else{
40651                 s.remove(r);
40652             }
40653         });
40654     },
40655
40656     // private
40657     onRemove : function(v, index, r){
40658         this.selections.remove(r);
40659     },
40660
40661     // private
40662     onRowUpdated : function(v, index, r){
40663         if(this.isSelected(r)){
40664             v.onRowSelect(index);
40665         }
40666     },
40667
40668     /**
40669      * Select records.
40670      * @param {Array} records The records to select
40671      * @param {Boolean} keepExisting (optional) True to keep existing selections
40672      */
40673     selectRecords : function(records, keepExisting){
40674         if(!keepExisting){
40675             this.clearSelections();
40676         }
40677         var ds = this.grid.ds;
40678         for(var i = 0, len = records.length; i < len; i++){
40679             this.selectRow(ds.indexOf(records[i]), true);
40680         }
40681     },
40682
40683     /**
40684      * Gets the number of selected rows.
40685      * @return {Number}
40686      */
40687     getCount : function(){
40688         return this.selections.length;
40689     },
40690
40691     /**
40692      * Selects the first row in the grid.
40693      */
40694     selectFirstRow : function(){
40695         this.selectRow(0);
40696     },
40697
40698     /**
40699      * Select the last row.
40700      * @param {Boolean} keepExisting (optional) True to keep existing selections
40701      */
40702     selectLastRow : function(keepExisting){
40703         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40704     },
40705
40706     /**
40707      * Selects the row immediately following the last selected row.
40708      * @param {Boolean} keepExisting (optional) True to keep existing selections
40709      */
40710     selectNext : function(keepExisting){
40711         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40712             this.selectRow(this.last+1, keepExisting);
40713             var view = this.grid.view ? this.grid.view : this.grid;
40714             view.focusRow(this.last);
40715         }
40716     },
40717
40718     /**
40719      * Selects the row that precedes the last selected row.
40720      * @param {Boolean} keepExisting (optional) True to keep existing selections
40721      */
40722     selectPrevious : function(keepExisting){
40723         if(this.last){
40724             this.selectRow(this.last-1, keepExisting);
40725             var view = this.grid.view ? this.grid.view : this.grid;
40726             view.focusRow(this.last);
40727         }
40728     },
40729
40730     /**
40731      * Returns the selected records
40732      * @return {Array} Array of selected records
40733      */
40734     getSelections : function(){
40735         return [].concat(this.selections.items);
40736     },
40737
40738     /**
40739      * Returns the first selected record.
40740      * @return {Record}
40741      */
40742     getSelected : function(){
40743         return this.selections.itemAt(0);
40744     },
40745
40746
40747     /**
40748      * Clears all selections.
40749      */
40750     clearSelections : function(fast){
40751         if(this.locked) {
40752             return;
40753         }
40754         if(fast !== true){
40755             var ds = this.grid.ds;
40756             var s = this.selections;
40757             s.each(function(r){
40758                 this.deselectRow(ds.indexOfId(r.id));
40759             }, this);
40760             s.clear();
40761         }else{
40762             this.selections.clear();
40763         }
40764         this.last = false;
40765     },
40766
40767
40768     /**
40769      * Selects all rows.
40770      */
40771     selectAll : function(){
40772         if(this.locked) {
40773             return;
40774         }
40775         this.selections.clear();
40776         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40777             this.selectRow(i, true);
40778         }
40779     },
40780
40781     /**
40782      * Returns True if there is a selection.
40783      * @return {Boolean}
40784      */
40785     hasSelection : function(){
40786         return this.selections.length > 0;
40787     },
40788
40789     /**
40790      * Returns True if the specified row is selected.
40791      * @param {Number/Record} record The record or index of the record to check
40792      * @return {Boolean}
40793      */
40794     isSelected : function(index){
40795         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40796         return (r && this.selections.key(r.id) ? true : false);
40797     },
40798
40799     /**
40800      * Returns True if the specified record id is selected.
40801      * @param {String} id The id of record to check
40802      * @return {Boolean}
40803      */
40804     isIdSelected : function(id){
40805         return (this.selections.key(id) ? true : false);
40806     },
40807
40808     // private
40809     handleMouseDown : function(e, t)
40810     {
40811         var view = this.grid.view ? this.grid.view : this.grid;
40812         var rowIndex;
40813         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40814             return;
40815         };
40816         if(e.shiftKey && this.last !== false){
40817             var last = this.last;
40818             this.selectRange(last, rowIndex, e.ctrlKey);
40819             this.last = last; // reset the last
40820             view.focusRow(rowIndex);
40821         }else{
40822             var isSelected = this.isSelected(rowIndex);
40823             if(e.button !== 0 && isSelected){
40824                 view.focusRow(rowIndex);
40825             }else if(e.ctrlKey && isSelected){
40826                 this.deselectRow(rowIndex);
40827             }else if(!isSelected){
40828                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40829                 view.focusRow(rowIndex);
40830             }
40831         }
40832         this.fireEvent("afterselectionchange", this);
40833     },
40834     // private
40835     handleDragableRowClick :  function(grid, rowIndex, e) 
40836     {
40837         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40838             this.selectRow(rowIndex, false);
40839             var view = this.grid.view ? this.grid.view : this.grid;
40840             view.focusRow(rowIndex);
40841              this.fireEvent("afterselectionchange", this);
40842         }
40843     },
40844     
40845     /**
40846      * Selects multiple rows.
40847      * @param {Array} rows Array of the indexes of the row to select
40848      * @param {Boolean} keepExisting (optional) True to keep existing selections
40849      */
40850     selectRows : function(rows, keepExisting){
40851         if(!keepExisting){
40852             this.clearSelections();
40853         }
40854         for(var i = 0, len = rows.length; i < len; i++){
40855             this.selectRow(rows[i], true);
40856         }
40857     },
40858
40859     /**
40860      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40861      * @param {Number} startRow The index of the first row in the range
40862      * @param {Number} endRow The index of the last row in the range
40863      * @param {Boolean} keepExisting (optional) True to retain existing selections
40864      */
40865     selectRange : function(startRow, endRow, keepExisting){
40866         if(this.locked) {
40867             return;
40868         }
40869         if(!keepExisting){
40870             this.clearSelections();
40871         }
40872         if(startRow <= endRow){
40873             for(var i = startRow; i <= endRow; i++){
40874                 this.selectRow(i, true);
40875             }
40876         }else{
40877             for(var i = startRow; i >= endRow; i--){
40878                 this.selectRow(i, true);
40879             }
40880         }
40881     },
40882
40883     /**
40884      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40885      * @param {Number} startRow The index of the first row in the range
40886      * @param {Number} endRow The index of the last row in the range
40887      */
40888     deselectRange : function(startRow, endRow, preventViewNotify){
40889         if(this.locked) {
40890             return;
40891         }
40892         for(var i = startRow; i <= endRow; i++){
40893             this.deselectRow(i, preventViewNotify);
40894         }
40895     },
40896
40897     /**
40898      * Selects a row.
40899      * @param {Number} row The index of the row to select
40900      * @param {Boolean} keepExisting (optional) True to keep existing selections
40901      */
40902     selectRow : function(index, keepExisting, preventViewNotify){
40903         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40904             return;
40905         }
40906         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40907             if(!keepExisting || this.singleSelect){
40908                 this.clearSelections();
40909             }
40910             var r = this.grid.ds.getAt(index);
40911             this.selections.add(r);
40912             this.last = this.lastActive = index;
40913             if(!preventViewNotify){
40914                 var view = this.grid.view ? this.grid.view : this.grid;
40915                 view.onRowSelect(index);
40916             }
40917             this.fireEvent("rowselect", this, index, r);
40918             this.fireEvent("selectionchange", this);
40919         }
40920     },
40921
40922     /**
40923      * Deselects a row.
40924      * @param {Number} row The index of the row to deselect
40925      */
40926     deselectRow : function(index, preventViewNotify){
40927         if(this.locked) {
40928             return;
40929         }
40930         if(this.last == index){
40931             this.last = false;
40932         }
40933         if(this.lastActive == index){
40934             this.lastActive = false;
40935         }
40936         var r = this.grid.ds.getAt(index);
40937         this.selections.remove(r);
40938         if(!preventViewNotify){
40939             var view = this.grid.view ? this.grid.view : this.grid;
40940             view.onRowDeselect(index);
40941         }
40942         this.fireEvent("rowdeselect", this, index);
40943         this.fireEvent("selectionchange", this);
40944     },
40945
40946     // private
40947     restoreLast : function(){
40948         if(this._last){
40949             this.last = this._last;
40950         }
40951     },
40952
40953     // private
40954     acceptsNav : function(row, col, cm){
40955         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40956     },
40957
40958     // private
40959     onEditorKey : function(field, e){
40960         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40961         if(k == e.TAB){
40962             e.stopEvent();
40963             ed.completeEdit();
40964             if(e.shiftKey){
40965                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40966             }else{
40967                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40968             }
40969         }else if(k == e.ENTER && !e.ctrlKey){
40970             e.stopEvent();
40971             ed.completeEdit();
40972             if(e.shiftKey){
40973                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40974             }else{
40975                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40976             }
40977         }else if(k == e.ESC){
40978             ed.cancelEdit();
40979         }
40980         if(newCell){
40981             g.startEditing(newCell[0], newCell[1]);
40982         }
40983     }
40984 });/*
40985  * Based on:
40986  * Ext JS Library 1.1.1
40987  * Copyright(c) 2006-2007, Ext JS, LLC.
40988  *
40989  * Originally Released Under LGPL - original licence link has changed is not relivant.
40990  *
40991  * Fork - LGPL
40992  * <script type="text/javascript">
40993  */
40994 /**
40995  * @class Roo.grid.CellSelectionModel
40996  * @extends Roo.grid.AbstractSelectionModel
40997  * This class provides the basic implementation for cell selection in a grid.
40998  * @constructor
40999  * @param {Object} config The object containing the configuration of this model.
41000  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
41001  */
41002 Roo.grid.CellSelectionModel = function(config){
41003     Roo.apply(this, config);
41004
41005     this.selection = null;
41006
41007     this.addEvents({
41008         /**
41009              * @event beforerowselect
41010              * Fires before a cell is selected.
41011              * @param {SelectionModel} this
41012              * @param {Number} rowIndex The selected row index
41013              * @param {Number} colIndex The selected cell index
41014              */
41015             "beforecellselect" : true,
41016         /**
41017              * @event cellselect
41018              * Fires when a cell is selected.
41019              * @param {SelectionModel} this
41020              * @param {Number} rowIndex The selected row index
41021              * @param {Number} colIndex The selected cell index
41022              */
41023             "cellselect" : true,
41024         /**
41025              * @event selectionchange
41026              * Fires when the active selection changes.
41027              * @param {SelectionModel} this
41028              * @param {Object} selection null for no selection or an object (o) with two properties
41029                 <ul>
41030                 <li>o.record: the record object for the row the selection is in</li>
41031                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
41032                 </ul>
41033              */
41034             "selectionchange" : true,
41035         /**
41036              * @event tabend
41037              * Fires when the tab (or enter) was pressed on the last editable cell
41038              * You can use this to trigger add new row.
41039              * @param {SelectionModel} this
41040              */
41041             "tabend" : true,
41042          /**
41043              * @event beforeeditnext
41044              * Fires before the next editable sell is made active
41045              * You can use this to skip to another cell or fire the tabend
41046              *    if you set cell to false
41047              * @param {Object} eventdata object : { cell : [ row, col ] } 
41048              */
41049             "beforeeditnext" : true
41050     });
41051     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
41052 };
41053
41054 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
41055     
41056     enter_is_tab: false,
41057
41058     /** @ignore */
41059     initEvents : function(){
41060         this.grid.on("mousedown", this.handleMouseDown, this);
41061         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
41062         var view = this.grid.view;
41063         view.on("refresh", this.onViewChange, this);
41064         view.on("rowupdated", this.onRowUpdated, this);
41065         view.on("beforerowremoved", this.clearSelections, this);
41066         view.on("beforerowsinserted", this.clearSelections, this);
41067         if(this.grid.isEditor){
41068             this.grid.on("beforeedit", this.beforeEdit,  this);
41069         }
41070     },
41071
41072         //private
41073     beforeEdit : function(e){
41074         this.select(e.row, e.column, false, true, e.record);
41075     },
41076
41077         //private
41078     onRowUpdated : function(v, index, r){
41079         if(this.selection && this.selection.record == r){
41080             v.onCellSelect(index, this.selection.cell[1]);
41081         }
41082     },
41083
41084         //private
41085     onViewChange : function(){
41086         this.clearSelections(true);
41087     },
41088
41089         /**
41090          * Returns the currently selected cell,.
41091          * @return {Array} The selected cell (row, column) or null if none selected.
41092          */
41093     getSelectedCell : function(){
41094         return this.selection ? this.selection.cell : null;
41095     },
41096
41097     /**
41098      * Clears all selections.
41099      * @param {Boolean} true to prevent the gridview from being notified about the change.
41100      */
41101     clearSelections : function(preventNotify){
41102         var s = this.selection;
41103         if(s){
41104             if(preventNotify !== true){
41105                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
41106             }
41107             this.selection = null;
41108             this.fireEvent("selectionchange", this, null);
41109         }
41110     },
41111
41112     /**
41113      * Returns true if there is a selection.
41114      * @return {Boolean}
41115      */
41116     hasSelection : function(){
41117         return this.selection ? true : false;
41118     },
41119
41120     /** @ignore */
41121     handleMouseDown : function(e, t){
41122         var v = this.grid.getView();
41123         if(this.isLocked()){
41124             return;
41125         };
41126         var row = v.findRowIndex(t);
41127         var cell = v.findCellIndex(t);
41128         if(row !== false && cell !== false){
41129             this.select(row, cell);
41130         }
41131     },
41132
41133     /**
41134      * Selects a cell.
41135      * @param {Number} rowIndex
41136      * @param {Number} collIndex
41137      */
41138     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
41139         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
41140             this.clearSelections();
41141             r = r || this.grid.dataSource.getAt(rowIndex);
41142             this.selection = {
41143                 record : r,
41144                 cell : [rowIndex, colIndex]
41145             };
41146             if(!preventViewNotify){
41147                 var v = this.grid.getView();
41148                 v.onCellSelect(rowIndex, colIndex);
41149                 if(preventFocus !== true){
41150                     v.focusCell(rowIndex, colIndex);
41151                 }
41152             }
41153             this.fireEvent("cellselect", this, rowIndex, colIndex);
41154             this.fireEvent("selectionchange", this, this.selection);
41155         }
41156     },
41157
41158         //private
41159     isSelectable : function(rowIndex, colIndex, cm){
41160         return !cm.isHidden(colIndex);
41161     },
41162
41163     /** @ignore */
41164     handleKeyDown : function(e){
41165         //Roo.log('Cell Sel Model handleKeyDown');
41166         if(!e.isNavKeyPress()){
41167             return;
41168         }
41169         var g = this.grid, s = this.selection;
41170         if(!s){
41171             e.stopEvent();
41172             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
41173             if(cell){
41174                 this.select(cell[0], cell[1]);
41175             }
41176             return;
41177         }
41178         var sm = this;
41179         var walk = function(row, col, step){
41180             return g.walkCells(row, col, step, sm.isSelectable,  sm);
41181         };
41182         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41183         var newCell;
41184
41185       
41186
41187         switch(k){
41188             case e.TAB:
41189                 // handled by onEditorKey
41190                 if (g.isEditor && g.editing) {
41191                     return;
41192                 }
41193                 if(e.shiftKey) {
41194                     newCell = walk(r, c-1, -1);
41195                 } else {
41196                     newCell = walk(r, c+1, 1);
41197                 }
41198                 break;
41199             
41200             case e.DOWN:
41201                newCell = walk(r+1, c, 1);
41202                 break;
41203             
41204             case e.UP:
41205                 newCell = walk(r-1, c, -1);
41206                 break;
41207             
41208             case e.RIGHT:
41209                 newCell = walk(r, c+1, 1);
41210                 break;
41211             
41212             case e.LEFT:
41213                 newCell = walk(r, c-1, -1);
41214                 break;
41215             
41216             case e.ENTER:
41217                 
41218                 if(g.isEditor && !g.editing){
41219                    g.startEditing(r, c);
41220                    e.stopEvent();
41221                    return;
41222                 }
41223                 
41224                 
41225              break;
41226         };
41227         if(newCell){
41228             this.select(newCell[0], newCell[1]);
41229             e.stopEvent();
41230             
41231         }
41232     },
41233
41234     acceptsNav : function(row, col, cm){
41235         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41236     },
41237     /**
41238      * Selects a cell.
41239      * @param {Number} field (not used) - as it's normally used as a listener
41240      * @param {Number} e - event - fake it by using
41241      *
41242      * var e = Roo.EventObjectImpl.prototype;
41243      * e.keyCode = e.TAB
41244      *
41245      * 
41246      */
41247     onEditorKey : function(field, e){
41248         
41249         var k = e.getKey(),
41250             newCell,
41251             g = this.grid,
41252             ed = g.activeEditor,
41253             forward = false;
41254         ///Roo.log('onEditorKey' + k);
41255         
41256         
41257         if (this.enter_is_tab && k == e.ENTER) {
41258             k = e.TAB;
41259         }
41260         
41261         if(k == e.TAB){
41262             if(e.shiftKey){
41263                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41264             }else{
41265                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41266                 forward = true;
41267             }
41268             
41269             e.stopEvent();
41270             
41271         } else if(k == e.ENTER &&  !e.ctrlKey){
41272             ed.completeEdit();
41273             e.stopEvent();
41274             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41275         
41276                 } else if(k == e.ESC){
41277             ed.cancelEdit();
41278         }
41279                 
41280         if (newCell) {
41281             var ecall = { cell : newCell, forward : forward };
41282             this.fireEvent('beforeeditnext', ecall );
41283             newCell = ecall.cell;
41284                         forward = ecall.forward;
41285         }
41286                 
41287         if(newCell){
41288             //Roo.log('next cell after edit');
41289             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41290         } else if (forward) {
41291             // tabbed past last
41292             this.fireEvent.defer(100, this, ['tabend',this]);
41293         }
41294     }
41295 });/*
41296  * Based on:
41297  * Ext JS Library 1.1.1
41298  * Copyright(c) 2006-2007, Ext JS, LLC.
41299  *
41300  * Originally Released Under LGPL - original licence link has changed is not relivant.
41301  *
41302  * Fork - LGPL
41303  * <script type="text/javascript">
41304  */
41305  
41306 /**
41307  * @class Roo.grid.EditorGrid
41308  * @extends Roo.grid.Grid
41309  * Class for creating and editable grid.
41310  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41311  * The container MUST have some type of size defined for the grid to fill. The container will be 
41312  * automatically set to position relative if it isn't already.
41313  * @param {Object} dataSource The data model to bind to
41314  * @param {Object} colModel The column model with info about this grid's columns
41315  */
41316 Roo.grid.EditorGrid = function(container, config){
41317     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41318     this.getGridEl().addClass("xedit-grid");
41319
41320     if(!this.selModel){
41321         this.selModel = new Roo.grid.CellSelectionModel();
41322     }
41323
41324     this.activeEditor = null;
41325
41326         this.addEvents({
41327             /**
41328              * @event beforeedit
41329              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41330              * <ul style="padding:5px;padding-left:16px;">
41331              * <li>grid - This grid</li>
41332              * <li>record - The record being edited</li>
41333              * <li>field - The field name being edited</li>
41334              * <li>value - The value for the field being edited.</li>
41335              * <li>row - The grid row index</li>
41336              * <li>column - The grid column index</li>
41337              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41338              * </ul>
41339              * @param {Object} e An edit event (see above for description)
41340              */
41341             "beforeedit" : true,
41342             /**
41343              * @event afteredit
41344              * Fires after a cell is edited. <br />
41345              * <ul style="padding:5px;padding-left:16px;">
41346              * <li>grid - This grid</li>
41347              * <li>record - The record being edited</li>
41348              * <li>field - The field name being edited</li>
41349              * <li>value - The value being set</li>
41350              * <li>originalValue - The original value for the field, before the edit.</li>
41351              * <li>row - The grid row index</li>
41352              * <li>column - The grid column index</li>
41353              * </ul>
41354              * @param {Object} e An edit event (see above for description)
41355              */
41356             "afteredit" : true,
41357             /**
41358              * @event validateedit
41359              * Fires after a cell is edited, but before the value is set in the record. 
41360          * You can use this to modify the value being set in the field, Return false
41361              * to cancel the change. The edit event object has the following properties <br />
41362              * <ul style="padding:5px;padding-left:16px;">
41363          * <li>editor - This editor</li>
41364              * <li>grid - This grid</li>
41365              * <li>record - The record being edited</li>
41366              * <li>field - The field name being edited</li>
41367              * <li>value - The value being set</li>
41368              * <li>originalValue - The original value for the field, before the edit.</li>
41369              * <li>row - The grid row index</li>
41370              * <li>column - The grid column index</li>
41371              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41372              * </ul>
41373              * @param {Object} e An edit event (see above for description)
41374              */
41375             "validateedit" : true
41376         });
41377     this.on("bodyscroll", this.stopEditing,  this);
41378     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41379 };
41380
41381 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41382     /**
41383      * @cfg {Number} clicksToEdit
41384      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41385      */
41386     clicksToEdit: 2,
41387
41388     // private
41389     isEditor : true,
41390     // private
41391     trackMouseOver: false, // causes very odd FF errors
41392
41393     onCellDblClick : function(g, row, col){
41394         this.startEditing(row, col);
41395     },
41396
41397     onEditComplete : function(ed, value, startValue){
41398         this.editing = false;
41399         this.activeEditor = null;
41400         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41401         var r = ed.record;
41402         var field = this.colModel.getDataIndex(ed.col);
41403         var e = {
41404             grid: this,
41405             record: r,
41406             field: field,
41407             originalValue: startValue,
41408             value: value,
41409             row: ed.row,
41410             column: ed.col,
41411             cancel:false,
41412             editor: ed
41413         };
41414         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41415         cell.show();
41416           
41417         if(String(value) !== String(startValue)){
41418             
41419             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41420                 r.set(field, e.value);
41421                 // if we are dealing with a combo box..
41422                 // then we also set the 'name' colum to be the displayField
41423                 if (ed.field.displayField && ed.field.name) {
41424                     r.set(ed.field.name, ed.field.el.dom.value);
41425                 }
41426                 
41427                 delete e.cancel; //?? why!!!
41428                 this.fireEvent("afteredit", e);
41429             }
41430         } else {
41431             this.fireEvent("afteredit", e); // always fire it!
41432         }
41433         this.view.focusCell(ed.row, ed.col);
41434     },
41435
41436     /**
41437      * Starts editing the specified for the specified row/column
41438      * @param {Number} rowIndex
41439      * @param {Number} colIndex
41440      */
41441     startEditing : function(row, col){
41442         this.stopEditing();
41443         if(this.colModel.isCellEditable(col, row)){
41444             this.view.ensureVisible(row, col, true);
41445           
41446             var r = this.dataSource.getAt(row);
41447             var field = this.colModel.getDataIndex(col);
41448             var cell = Roo.get(this.view.getCell(row,col));
41449             var e = {
41450                 grid: this,
41451                 record: r,
41452                 field: field,
41453                 value: r.data[field],
41454                 row: row,
41455                 column: col,
41456                 cancel:false 
41457             };
41458             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41459                 this.editing = true;
41460                 var ed = this.colModel.getCellEditor(col, row);
41461                 
41462                 if (!ed) {
41463                     return;
41464                 }
41465                 if(!ed.rendered){
41466                     ed.render(ed.parentEl || document.body);
41467                 }
41468                 ed.field.reset();
41469                
41470                 cell.hide();
41471                 
41472                 (function(){ // complex but required for focus issues in safari, ie and opera
41473                     ed.row = row;
41474                     ed.col = col;
41475                     ed.record = r;
41476                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41477                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41478                     this.activeEditor = ed;
41479                     var v = r.data[field];
41480                     ed.startEdit(this.view.getCell(row, col), v);
41481                     // combo's with 'displayField and name set
41482                     if (ed.field.displayField && ed.field.name) {
41483                         ed.field.el.dom.value = r.data[ed.field.name];
41484                     }
41485                     
41486                     
41487                 }).defer(50, this);
41488             }
41489         }
41490     },
41491         
41492     /**
41493      * Stops any active editing
41494      */
41495     stopEditing : function(){
41496         if(this.activeEditor){
41497             this.activeEditor.completeEdit();
41498         }
41499         this.activeEditor = null;
41500     },
41501         
41502          /**
41503      * Called to get grid's drag proxy text, by default returns this.ddText.
41504      * @return {String}
41505      */
41506     getDragDropText : function(){
41507         var count = this.selModel.getSelectedCell() ? 1 : 0;
41508         return String.format(this.ddText, count, count == 1 ? '' : 's');
41509     }
41510         
41511 });/*
41512  * Based on:
41513  * Ext JS Library 1.1.1
41514  * Copyright(c) 2006-2007, Ext JS, LLC.
41515  *
41516  * Originally Released Under LGPL - original licence link has changed is not relivant.
41517  *
41518  * Fork - LGPL
41519  * <script type="text/javascript">
41520  */
41521
41522 // private - not really -- you end up using it !
41523 // This is a support class used internally by the Grid components
41524
41525 /**
41526  * @class Roo.grid.GridEditor
41527  * @extends Roo.Editor
41528  * Class for creating and editable grid elements.
41529  * @param {Object} config any settings (must include field)
41530  */
41531 Roo.grid.GridEditor = function(field, config){
41532     if (!config && field.field) {
41533         config = field;
41534         field = Roo.factory(config.field, Roo.form);
41535     }
41536     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41537     field.monitorTab = false;
41538 };
41539
41540 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41541     
41542     /**
41543      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41544      */
41545     
41546     alignment: "tl-tl",
41547     autoSize: "width",
41548     hideEl : false,
41549     cls: "x-small-editor x-grid-editor",
41550     shim:false,
41551     shadow:"frame"
41552 });/*
41553  * Based on:
41554  * Ext JS Library 1.1.1
41555  * Copyright(c) 2006-2007, Ext JS, LLC.
41556  *
41557  * Originally Released Under LGPL - original licence link has changed is not relivant.
41558  *
41559  * Fork - LGPL
41560  * <script type="text/javascript">
41561  */
41562   
41563
41564   
41565 Roo.grid.PropertyRecord = Roo.data.Record.create([
41566     {name:'name',type:'string'},  'value'
41567 ]);
41568
41569
41570 Roo.grid.PropertyStore = function(grid, source){
41571     this.grid = grid;
41572     this.store = new Roo.data.Store({
41573         recordType : Roo.grid.PropertyRecord
41574     });
41575     this.store.on('update', this.onUpdate,  this);
41576     if(source){
41577         this.setSource(source);
41578     }
41579     Roo.grid.PropertyStore.superclass.constructor.call(this);
41580 };
41581
41582
41583
41584 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41585     setSource : function(o){
41586         this.source = o;
41587         this.store.removeAll();
41588         var data = [];
41589         for(var k in o){
41590             if(this.isEditableValue(o[k])){
41591                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41592             }
41593         }
41594         this.store.loadRecords({records: data}, {}, true);
41595     },
41596
41597     onUpdate : function(ds, record, type){
41598         if(type == Roo.data.Record.EDIT){
41599             var v = record.data['value'];
41600             var oldValue = record.modified['value'];
41601             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41602                 this.source[record.id] = v;
41603                 record.commit();
41604                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41605             }else{
41606                 record.reject();
41607             }
41608         }
41609     },
41610
41611     getProperty : function(row){
41612        return this.store.getAt(row);
41613     },
41614
41615     isEditableValue: function(val){
41616         if(val && val instanceof Date){
41617             return true;
41618         }else if(typeof val == 'object' || typeof val == 'function'){
41619             return false;
41620         }
41621         return true;
41622     },
41623
41624     setValue : function(prop, value){
41625         this.source[prop] = value;
41626         this.store.getById(prop).set('value', value);
41627     },
41628
41629     getSource : function(){
41630         return this.source;
41631     }
41632 });
41633
41634 Roo.grid.PropertyColumnModel = function(grid, store){
41635     this.grid = grid;
41636     var g = Roo.grid;
41637     g.PropertyColumnModel.superclass.constructor.call(this, [
41638         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41639         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41640     ]);
41641     this.store = store;
41642     this.bselect = Roo.DomHelper.append(document.body, {
41643         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41644             {tag: 'option', value: 'true', html: 'true'},
41645             {tag: 'option', value: 'false', html: 'false'}
41646         ]
41647     });
41648     Roo.id(this.bselect);
41649     var f = Roo.form;
41650     this.editors = {
41651         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41652         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41653         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41654         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41655         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41656     };
41657     this.renderCellDelegate = this.renderCell.createDelegate(this);
41658     this.renderPropDelegate = this.renderProp.createDelegate(this);
41659 };
41660
41661 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41662     
41663     
41664     nameText : 'Name',
41665     valueText : 'Value',
41666     
41667     dateFormat : 'm/j/Y',
41668     
41669     
41670     renderDate : function(dateVal){
41671         return dateVal.dateFormat(this.dateFormat);
41672     },
41673
41674     renderBool : function(bVal){
41675         return bVal ? 'true' : 'false';
41676     },
41677
41678     isCellEditable : function(colIndex, rowIndex){
41679         return colIndex == 1;
41680     },
41681
41682     getRenderer : function(col){
41683         return col == 1 ?
41684             this.renderCellDelegate : this.renderPropDelegate;
41685     },
41686
41687     renderProp : function(v){
41688         return this.getPropertyName(v);
41689     },
41690
41691     renderCell : function(val){
41692         var rv = val;
41693         if(val instanceof Date){
41694             rv = this.renderDate(val);
41695         }else if(typeof val == 'boolean'){
41696             rv = this.renderBool(val);
41697         }
41698         return Roo.util.Format.htmlEncode(rv);
41699     },
41700
41701     getPropertyName : function(name){
41702         var pn = this.grid.propertyNames;
41703         return pn && pn[name] ? pn[name] : name;
41704     },
41705
41706     getCellEditor : function(colIndex, rowIndex){
41707         var p = this.store.getProperty(rowIndex);
41708         var n = p.data['name'], val = p.data['value'];
41709         
41710         if(typeof(this.grid.customEditors[n]) == 'string'){
41711             return this.editors[this.grid.customEditors[n]];
41712         }
41713         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41714             return this.grid.customEditors[n];
41715         }
41716         if(val instanceof Date){
41717             return this.editors['date'];
41718         }else if(typeof val == 'number'){
41719             return this.editors['number'];
41720         }else if(typeof val == 'boolean'){
41721             return this.editors['boolean'];
41722         }else{
41723             return this.editors['string'];
41724         }
41725     }
41726 });
41727
41728 /**
41729  * @class Roo.grid.PropertyGrid
41730  * @extends Roo.grid.EditorGrid
41731  * This class represents the  interface of a component based property grid control.
41732  * <br><br>Usage:<pre><code>
41733  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41734       
41735  });
41736  // set any options
41737  grid.render();
41738  * </code></pre>
41739   
41740  * @constructor
41741  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41742  * The container MUST have some type of size defined for the grid to fill. The container will be
41743  * automatically set to position relative if it isn't already.
41744  * @param {Object} config A config object that sets properties on this grid.
41745  */
41746 Roo.grid.PropertyGrid = function(container, config){
41747     config = config || {};
41748     var store = new Roo.grid.PropertyStore(this);
41749     this.store = store;
41750     var cm = new Roo.grid.PropertyColumnModel(this, store);
41751     store.store.sort('name', 'ASC');
41752     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41753         ds: store.store,
41754         cm: cm,
41755         enableColLock:false,
41756         enableColumnMove:false,
41757         stripeRows:false,
41758         trackMouseOver: false,
41759         clicksToEdit:1
41760     }, config));
41761     this.getGridEl().addClass('x-props-grid');
41762     this.lastEditRow = null;
41763     this.on('columnresize', this.onColumnResize, this);
41764     this.addEvents({
41765          /**
41766              * @event beforepropertychange
41767              * Fires before a property changes (return false to stop?)
41768              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41769              * @param {String} id Record Id
41770              * @param {String} newval New Value
41771          * @param {String} oldval Old Value
41772              */
41773         "beforepropertychange": true,
41774         /**
41775              * @event propertychange
41776              * Fires after a property changes
41777              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41778              * @param {String} id Record Id
41779              * @param {String} newval New Value
41780          * @param {String} oldval Old Value
41781              */
41782         "propertychange": true
41783     });
41784     this.customEditors = this.customEditors || {};
41785 };
41786 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41787     
41788      /**
41789      * @cfg {Object} customEditors map of colnames=> custom editors.
41790      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41791      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41792      * false disables editing of the field.
41793          */
41794     
41795       /**
41796      * @cfg {Object} propertyNames map of property Names to their displayed value
41797          */
41798     
41799     render : function(){
41800         Roo.grid.PropertyGrid.superclass.render.call(this);
41801         this.autoSize.defer(100, this);
41802     },
41803
41804     autoSize : function(){
41805         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41806         if(this.view){
41807             this.view.fitColumns();
41808         }
41809     },
41810
41811     onColumnResize : function(){
41812         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41813         this.autoSize();
41814     },
41815     /**
41816      * Sets the data for the Grid
41817      * accepts a Key => Value object of all the elements avaiable.
41818      * @param {Object} data  to appear in grid.
41819      */
41820     setSource : function(source){
41821         this.store.setSource(source);
41822         //this.autoSize();
41823     },
41824     /**
41825      * Gets all the data from the grid.
41826      * @return {Object} data  data stored in grid
41827      */
41828     getSource : function(){
41829         return this.store.getSource();
41830     }
41831 });/*
41832   
41833  * Licence LGPL
41834  
41835  */
41836  
41837 /**
41838  * @class Roo.grid.Calendar
41839  * @extends Roo.grid.Grid
41840  * This class extends the Grid to provide a calendar widget
41841  * <br><br>Usage:<pre><code>
41842  var grid = new Roo.grid.Calendar("my-container-id", {
41843      ds: myDataStore,
41844      cm: myColModel,
41845      selModel: mySelectionModel,
41846      autoSizeColumns: true,
41847      monitorWindowResize: false,
41848      trackMouseOver: true
41849      eventstore : real data store..
41850  });
41851  // set any options
41852  grid.render();
41853   
41854   * @constructor
41855  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41856  * The container MUST have some type of size defined for the grid to fill. The container will be
41857  * automatically set to position relative if it isn't already.
41858  * @param {Object} config A config object that sets properties on this grid.
41859  */
41860 Roo.grid.Calendar = function(container, config){
41861         // initialize the container
41862         this.container = Roo.get(container);
41863         this.container.update("");
41864         this.container.setStyle("overflow", "hidden");
41865     this.container.addClass('x-grid-container');
41866
41867     this.id = this.container.id;
41868
41869     Roo.apply(this, config);
41870     // check and correct shorthanded configs
41871     
41872     var rows = [];
41873     var d =1;
41874     for (var r = 0;r < 6;r++) {
41875         
41876         rows[r]=[];
41877         for (var c =0;c < 7;c++) {
41878             rows[r][c]= '';
41879         }
41880     }
41881     if (this.eventStore) {
41882         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41883         this.eventStore.on('load',this.onLoad, this);
41884         this.eventStore.on('beforeload',this.clearEvents, this);
41885          
41886     }
41887     
41888     this.dataSource = new Roo.data.Store({
41889             proxy: new Roo.data.MemoryProxy(rows),
41890             reader: new Roo.data.ArrayReader({}, [
41891                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41892     });
41893
41894     this.dataSource.load();
41895     this.ds = this.dataSource;
41896     this.ds.xmodule = this.xmodule || false;
41897     
41898     
41899     var cellRender = function(v,x,r)
41900     {
41901         return String.format(
41902             '<div class="fc-day  fc-widget-content"><div>' +
41903                 '<div class="fc-event-container"></div>' +
41904                 '<div class="fc-day-number">{0}</div>'+
41905                 
41906                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41907             '</div></div>', v);
41908     
41909     }
41910     
41911     
41912     this.colModel = new Roo.grid.ColumnModel( [
41913         {
41914             xtype: 'ColumnModel',
41915             xns: Roo.grid,
41916             dataIndex : 'weekday0',
41917             header : 'Sunday',
41918             renderer : cellRender
41919         },
41920         {
41921             xtype: 'ColumnModel',
41922             xns: Roo.grid,
41923             dataIndex : 'weekday1',
41924             header : 'Monday',
41925             renderer : cellRender
41926         },
41927         {
41928             xtype: 'ColumnModel',
41929             xns: Roo.grid,
41930             dataIndex : 'weekday2',
41931             header : 'Tuesday',
41932             renderer : cellRender
41933         },
41934         {
41935             xtype: 'ColumnModel',
41936             xns: Roo.grid,
41937             dataIndex : 'weekday3',
41938             header : 'Wednesday',
41939             renderer : cellRender
41940         },
41941         {
41942             xtype: 'ColumnModel',
41943             xns: Roo.grid,
41944             dataIndex : 'weekday4',
41945             header : 'Thursday',
41946             renderer : cellRender
41947         },
41948         {
41949             xtype: 'ColumnModel',
41950             xns: Roo.grid,
41951             dataIndex : 'weekday5',
41952             header : 'Friday',
41953             renderer : cellRender
41954         },
41955         {
41956             xtype: 'ColumnModel',
41957             xns: Roo.grid,
41958             dataIndex : 'weekday6',
41959             header : 'Saturday',
41960             renderer : cellRender
41961         }
41962     ]);
41963     this.cm = this.colModel;
41964     this.cm.xmodule = this.xmodule || false;
41965  
41966         
41967           
41968     //this.selModel = new Roo.grid.CellSelectionModel();
41969     //this.sm = this.selModel;
41970     //this.selModel.init(this);
41971     
41972     
41973     if(this.width){
41974         this.container.setWidth(this.width);
41975     }
41976
41977     if(this.height){
41978         this.container.setHeight(this.height);
41979     }
41980     /** @private */
41981         this.addEvents({
41982         // raw events
41983         /**
41984          * @event click
41985          * The raw click event for the entire grid.
41986          * @param {Roo.EventObject} e
41987          */
41988         "click" : true,
41989         /**
41990          * @event dblclick
41991          * The raw dblclick event for the entire grid.
41992          * @param {Roo.EventObject} e
41993          */
41994         "dblclick" : true,
41995         /**
41996          * @event contextmenu
41997          * The raw contextmenu event for the entire grid.
41998          * @param {Roo.EventObject} e
41999          */
42000         "contextmenu" : true,
42001         /**
42002          * @event mousedown
42003          * The raw mousedown event for the entire grid.
42004          * @param {Roo.EventObject} e
42005          */
42006         "mousedown" : true,
42007         /**
42008          * @event mouseup
42009          * The raw mouseup event for the entire grid.
42010          * @param {Roo.EventObject} e
42011          */
42012         "mouseup" : true,
42013         /**
42014          * @event mouseover
42015          * The raw mouseover event for the entire grid.
42016          * @param {Roo.EventObject} e
42017          */
42018         "mouseover" : true,
42019         /**
42020          * @event mouseout
42021          * The raw mouseout event for the entire grid.
42022          * @param {Roo.EventObject} e
42023          */
42024         "mouseout" : true,
42025         /**
42026          * @event keypress
42027          * The raw keypress event for the entire grid.
42028          * @param {Roo.EventObject} e
42029          */
42030         "keypress" : true,
42031         /**
42032          * @event keydown
42033          * The raw keydown event for the entire grid.
42034          * @param {Roo.EventObject} e
42035          */
42036         "keydown" : true,
42037
42038         // custom events
42039
42040         /**
42041          * @event cellclick
42042          * Fires when a cell is clicked
42043          * @param {Grid} this
42044          * @param {Number} rowIndex
42045          * @param {Number} columnIndex
42046          * @param {Roo.EventObject} e
42047          */
42048         "cellclick" : true,
42049         /**
42050          * @event celldblclick
42051          * Fires when a cell is double clicked
42052          * @param {Grid} this
42053          * @param {Number} rowIndex
42054          * @param {Number} columnIndex
42055          * @param {Roo.EventObject} e
42056          */
42057         "celldblclick" : true,
42058         /**
42059          * @event rowclick
42060          * Fires when a row is clicked
42061          * @param {Grid} this
42062          * @param {Number} rowIndex
42063          * @param {Roo.EventObject} e
42064          */
42065         "rowclick" : true,
42066         /**
42067          * @event rowdblclick
42068          * Fires when a row is double clicked
42069          * @param {Grid} this
42070          * @param {Number} rowIndex
42071          * @param {Roo.EventObject} e
42072          */
42073         "rowdblclick" : true,
42074         /**
42075          * @event headerclick
42076          * Fires when a header is clicked
42077          * @param {Grid} this
42078          * @param {Number} columnIndex
42079          * @param {Roo.EventObject} e
42080          */
42081         "headerclick" : true,
42082         /**
42083          * @event headerdblclick
42084          * Fires when a header cell is double clicked
42085          * @param {Grid} this
42086          * @param {Number} columnIndex
42087          * @param {Roo.EventObject} e
42088          */
42089         "headerdblclick" : true,
42090         /**
42091          * @event rowcontextmenu
42092          * Fires when a row is right clicked
42093          * @param {Grid} this
42094          * @param {Number} rowIndex
42095          * @param {Roo.EventObject} e
42096          */
42097         "rowcontextmenu" : true,
42098         /**
42099          * @event cellcontextmenu
42100          * Fires when a cell is right clicked
42101          * @param {Grid} this
42102          * @param {Number} rowIndex
42103          * @param {Number} cellIndex
42104          * @param {Roo.EventObject} e
42105          */
42106          "cellcontextmenu" : true,
42107         /**
42108          * @event headercontextmenu
42109          * Fires when a header is right clicked
42110          * @param {Grid} this
42111          * @param {Number} columnIndex
42112          * @param {Roo.EventObject} e
42113          */
42114         "headercontextmenu" : true,
42115         /**
42116          * @event bodyscroll
42117          * Fires when the body element is scrolled
42118          * @param {Number} scrollLeft
42119          * @param {Number} scrollTop
42120          */
42121         "bodyscroll" : true,
42122         /**
42123          * @event columnresize
42124          * Fires when the user resizes a column
42125          * @param {Number} columnIndex
42126          * @param {Number} newSize
42127          */
42128         "columnresize" : true,
42129         /**
42130          * @event columnmove
42131          * Fires when the user moves a column
42132          * @param {Number} oldIndex
42133          * @param {Number} newIndex
42134          */
42135         "columnmove" : true,
42136         /**
42137          * @event startdrag
42138          * Fires when row(s) start being dragged
42139          * @param {Grid} this
42140          * @param {Roo.GridDD} dd The drag drop object
42141          * @param {event} e The raw browser event
42142          */
42143         "startdrag" : true,
42144         /**
42145          * @event enddrag
42146          * Fires when a drag operation is complete
42147          * @param {Grid} this
42148          * @param {Roo.GridDD} dd The drag drop object
42149          * @param {event} e The raw browser event
42150          */
42151         "enddrag" : true,
42152         /**
42153          * @event dragdrop
42154          * Fires when dragged row(s) are dropped on a valid DD target
42155          * @param {Grid} this
42156          * @param {Roo.GridDD} dd The drag drop object
42157          * @param {String} targetId The target drag drop object
42158          * @param {event} e The raw browser event
42159          */
42160         "dragdrop" : true,
42161         /**
42162          * @event dragover
42163          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42164          * @param {Grid} this
42165          * @param {Roo.GridDD} dd The drag drop object
42166          * @param {String} targetId The target drag drop object
42167          * @param {event} e The raw browser event
42168          */
42169         "dragover" : true,
42170         /**
42171          * @event dragenter
42172          *  Fires when the dragged row(s) first cross another DD target while being dragged
42173          * @param {Grid} this
42174          * @param {Roo.GridDD} dd The drag drop object
42175          * @param {String} targetId The target drag drop object
42176          * @param {event} e The raw browser event
42177          */
42178         "dragenter" : true,
42179         /**
42180          * @event dragout
42181          * Fires when the dragged row(s) leave another DD target while being dragged
42182          * @param {Grid} this
42183          * @param {Roo.GridDD} dd The drag drop object
42184          * @param {String} targetId The target drag drop object
42185          * @param {event} e The raw browser event
42186          */
42187         "dragout" : true,
42188         /**
42189          * @event rowclass
42190          * Fires when a row is rendered, so you can change add a style to it.
42191          * @param {GridView} gridview   The grid view
42192          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
42193          */
42194         'rowclass' : true,
42195
42196         /**
42197          * @event render
42198          * Fires when the grid is rendered
42199          * @param {Grid} grid
42200          */
42201         'render' : true,
42202             /**
42203              * @event select
42204              * Fires when a date is selected
42205              * @param {DatePicker} this
42206              * @param {Date} date The selected date
42207              */
42208         'select': true,
42209         /**
42210              * @event monthchange
42211              * Fires when the displayed month changes 
42212              * @param {DatePicker} this
42213              * @param {Date} date The selected month
42214              */
42215         'monthchange': true,
42216         /**
42217              * @event evententer
42218              * Fires when mouse over an event
42219              * @param {Calendar} this
42220              * @param {event} Event
42221              */
42222         'evententer': true,
42223         /**
42224              * @event eventleave
42225              * Fires when the mouse leaves an
42226              * @param {Calendar} this
42227              * @param {event}
42228              */
42229         'eventleave': true,
42230         /**
42231              * @event eventclick
42232              * Fires when the mouse click an
42233              * @param {Calendar} this
42234              * @param {event}
42235              */
42236         'eventclick': true,
42237         /**
42238              * @event eventrender
42239              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42240              * @param {Calendar} this
42241              * @param {data} data to be modified
42242              */
42243         'eventrender': true
42244         
42245     });
42246
42247     Roo.grid.Grid.superclass.constructor.call(this);
42248     this.on('render', function() {
42249         this.view.el.addClass('x-grid-cal'); 
42250         
42251         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42252
42253     },this);
42254     
42255     if (!Roo.grid.Calendar.style) {
42256         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42257             
42258             
42259             '.x-grid-cal .x-grid-col' :  {
42260                 height: 'auto !important',
42261                 'vertical-align': 'top'
42262             },
42263             '.x-grid-cal  .fc-event-hori' : {
42264                 height: '14px'
42265             }
42266              
42267             
42268         }, Roo.id());
42269     }
42270
42271     
42272     
42273 };
42274 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42275     /**
42276      * @cfg {Store} eventStore The store that loads events.
42277      */
42278     eventStore : 25,
42279
42280      
42281     activeDate : false,
42282     startDay : 0,
42283     autoWidth : true,
42284     monitorWindowResize : false,
42285
42286     
42287     resizeColumns : function() {
42288         var col = (this.view.el.getWidth() / 7) - 3;
42289         // loop through cols, and setWidth
42290         for(var i =0 ; i < 7 ; i++){
42291             this.cm.setColumnWidth(i, col);
42292         }
42293     },
42294      setDate :function(date) {
42295         
42296         Roo.log('setDate?');
42297         
42298         this.resizeColumns();
42299         var vd = this.activeDate;
42300         this.activeDate = date;
42301 //        if(vd && this.el){
42302 //            var t = date.getTime();
42303 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42304 //                Roo.log('using add remove');
42305 //                
42306 //                this.fireEvent('monthchange', this, date);
42307 //                
42308 //                this.cells.removeClass("fc-state-highlight");
42309 //                this.cells.each(function(c){
42310 //                   if(c.dateValue == t){
42311 //                       c.addClass("fc-state-highlight");
42312 //                       setTimeout(function(){
42313 //                            try{c.dom.firstChild.focus();}catch(e){}
42314 //                       }, 50);
42315 //                       return false;
42316 //                   }
42317 //                   return true;
42318 //                });
42319 //                return;
42320 //            }
42321 //        }
42322         
42323         var days = date.getDaysInMonth();
42324         
42325         var firstOfMonth = date.getFirstDateOfMonth();
42326         var startingPos = firstOfMonth.getDay()-this.startDay;
42327         
42328         if(startingPos < this.startDay){
42329             startingPos += 7;
42330         }
42331         
42332         var pm = date.add(Date.MONTH, -1);
42333         var prevStart = pm.getDaysInMonth()-startingPos;
42334 //        
42335         
42336         
42337         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42338         
42339         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42340         //this.cells.addClassOnOver('fc-state-hover');
42341         
42342         var cells = this.cells.elements;
42343         var textEls = this.textNodes;
42344         
42345         //Roo.each(cells, function(cell){
42346         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42347         //});
42348         
42349         days += startingPos;
42350
42351         // convert everything to numbers so it's fast
42352         var day = 86400000;
42353         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42354         //Roo.log(d);
42355         //Roo.log(pm);
42356         //Roo.log(prevStart);
42357         
42358         var today = new Date().clearTime().getTime();
42359         var sel = date.clearTime().getTime();
42360         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42361         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42362         var ddMatch = this.disabledDatesRE;
42363         var ddText = this.disabledDatesText;
42364         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42365         var ddaysText = this.disabledDaysText;
42366         var format = this.format;
42367         
42368         var setCellClass = function(cal, cell){
42369             
42370             //Roo.log('set Cell Class');
42371             cell.title = "";
42372             var t = d.getTime();
42373             
42374             //Roo.log(d);
42375             
42376             
42377             cell.dateValue = t;
42378             if(t == today){
42379                 cell.className += " fc-today";
42380                 cell.className += " fc-state-highlight";
42381                 cell.title = cal.todayText;
42382             }
42383             if(t == sel){
42384                 // disable highlight in other month..
42385                 cell.className += " fc-state-highlight";
42386                 
42387             }
42388             // disabling
42389             if(t < min) {
42390                 //cell.className = " fc-state-disabled";
42391                 cell.title = cal.minText;
42392                 return;
42393             }
42394             if(t > max) {
42395                 //cell.className = " fc-state-disabled";
42396                 cell.title = cal.maxText;
42397                 return;
42398             }
42399             if(ddays){
42400                 if(ddays.indexOf(d.getDay()) != -1){
42401                     // cell.title = ddaysText;
42402                    // cell.className = " fc-state-disabled";
42403                 }
42404             }
42405             if(ddMatch && format){
42406                 var fvalue = d.dateFormat(format);
42407                 if(ddMatch.test(fvalue)){
42408                     cell.title = ddText.replace("%0", fvalue);
42409                    cell.className = " fc-state-disabled";
42410                 }
42411             }
42412             
42413             if (!cell.initialClassName) {
42414                 cell.initialClassName = cell.dom.className;
42415             }
42416             
42417             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42418         };
42419
42420         var i = 0;
42421         
42422         for(; i < startingPos; i++) {
42423             cells[i].dayName =  (++prevStart);
42424             Roo.log(textEls[i]);
42425             d.setDate(d.getDate()+1);
42426             
42427             //cells[i].className = "fc-past fc-other-month";
42428             setCellClass(this, cells[i]);
42429         }
42430         
42431         var intDay = 0;
42432         
42433         for(; i < days; i++){
42434             intDay = i - startingPos + 1;
42435             cells[i].dayName =  (intDay);
42436             d.setDate(d.getDate()+1);
42437             
42438             cells[i].className = ''; // "x-date-active";
42439             setCellClass(this, cells[i]);
42440         }
42441         var extraDays = 0;
42442         
42443         for(; i < 42; i++) {
42444             //textEls[i].innerHTML = (++extraDays);
42445             
42446             d.setDate(d.getDate()+1);
42447             cells[i].dayName = (++extraDays);
42448             cells[i].className = "fc-future fc-other-month";
42449             setCellClass(this, cells[i]);
42450         }
42451         
42452         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42453         
42454         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42455         
42456         // this will cause all the cells to mis
42457         var rows= [];
42458         var i =0;
42459         for (var r = 0;r < 6;r++) {
42460             for (var c =0;c < 7;c++) {
42461                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42462             }    
42463         }
42464         
42465         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42466         for(i=0;i<cells.length;i++) {
42467             
42468             this.cells.elements[i].dayName = cells[i].dayName ;
42469             this.cells.elements[i].className = cells[i].className;
42470             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42471             this.cells.elements[i].title = cells[i].title ;
42472             this.cells.elements[i].dateValue = cells[i].dateValue ;
42473         }
42474         
42475         
42476         
42477         
42478         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42479         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42480         
42481         ////if(totalRows != 6){
42482             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42483            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42484        // }
42485         
42486         this.fireEvent('monthchange', this, date);
42487         
42488         
42489     },
42490  /**
42491      * Returns the grid's SelectionModel.
42492      * @return {SelectionModel}
42493      */
42494     getSelectionModel : function(){
42495         if(!this.selModel){
42496             this.selModel = new Roo.grid.CellSelectionModel();
42497         }
42498         return this.selModel;
42499     },
42500
42501     load: function() {
42502         this.eventStore.load()
42503         
42504         
42505         
42506     },
42507     
42508     findCell : function(dt) {
42509         dt = dt.clearTime().getTime();
42510         var ret = false;
42511         this.cells.each(function(c){
42512             //Roo.log("check " +c.dateValue + '?=' + dt);
42513             if(c.dateValue == dt){
42514                 ret = c;
42515                 return false;
42516             }
42517             return true;
42518         });
42519         
42520         return ret;
42521     },
42522     
42523     findCells : function(rec) {
42524         var s = rec.data.start_dt.clone().clearTime().getTime();
42525        // Roo.log(s);
42526         var e= rec.data.end_dt.clone().clearTime().getTime();
42527        // Roo.log(e);
42528         var ret = [];
42529         this.cells.each(function(c){
42530              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42531             
42532             if(c.dateValue > e){
42533                 return ;
42534             }
42535             if(c.dateValue < s){
42536                 return ;
42537             }
42538             ret.push(c);
42539         });
42540         
42541         return ret;    
42542     },
42543     
42544     findBestRow: function(cells)
42545     {
42546         var ret = 0;
42547         
42548         for (var i =0 ; i < cells.length;i++) {
42549             ret  = Math.max(cells[i].rows || 0,ret);
42550         }
42551         return ret;
42552         
42553     },
42554     
42555     
42556     addItem : function(rec)
42557     {
42558         // look for vertical location slot in
42559         var cells = this.findCells(rec);
42560         
42561         rec.row = this.findBestRow(cells);
42562         
42563         // work out the location.
42564         
42565         var crow = false;
42566         var rows = [];
42567         for(var i =0; i < cells.length; i++) {
42568             if (!crow) {
42569                 crow = {
42570                     start : cells[i],
42571                     end :  cells[i]
42572                 };
42573                 continue;
42574             }
42575             if (crow.start.getY() == cells[i].getY()) {
42576                 // on same row.
42577                 crow.end = cells[i];
42578                 continue;
42579             }
42580             // different row.
42581             rows.push(crow);
42582             crow = {
42583                 start: cells[i],
42584                 end : cells[i]
42585             };
42586             
42587         }
42588         
42589         rows.push(crow);
42590         rec.els = [];
42591         rec.rows = rows;
42592         rec.cells = cells;
42593         for (var i = 0; i < cells.length;i++) {
42594             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42595             
42596         }
42597         
42598         
42599     },
42600     
42601     clearEvents: function() {
42602         
42603         if (!this.eventStore.getCount()) {
42604             return;
42605         }
42606         // reset number of rows in cells.
42607         Roo.each(this.cells.elements, function(c){
42608             c.rows = 0;
42609         });
42610         
42611         this.eventStore.each(function(e) {
42612             this.clearEvent(e);
42613         },this);
42614         
42615     },
42616     
42617     clearEvent : function(ev)
42618     {
42619         if (ev.els) {
42620             Roo.each(ev.els, function(el) {
42621                 el.un('mouseenter' ,this.onEventEnter, this);
42622                 el.un('mouseleave' ,this.onEventLeave, this);
42623                 el.remove();
42624             },this);
42625             ev.els = [];
42626         }
42627     },
42628     
42629     
42630     renderEvent : function(ev,ctr) {
42631         if (!ctr) {
42632              ctr = this.view.el.select('.fc-event-container',true).first();
42633         }
42634         
42635          
42636         this.clearEvent(ev);
42637             //code
42638        
42639         
42640         
42641         ev.els = [];
42642         var cells = ev.cells;
42643         var rows = ev.rows;
42644         this.fireEvent('eventrender', this, ev);
42645         
42646         for(var i =0; i < rows.length; i++) {
42647             
42648             cls = '';
42649             if (i == 0) {
42650                 cls += ' fc-event-start';
42651             }
42652             if ((i+1) == rows.length) {
42653                 cls += ' fc-event-end';
42654             }
42655             
42656             //Roo.log(ev.data);
42657             // how many rows should it span..
42658             var cg = this.eventTmpl.append(ctr,Roo.apply({
42659                 fccls : cls
42660                 
42661             }, ev.data) , true);
42662             
42663             
42664             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42665             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42666             cg.on('click', this.onEventClick, this, ev);
42667             
42668             ev.els.push(cg);
42669             
42670             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42671             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42672             //Roo.log(cg);
42673              
42674             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42675             cg.setWidth(ebox.right - sbox.x -2);
42676         }
42677     },
42678     
42679     renderEvents: function()
42680     {   
42681         // first make sure there is enough space..
42682         
42683         if (!this.eventTmpl) {
42684             this.eventTmpl = new Roo.Template(
42685                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42686                     '<div class="fc-event-inner">' +
42687                         '<span class="fc-event-time">{time}</span>' +
42688                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42689                     '</div>' +
42690                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42691                 '</div>'
42692             );
42693                 
42694         }
42695                
42696         
42697         
42698         this.cells.each(function(c) {
42699             //Roo.log(c.select('.fc-day-content div',true).first());
42700             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42701         });
42702         
42703         var ctr = this.view.el.select('.fc-event-container',true).first();
42704         
42705         var cls;
42706         this.eventStore.each(function(ev){
42707             
42708             this.renderEvent(ev);
42709              
42710              
42711         }, this);
42712         this.view.layout();
42713         
42714     },
42715     
42716     onEventEnter: function (e, el,event,d) {
42717         this.fireEvent('evententer', this, el, event);
42718     },
42719     
42720     onEventLeave: function (e, el,event,d) {
42721         this.fireEvent('eventleave', this, el, event);
42722     },
42723     
42724     onEventClick: function (e, el,event,d) {
42725         this.fireEvent('eventclick', this, el, event);
42726     },
42727     
42728     onMonthChange: function () {
42729         this.store.load();
42730     },
42731     
42732     onLoad: function () {
42733         
42734         //Roo.log('calendar onload');
42735 //         
42736         if(this.eventStore.getCount() > 0){
42737             
42738            
42739             
42740             this.eventStore.each(function(d){
42741                 
42742                 
42743                 // FIXME..
42744                 var add =   d.data;
42745                 if (typeof(add.end_dt) == 'undefined')  {
42746                     Roo.log("Missing End time in calendar data: ");
42747                     Roo.log(d);
42748                     return;
42749                 }
42750                 if (typeof(add.start_dt) == 'undefined')  {
42751                     Roo.log("Missing Start time in calendar data: ");
42752                     Roo.log(d);
42753                     return;
42754                 }
42755                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42756                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42757                 add.id = add.id || d.id;
42758                 add.title = add.title || '??';
42759                 
42760                 this.addItem(d);
42761                 
42762              
42763             },this);
42764         }
42765         
42766         this.renderEvents();
42767     }
42768     
42769
42770 });
42771 /*
42772  grid : {
42773                 xtype: 'Grid',
42774                 xns: Roo.grid,
42775                 listeners : {
42776                     render : function ()
42777                     {
42778                         _this.grid = this;
42779                         
42780                         if (!this.view.el.hasClass('course-timesheet')) {
42781                             this.view.el.addClass('course-timesheet');
42782                         }
42783                         if (this.tsStyle) {
42784                             this.ds.load({});
42785                             return; 
42786                         }
42787                         Roo.log('width');
42788                         Roo.log(_this.grid.view.el.getWidth());
42789                         
42790                         
42791                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42792                             '.course-timesheet .x-grid-row' : {
42793                                 height: '80px'
42794                             },
42795                             '.x-grid-row td' : {
42796                                 'vertical-align' : 0
42797                             },
42798                             '.course-edit-link' : {
42799                                 'color' : 'blue',
42800                                 'text-overflow' : 'ellipsis',
42801                                 'overflow' : 'hidden',
42802                                 'white-space' : 'nowrap',
42803                                 'cursor' : 'pointer'
42804                             },
42805                             '.sub-link' : {
42806                                 'color' : 'green'
42807                             },
42808                             '.de-act-sup-link' : {
42809                                 'color' : 'purple',
42810                                 'text-decoration' : 'line-through'
42811                             },
42812                             '.de-act-link' : {
42813                                 'color' : 'red',
42814                                 'text-decoration' : 'line-through'
42815                             },
42816                             '.course-timesheet .course-highlight' : {
42817                                 'border-top-style': 'dashed !important',
42818                                 'border-bottom-bottom': 'dashed !important'
42819                             },
42820                             '.course-timesheet .course-item' : {
42821                                 'font-family'   : 'tahoma, arial, helvetica',
42822                                 'font-size'     : '11px',
42823                                 'overflow'      : 'hidden',
42824                                 'padding-left'  : '10px',
42825                                 'padding-right' : '10px',
42826                                 'padding-top' : '10px' 
42827                             }
42828                             
42829                         }, Roo.id());
42830                                 this.ds.load({});
42831                     }
42832                 },
42833                 autoWidth : true,
42834                 monitorWindowResize : false,
42835                 cellrenderer : function(v,x,r)
42836                 {
42837                     return v;
42838                 },
42839                 sm : {
42840                     xtype: 'CellSelectionModel',
42841                     xns: Roo.grid
42842                 },
42843                 dataSource : {
42844                     xtype: 'Store',
42845                     xns: Roo.data,
42846                     listeners : {
42847                         beforeload : function (_self, options)
42848                         {
42849                             options.params = options.params || {};
42850                             options.params._month = _this.monthField.getValue();
42851                             options.params.limit = 9999;
42852                             options.params['sort'] = 'when_dt';    
42853                             options.params['dir'] = 'ASC';    
42854                             this.proxy.loadResponse = this.loadResponse;
42855                             Roo.log("load?");
42856                             //this.addColumns();
42857                         },
42858                         load : function (_self, records, options)
42859                         {
42860                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42861                                 // if you click on the translation.. you can edit it...
42862                                 var el = Roo.get(this);
42863                                 var id = el.dom.getAttribute('data-id');
42864                                 var d = el.dom.getAttribute('data-date');
42865                                 var t = el.dom.getAttribute('data-time');
42866                                 //var id = this.child('span').dom.textContent;
42867                                 
42868                                 //Roo.log(this);
42869                                 Pman.Dialog.CourseCalendar.show({
42870                                     id : id,
42871                                     when_d : d,
42872                                     when_t : t,
42873                                     productitem_active : id ? 1 : 0
42874                                 }, function() {
42875                                     _this.grid.ds.load({});
42876                                 });
42877                            
42878                            });
42879                            
42880                            _this.panel.fireEvent('resize', [ '', '' ]);
42881                         }
42882                     },
42883                     loadResponse : function(o, success, response){
42884                             // this is overridden on before load..
42885                             
42886                             Roo.log("our code?");       
42887                             //Roo.log(success);
42888                             //Roo.log(response)
42889                             delete this.activeRequest;
42890                             if(!success){
42891                                 this.fireEvent("loadexception", this, o, response);
42892                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42893                                 return;
42894                             }
42895                             var result;
42896                             try {
42897                                 result = o.reader.read(response);
42898                             }catch(e){
42899                                 Roo.log("load exception?");
42900                                 this.fireEvent("loadexception", this, o, response, e);
42901                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42902                                 return;
42903                             }
42904                             Roo.log("ready...");        
42905                             // loop through result.records;
42906                             // and set this.tdate[date] = [] << array of records..
42907                             _this.tdata  = {};
42908                             Roo.each(result.records, function(r){
42909                                 //Roo.log(r.data);
42910                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42911                                     _this.tdata[r.data.when_dt.format('j')] = [];
42912                                 }
42913                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42914                             });
42915                             
42916                             //Roo.log(_this.tdata);
42917                             
42918                             result.records = [];
42919                             result.totalRecords = 6;
42920                     
42921                             // let's generate some duumy records for the rows.
42922                             //var st = _this.dateField.getValue();
42923                             
42924                             // work out monday..
42925                             //st = st.add(Date.DAY, -1 * st.format('w'));
42926                             
42927                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42928                             
42929                             var firstOfMonth = date.getFirstDayOfMonth();
42930                             var days = date.getDaysInMonth();
42931                             var d = 1;
42932                             var firstAdded = false;
42933                             for (var i = 0; i < result.totalRecords ; i++) {
42934                                 //var d= st.add(Date.DAY, i);
42935                                 var row = {};
42936                                 var added = 0;
42937                                 for(var w = 0 ; w < 7 ; w++){
42938                                     if(!firstAdded && firstOfMonth != w){
42939                                         continue;
42940                                     }
42941                                     if(d > days){
42942                                         continue;
42943                                     }
42944                                     firstAdded = true;
42945                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42946                                     row['weekday'+w] = String.format(
42947                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42948                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42949                                                     d,
42950                                                     date.format('Y-m-')+dd
42951                                                 );
42952                                     added++;
42953                                     if(typeof(_this.tdata[d]) != 'undefined'){
42954                                         Roo.each(_this.tdata[d], function(r){
42955                                             var is_sub = '';
42956                                             var deactive = '';
42957                                             var id = r.id;
42958                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42959                                             if(r.parent_id*1>0){
42960                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42961                                                 id = r.parent_id;
42962                                             }
42963                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42964                                                 deactive = 'de-act-link';
42965                                             }
42966                                             
42967                                             row['weekday'+w] += String.format(
42968                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42969                                                     id, //0
42970                                                     r.product_id_name, //1
42971                                                     r.when_dt.format('h:ia'), //2
42972                                                     is_sub, //3
42973                                                     deactive, //4
42974                                                     desc // 5
42975                                             );
42976                                         });
42977                                     }
42978                                     d++;
42979                                 }
42980                                 
42981                                 // only do this if something added..
42982                                 if(added > 0){ 
42983                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42984                                 }
42985                                 
42986                                 
42987                                 // push it twice. (second one with an hour..
42988                                 
42989                             }
42990                             //Roo.log(result);
42991                             this.fireEvent("load", this, o, o.request.arg);
42992                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42993                         },
42994                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42995                     proxy : {
42996                         xtype: 'HttpProxy',
42997                         xns: Roo.data,
42998                         method : 'GET',
42999                         url : baseURL + '/Roo/Shop_course.php'
43000                     },
43001                     reader : {
43002                         xtype: 'JsonReader',
43003                         xns: Roo.data,
43004                         id : 'id',
43005                         fields : [
43006                             {
43007                                 'name': 'id',
43008                                 'type': 'int'
43009                             },
43010                             {
43011                                 'name': 'when_dt',
43012                                 'type': 'string'
43013                             },
43014                             {
43015                                 'name': 'end_dt',
43016                                 'type': 'string'
43017                             },
43018                             {
43019                                 'name': 'parent_id',
43020                                 'type': 'int'
43021                             },
43022                             {
43023                                 'name': 'product_id',
43024                                 'type': 'int'
43025                             },
43026                             {
43027                                 'name': 'productitem_id',
43028                                 'type': 'int'
43029                             },
43030                             {
43031                                 'name': 'guid',
43032                                 'type': 'int'
43033                             }
43034                         ]
43035                     }
43036                 },
43037                 toolbar : {
43038                     xtype: 'Toolbar',
43039                     xns: Roo,
43040                     items : [
43041                         {
43042                             xtype: 'Button',
43043                             xns: Roo.Toolbar,
43044                             listeners : {
43045                                 click : function (_self, e)
43046                                 {
43047                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43048                                     sd.setMonth(sd.getMonth()-1);
43049                                     _this.monthField.setValue(sd.format('Y-m-d'));
43050                                     _this.grid.ds.load({});
43051                                 }
43052                             },
43053                             text : "Back"
43054                         },
43055                         {
43056                             xtype: 'Separator',
43057                             xns: Roo.Toolbar
43058                         },
43059                         {
43060                             xtype: 'MonthField',
43061                             xns: Roo.form,
43062                             listeners : {
43063                                 render : function (_self)
43064                                 {
43065                                     _this.monthField = _self;
43066                                    // _this.monthField.set  today
43067                                 },
43068                                 select : function (combo, date)
43069                                 {
43070                                     _this.grid.ds.load({});
43071                                 }
43072                             },
43073                             value : (function() { return new Date(); })()
43074                         },
43075                         {
43076                             xtype: 'Separator',
43077                             xns: Roo.Toolbar
43078                         },
43079                         {
43080                             xtype: 'TextItem',
43081                             xns: Roo.Toolbar,
43082                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
43083                         },
43084                         {
43085                             xtype: 'Fill',
43086                             xns: Roo.Toolbar
43087                         },
43088                         {
43089                             xtype: 'Button',
43090                             xns: Roo.Toolbar,
43091                             listeners : {
43092                                 click : function (_self, e)
43093                                 {
43094                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43095                                     sd.setMonth(sd.getMonth()+1);
43096                                     _this.monthField.setValue(sd.format('Y-m-d'));
43097                                     _this.grid.ds.load({});
43098                                 }
43099                             },
43100                             text : "Next"
43101                         }
43102                     ]
43103                 },
43104                  
43105             }
43106         };
43107         
43108         *//*
43109  * Based on:
43110  * Ext JS Library 1.1.1
43111  * Copyright(c) 2006-2007, Ext JS, LLC.
43112  *
43113  * Originally Released Under LGPL - original licence link has changed is not relivant.
43114  *
43115  * Fork - LGPL
43116  * <script type="text/javascript">
43117  */
43118  
43119 /**
43120  * @class Roo.LoadMask
43121  * A simple utility class for generically masking elements while loading data.  If the element being masked has
43122  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
43123  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
43124  * element's UpdateManager load indicator and will be destroyed after the initial load.
43125  * @constructor
43126  * Create a new LoadMask
43127  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
43128  * @param {Object} config The config object
43129  */
43130 Roo.LoadMask = function(el, config){
43131     this.el = Roo.get(el);
43132     Roo.apply(this, config);
43133     if(this.store){
43134         this.store.on('beforeload', this.onBeforeLoad, this);
43135         this.store.on('load', this.onLoad, this);
43136         this.store.on('loadexception', this.onLoadException, this);
43137         this.removeMask = false;
43138     }else{
43139         var um = this.el.getUpdateManager();
43140         um.showLoadIndicator = false; // disable the default indicator
43141         um.on('beforeupdate', this.onBeforeLoad, this);
43142         um.on('update', this.onLoad, this);
43143         um.on('failure', this.onLoad, this);
43144         this.removeMask = true;
43145     }
43146 };
43147
43148 Roo.LoadMask.prototype = {
43149     /**
43150      * @cfg {Boolean} removeMask
43151      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
43152      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
43153      */
43154     removeMask : false,
43155     /**
43156      * @cfg {String} msg
43157      * The text to display in a centered loading message box (defaults to 'Loading...')
43158      */
43159     msg : 'Loading...',
43160     /**
43161      * @cfg {String} msgCls
43162      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43163      */
43164     msgCls : 'x-mask-loading',
43165
43166     /**
43167      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43168      * @type Boolean
43169      */
43170     disabled: false,
43171
43172     /**
43173      * Disables the mask to prevent it from being displayed
43174      */
43175     disable : function(){
43176        this.disabled = true;
43177     },
43178
43179     /**
43180      * Enables the mask so that it can be displayed
43181      */
43182     enable : function(){
43183         this.disabled = false;
43184     },
43185     
43186     onLoadException : function()
43187     {
43188         Roo.log(arguments);
43189         
43190         if (typeof(arguments[3]) != 'undefined') {
43191             Roo.MessageBox.alert("Error loading",arguments[3]);
43192         } 
43193         /*
43194         try {
43195             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43196                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43197             }   
43198         } catch(e) {
43199             
43200         }
43201         */
43202     
43203         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43204     },
43205     // private
43206     onLoad : function()
43207     {
43208         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43209     },
43210
43211     // private
43212     onBeforeLoad : function(){
43213         if(!this.disabled){
43214             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43215         }
43216     },
43217
43218     // private
43219     destroy : function(){
43220         if(this.store){
43221             this.store.un('beforeload', this.onBeforeLoad, this);
43222             this.store.un('load', this.onLoad, this);
43223             this.store.un('loadexception', this.onLoadException, this);
43224         }else{
43225             var um = this.el.getUpdateManager();
43226             um.un('beforeupdate', this.onBeforeLoad, this);
43227             um.un('update', this.onLoad, this);
43228             um.un('failure', this.onLoad, this);
43229         }
43230     }
43231 };/*
43232  * Based on:
43233  * Ext JS Library 1.1.1
43234  * Copyright(c) 2006-2007, Ext JS, LLC.
43235  *
43236  * Originally Released Under LGPL - original licence link has changed is not relivant.
43237  *
43238  * Fork - LGPL
43239  * <script type="text/javascript">
43240  */
43241
43242
43243 /**
43244  * @class Roo.XTemplate
43245  * @extends Roo.Template
43246  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43247 <pre><code>
43248 var t = new Roo.XTemplate(
43249         '&lt;select name="{name}"&gt;',
43250                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43251         '&lt;/select&gt;'
43252 );
43253  
43254 // then append, applying the master template values
43255  </code></pre>
43256  *
43257  * Supported features:
43258  *
43259  *  Tags:
43260
43261 <pre><code>
43262       {a_variable} - output encoded.
43263       {a_variable.format:("Y-m-d")} - call a method on the variable
43264       {a_variable:raw} - unencoded output
43265       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43266       {a_variable:this.method_on_template(...)} - call a method on the template object.
43267  
43268 </code></pre>
43269  *  The tpl tag:
43270 <pre><code>
43271         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43272         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43273         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43274         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43275   
43276         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43277         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43278 </code></pre>
43279  *      
43280  */
43281 Roo.XTemplate = function()
43282 {
43283     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43284     if (this.html) {
43285         this.compile();
43286     }
43287 };
43288
43289
43290 Roo.extend(Roo.XTemplate, Roo.Template, {
43291
43292     /**
43293      * The various sub templates
43294      */
43295     tpls : false,
43296     /**
43297      *
43298      * basic tag replacing syntax
43299      * WORD:WORD()
43300      *
43301      * // you can fake an object call by doing this
43302      *  x.t:(test,tesT) 
43303      * 
43304      */
43305     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43306
43307     /**
43308      * compile the template
43309      *
43310      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43311      *
43312      */
43313     compile: function()
43314     {
43315         var s = this.html;
43316      
43317         s = ['<tpl>', s, '</tpl>'].join('');
43318     
43319         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43320             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43321             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43322             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43323             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43324             m,
43325             id     = 0,
43326             tpls   = [];
43327     
43328         while(true == !!(m = s.match(re))){
43329             var forMatch   = m[0].match(nameRe),
43330                 ifMatch   = m[0].match(ifRe),
43331                 execMatch   = m[0].match(execRe),
43332                 namedMatch   = m[0].match(namedRe),
43333                 
43334                 exp  = null, 
43335                 fn   = null,
43336                 exec = null,
43337                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43338                 
43339             if (ifMatch) {
43340                 // if - puts fn into test..
43341                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43342                 if(exp){
43343                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43344                 }
43345             }
43346             
43347             if (execMatch) {
43348                 // exec - calls a function... returns empty if true is  returned.
43349                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43350                 if(exp){
43351                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43352                 }
43353             }
43354             
43355             
43356             if (name) {
43357                 // for = 
43358                 switch(name){
43359                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43360                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43361                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43362                 }
43363             }
43364             var uid = namedMatch ? namedMatch[1] : id;
43365             
43366             
43367             tpls.push({
43368                 id:     namedMatch ? namedMatch[1] : id,
43369                 target: name,
43370                 exec:   exec,
43371                 test:   fn,
43372                 body:   m[1] || ''
43373             });
43374             if (namedMatch) {
43375                 s = s.replace(m[0], '');
43376             } else { 
43377                 s = s.replace(m[0], '{xtpl'+ id + '}');
43378             }
43379             ++id;
43380         }
43381         this.tpls = [];
43382         for(var i = tpls.length-1; i >= 0; --i){
43383             this.compileTpl(tpls[i]);
43384             this.tpls[tpls[i].id] = tpls[i];
43385         }
43386         this.master = tpls[tpls.length-1];
43387         return this;
43388     },
43389     /**
43390      * same as applyTemplate, except it's done to one of the subTemplates
43391      * when using named templates, you can do:
43392      *
43393      * var str = pl.applySubTemplate('your-name', values);
43394      *
43395      * 
43396      * @param {Number} id of the template
43397      * @param {Object} values to apply to template
43398      * @param {Object} parent (normaly the instance of this object)
43399      */
43400     applySubTemplate : function(id, values, parent)
43401     {
43402         
43403         
43404         var t = this.tpls[id];
43405         
43406         
43407         try { 
43408             if(t.test && !t.test.call(this, values, parent)){
43409                 return '';
43410             }
43411         } catch(e) {
43412             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43413             Roo.log(e.toString());
43414             Roo.log(t.test);
43415             return ''
43416         }
43417         try { 
43418             
43419             if(t.exec && t.exec.call(this, values, parent)){
43420                 return '';
43421             }
43422         } catch(e) {
43423             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43424             Roo.log(e.toString());
43425             Roo.log(t.exec);
43426             return ''
43427         }
43428         try {
43429             var vs = t.target ? t.target.call(this, values, parent) : values;
43430             parent = t.target ? values : parent;
43431             if(t.target && vs instanceof Array){
43432                 var buf = [];
43433                 for(var i = 0, len = vs.length; i < len; i++){
43434                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43435                 }
43436                 return buf.join('');
43437             }
43438             return t.compiled.call(this, vs, parent);
43439         } catch (e) {
43440             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43441             Roo.log(e.toString());
43442             Roo.log(t.compiled);
43443             return '';
43444         }
43445     },
43446
43447     compileTpl : function(tpl)
43448     {
43449         var fm = Roo.util.Format;
43450         var useF = this.disableFormats !== true;
43451         var sep = Roo.isGecko ? "+" : ",";
43452         var undef = function(str) {
43453             Roo.log("Property not found :"  + str);
43454             return '';
43455         };
43456         
43457         var fn = function(m, name, format, args)
43458         {
43459             //Roo.log(arguments);
43460             args = args ? args.replace(/\\'/g,"'") : args;
43461             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43462             if (typeof(format) == 'undefined') {
43463                 format= 'htmlEncode';
43464             }
43465             if (format == 'raw' ) {
43466                 format = false;
43467             }
43468             
43469             if(name.substr(0, 4) == 'xtpl'){
43470                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43471             }
43472             
43473             // build an array of options to determine if value is undefined..
43474             
43475             // basically get 'xxxx.yyyy' then do
43476             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43477             //    (function () { Roo.log("Property not found"); return ''; })() :
43478             //    ......
43479             
43480             var udef_ar = [];
43481             var lookfor = '';
43482             Roo.each(name.split('.'), function(st) {
43483                 lookfor += (lookfor.length ? '.': '') + st;
43484                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43485             });
43486             
43487             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43488             
43489             
43490             if(format && useF){
43491                 
43492                 args = args ? ',' + args : "";
43493                  
43494                 if(format.substr(0, 5) != "this."){
43495                     format = "fm." + format + '(';
43496                 }else{
43497                     format = 'this.call("'+ format.substr(5) + '", ';
43498                     args = ", values";
43499                 }
43500                 
43501                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43502             }
43503              
43504             if (args.length) {
43505                 // called with xxyx.yuu:(test,test)
43506                 // change to ()
43507                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43508             }
43509             // raw.. - :raw modifier..
43510             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43511             
43512         };
43513         var body;
43514         // branched to use + in gecko and [].join() in others
43515         if(Roo.isGecko){
43516             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43517                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43518                     "';};};";
43519         }else{
43520             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43521             body.push(tpl.body.replace(/(\r\n|\n)/g,
43522                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43523             body.push("'].join('');};};");
43524             body = body.join('');
43525         }
43526         
43527         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43528        
43529         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43530         eval(body);
43531         
43532         return this;
43533     },
43534
43535     applyTemplate : function(values){
43536         return this.master.compiled.call(this, values, {});
43537         //var s = this.subs;
43538     },
43539
43540     apply : function(){
43541         return this.applyTemplate.apply(this, arguments);
43542     }
43543
43544  });
43545
43546 Roo.XTemplate.from = function(el){
43547     el = Roo.getDom(el);
43548     return new Roo.XTemplate(el.value || el.innerHTML);
43549 };