Roo/HtmlEditorCore.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1398  * to the Reader when its load method is called.
1399  * @constructor
1400  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1401  */
1402 Roo.data.MemoryProxy = function(data){
1403     if (data.data) {
1404         data = data.data;
1405     }
1406     Roo.data.MemoryProxy.superclass.constructor.call(this);
1407     this.data = data;
1408 };
1409
1410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1411     
1412     /**
1413      * Load data from the requested source (in this case an in-memory
1414      * data object passed to the constructor), read the data object into
1415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1416      * process that block using the passed callback.
1417      * @param {Object} params This parameter is not used by the MemoryProxy class.
1418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1419      * object into a block of Roo.data.Records.
1420      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1421      * The function must be passed <ul>
1422      * <li>The Record block object</li>
1423      * <li>The "arg" argument from the load function</li>
1424      * <li>A boolean success indicator</li>
1425      * </ul>
1426      * @param {Object} scope The scope in which to call the callback
1427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1428      */
1429     load : function(params, reader, callback, scope, arg){
1430         params = params || {};
1431         var result;
1432         try {
1433             result = reader.readRecords(params.data ? params.data :this.data);
1434         }catch(e){
1435             this.fireEvent("loadexception", this, arg, null, e);
1436             callback.call(scope, null, arg, false);
1437             return;
1438         }
1439         callback.call(scope, result, arg, true);
1440     },
1441     
1442     // private
1443     update : function(params, records){
1444         
1445     }
1446 });/*
1447  * Based on:
1448  * Ext JS Library 1.1.1
1449  * Copyright(c) 2006-2007, Ext JS, LLC.
1450  *
1451  * Originally Released Under LGPL - original licence link has changed is not relivant.
1452  *
1453  * Fork - LGPL
1454  * <script type="text/javascript">
1455  */
1456 /**
1457  * @class Roo.data.HttpProxy
1458  * @extends Roo.data.DataProxy
1459  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1460  * configured to reference a certain URL.<br><br>
1461  * <p>
1462  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1463  * from which the running page was served.<br><br>
1464  * <p>
1465  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1466  * <p>
1467  * Be aware that to enable the browser to parse an XML document, the server must set
1468  * the Content-Type header in the HTTP response to "text/xml".
1469  * @constructor
1470  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1471  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1472  * will be used to make the request.
1473  */
1474 Roo.data.HttpProxy = function(conn){
1475     Roo.data.HttpProxy.superclass.constructor.call(this);
1476     // is conn a conn config or a real conn?
1477     this.conn = conn;
1478     this.useAjax = !conn || !conn.events;
1479   
1480 };
1481
1482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1483     // thse are take from connection...
1484     
1485     /**
1486      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1487      */
1488     /**
1489      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1490      * extra parameters to each request made by this object. (defaults to undefined)
1491      */
1492     /**
1493      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1494      *  to each request made by this object. (defaults to undefined)
1495      */
1496     /**
1497      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1498      */
1499     /**
1500      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1501      */
1502      /**
1503      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1504      * @type Boolean
1505      */
1506   
1507
1508     /**
1509      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1510      * @type Boolean
1511      */
1512     /**
1513      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1514      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1515      * a finer-grained basis than the DataProxy events.
1516      */
1517     getConnection : function(){
1518         return this.useAjax ? Roo.Ajax : this.conn;
1519     },
1520
1521     /**
1522      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1523      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1524      * process that block using the passed callback.
1525      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1526      * for the request to the remote server.
1527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1528      * object into a block of Roo.data.Records.
1529      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1530      * The function must be passed <ul>
1531      * <li>The Record block object</li>
1532      * <li>The "arg" argument from the load function</li>
1533      * <li>A boolean success indicator</li>
1534      * </ul>
1535      * @param {Object} scope The scope in which to call the callback
1536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1537      */
1538     load : function(params, reader, callback, scope, arg){
1539         if(this.fireEvent("beforeload", this, params) !== false){
1540             var  o = {
1541                 params : params || {},
1542                 request: {
1543                     callback : callback,
1544                     scope : scope,
1545                     arg : arg
1546                 },
1547                 reader: reader,
1548                 callback : this.loadResponse,
1549                 scope: this
1550             };
1551             if(this.useAjax){
1552                 Roo.applyIf(o, this.conn);
1553                 if(this.activeRequest){
1554                     Roo.Ajax.abort(this.activeRequest);
1555                 }
1556                 this.activeRequest = Roo.Ajax.request(o);
1557             }else{
1558                 this.conn.request(o);
1559             }
1560         }else{
1561             callback.call(scope||this, null, arg, false);
1562         }
1563     },
1564
1565     // private
1566     loadResponse : function(o, success, response){
1567         delete this.activeRequest;
1568         if(!success){
1569             this.fireEvent("loadexception", this, o, response);
1570             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1571             return;
1572         }
1573         var result;
1574         try {
1575             result = o.reader.read(response);
1576         }catch(e){
1577             o.success = false;
1578             o.raw = { errorMsg : response.responseText };
1579             this.fireEvent("loadexception", this, o, response, e);
1580             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1581             return;
1582         }
1583         
1584         this.fireEvent("load", this, o, o.request.arg);
1585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1586     },
1587
1588     // private
1589     update : function(dataSet){
1590
1591     },
1592
1593     // private
1594     updateResponse : function(dataSet){
1595
1596     }
1597 });/*
1598  * Based on:
1599  * Ext JS Library 1.1.1
1600  * Copyright(c) 2006-2007, Ext JS, LLC.
1601  *
1602  * Originally Released Under LGPL - original licence link has changed is not relivant.
1603  *
1604  * Fork - LGPL
1605  * <script type="text/javascript">
1606  */
1607
1608 /**
1609  * @class Roo.data.ScriptTagProxy
1610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1611  * other than the originating domain of the running page.<br><br>
1612  * <p>
1613  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1615  * <p>
1616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1617  * source code that is used as the source inside a &lt;script> tag.<br><br>
1618  * <p>
1619  * In order for the browser to process the returned data, the server must wrap the data object
1620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1622  * depending on whether the callback name was passed:
1623  * <p>
1624  * <pre><code>
1625 boolean scriptTag = false;
1626 String cb = request.getParameter("callback");
1627 if (cb != null) {
1628     scriptTag = true;
1629     response.setContentType("text/javascript");
1630 } else {
1631     response.setContentType("application/x-json");
1632 }
1633 Writer out = response.getWriter();
1634 if (scriptTag) {
1635     out.write(cb + "(");
1636 }
1637 out.print(dataBlock.toJsonString());
1638 if (scriptTag) {
1639     out.write(");");
1640 }
1641 </pre></code>
1642  *
1643  * @constructor
1644  * @param {Object} config A configuration object.
1645  */
1646 Roo.data.ScriptTagProxy = function(config){
1647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1648     Roo.apply(this, config);
1649     this.head = document.getElementsByTagName("head")[0];
1650 };
1651
1652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1653
1654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1655     /**
1656      * @cfg {String} url The URL from which to request the data object.
1657      */
1658     /**
1659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1660      */
1661     timeout : 30000,
1662     /**
1663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1664      * the server the name of the callback function set up by the load call to process the returned data object.
1665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1666      * javascript output which calls this named function passing the data object as its only parameter.
1667      */
1668     callbackParam : "callback",
1669     /**
1670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1671      * name to the request.
1672      */
1673     nocache : true,
1674
1675     /**
1676      * Load data from the configured URL, read the data object into
1677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1678      * process that block using the passed callback.
1679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1680      * for the request to the remote server.
1681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1682      * object into a block of Roo.data.Records.
1683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1684      * The function must be passed <ul>
1685      * <li>The Record block object</li>
1686      * <li>The "arg" argument from the load function</li>
1687      * <li>A boolean success indicator</li>
1688      * </ul>
1689      * @param {Object} scope The scope in which to call the callback
1690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1691      */
1692     load : function(params, reader, callback, scope, arg){
1693         if(this.fireEvent("beforeload", this, params) !== false){
1694
1695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1696
1697             var url = this.url;
1698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1699             if(this.nocache){
1700                 url += "&_dc=" + (new Date().getTime());
1701             }
1702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1703             var trans = {
1704                 id : transId,
1705                 cb : "stcCallback"+transId,
1706                 scriptId : "stcScript"+transId,
1707                 params : params,
1708                 arg : arg,
1709                 url : url,
1710                 callback : callback,
1711                 scope : scope,
1712                 reader : reader
1713             };
1714             var conn = this;
1715
1716             window[trans.cb] = function(o){
1717                 conn.handleResponse(o, trans);
1718             };
1719
1720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1721
1722             if(this.autoAbort !== false){
1723                 this.abort();
1724             }
1725
1726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1727
1728             var script = document.createElement("script");
1729             script.setAttribute("src", url);
1730             script.setAttribute("type", "text/javascript");
1731             script.setAttribute("id", trans.scriptId);
1732             this.head.appendChild(script);
1733
1734             this.trans = trans;
1735         }else{
1736             callback.call(scope||this, null, arg, false);
1737         }
1738     },
1739
1740     // private
1741     isLoading : function(){
1742         return this.trans ? true : false;
1743     },
1744
1745     /**
1746      * Abort the current server request.
1747      */
1748     abort : function(){
1749         if(this.isLoading()){
1750             this.destroyTrans(this.trans);
1751         }
1752     },
1753
1754     // private
1755     destroyTrans : function(trans, isLoaded){
1756         this.head.removeChild(document.getElementById(trans.scriptId));
1757         clearTimeout(trans.timeoutId);
1758         if(isLoaded){
1759             window[trans.cb] = undefined;
1760             try{
1761                 delete window[trans.cb];
1762             }catch(e){}
1763         }else{
1764             // if hasn't been loaded, wait for load to remove it to prevent script error
1765             window[trans.cb] = function(){
1766                 window[trans.cb] = undefined;
1767                 try{
1768                     delete window[trans.cb];
1769                 }catch(e){}
1770             };
1771         }
1772     },
1773
1774     // private
1775     handleResponse : function(o, trans){
1776         this.trans = false;
1777         this.destroyTrans(trans, true);
1778         var result;
1779         try {
1780             result = trans.reader.readRecords(o);
1781         }catch(e){
1782             this.fireEvent("loadexception", this, o, trans.arg, e);
1783             trans.callback.call(trans.scope||window, null, trans.arg, false);
1784             return;
1785         }
1786         this.fireEvent("load", this, o, trans.arg);
1787         trans.callback.call(trans.scope||window, result, trans.arg, true);
1788     },
1789
1790     // private
1791     handleFailure : function(trans){
1792         this.trans = false;
1793         this.destroyTrans(trans, false);
1794         this.fireEvent("loadexception", this, null, trans.arg);
1795         trans.callback.call(trans.scope||window, null, trans.arg, false);
1796     }
1797 });/*
1798  * Based on:
1799  * Ext JS Library 1.1.1
1800  * Copyright(c) 2006-2007, Ext JS, LLC.
1801  *
1802  * Originally Released Under LGPL - original licence link has changed is not relivant.
1803  *
1804  * Fork - LGPL
1805  * <script type="text/javascript">
1806  */
1807
1808 /**
1809  * @class Roo.data.JsonReader
1810  * @extends Roo.data.DataReader
1811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1812  * based on mappings in a provided Roo.data.Record constructor.
1813  * 
1814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1815  * in the reply previously. 
1816  * 
1817  * <p>
1818  * Example code:
1819  * <pre><code>
1820 var RecordDef = Roo.data.Record.create([
1821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1823 ]);
1824 var myReader = new Roo.data.JsonReader({
1825     totalProperty: "results",    // The property which contains the total dataset size (optional)
1826     root: "rows",                // The property which contains an Array of row objects
1827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1828 }, RecordDef);
1829 </code></pre>
1830  * <p>
1831  * This would consume a JSON file like this:
1832  * <pre><code>
1833 { 'results': 2, 'rows': [
1834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1836 }
1837 </code></pre>
1838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1840  * paged from the remote server.
1841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1842  * @cfg {String} root name of the property which contains the Array of row objects.
1843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1844  * @cfg {Array} fields Array of field definition objects
1845  * @constructor
1846  * Create a new JsonReader
1847  * @param {Object} meta Metadata configuration options
1848  * @param {Object} recordType Either an Array of field definition objects,
1849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1850  */
1851 Roo.data.JsonReader = function(meta, recordType){
1852     
1853     meta = meta || {};
1854     // set some defaults:
1855     Roo.applyIf(meta, {
1856         totalProperty: 'total',
1857         successProperty : 'success',
1858         root : 'data',
1859         id : 'id'
1860     });
1861     
1862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1863 };
1864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1865     
1866     readerType : 'Json',
1867     
1868     /**
1869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1870      * Used by Store query builder to append _requestMeta to params.
1871      * 
1872      */
1873     metaFromRemote : false,
1874     /**
1875      * This method is only used by a DataProxy which has retrieved data from a remote server.
1876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1877      * @return {Object} data A data block which is used by an Roo.data.Store object as
1878      * a cache of Roo.data.Records.
1879      */
1880     read : function(response){
1881         var json = response.responseText;
1882        
1883         var o = /* eval:var:o */ eval("("+json+")");
1884         if(!o) {
1885             throw {message: "JsonReader.read: Json object not found"};
1886         }
1887         
1888         if(o.metaData){
1889             
1890             delete this.ef;
1891             this.metaFromRemote = true;
1892             this.meta = o.metaData;
1893             this.recordType = Roo.data.Record.create(o.metaData.fields);
1894             this.onMetaChange(this.meta, this.recordType, o);
1895         }
1896         return this.readRecords(o);
1897     },
1898
1899     // private function a store will implement
1900     onMetaChange : function(meta, recordType, o){
1901
1902     },
1903
1904     /**
1905          * @ignore
1906          */
1907     simpleAccess: function(obj, subsc) {
1908         return obj[subsc];
1909     },
1910
1911         /**
1912          * @ignore
1913          */
1914     getJsonAccessor: function(){
1915         var re = /[\[\.]/;
1916         return function(expr) {
1917             try {
1918                 return(re.test(expr))
1919                     ? new Function("obj", "return obj." + expr)
1920                     : function(obj){
1921                         return obj[expr];
1922                     };
1923             } catch(e){}
1924             return Roo.emptyFn;
1925         };
1926     }(),
1927
1928     /**
1929      * Create a data block containing Roo.data.Records from an XML document.
1930      * @param {Object} o An object which contains an Array of row objects in the property specified
1931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1932      * which contains the total size of the dataset.
1933      * @return {Object} data A data block which is used by an Roo.data.Store object as
1934      * a cache of Roo.data.Records.
1935      */
1936     readRecords : function(o){
1937         /**
1938          * After any data loads, the raw JSON data is available for further custom processing.
1939          * @type Object
1940          */
1941         this.o = o;
1942         var s = this.meta, Record = this.recordType,
1943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1944
1945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1946         if (!this.ef) {
1947             if(s.totalProperty) {
1948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1949                 }
1950                 if(s.successProperty) {
1951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1952                 }
1953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1954                 if (s.id) {
1955                         var g = this.getJsonAccessor(s.id);
1956                         this.getId = function(rec) {
1957                                 var r = g(rec);  
1958                                 return (r === undefined || r === "") ? null : r;
1959                         };
1960                 } else {
1961                         this.getId = function(){return null;};
1962                 }
1963             this.ef = [];
1964             for(var jj = 0; jj < fl; jj++){
1965                 f = fi[jj];
1966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1967                 this.ef[jj] = this.getJsonAccessor(map);
1968             }
1969         }
1970
1971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1972         if(s.totalProperty){
1973             var vt = parseInt(this.getTotal(o), 10);
1974             if(!isNaN(vt)){
1975                 totalRecords = vt;
1976             }
1977         }
1978         if(s.successProperty){
1979             var vs = this.getSuccess(o);
1980             if(vs === false || vs === 'false'){
1981                 success = false;
1982             }
1983         }
1984         var records = [];
1985         for(var i = 0; i < c; i++){
1986             var n = root[i];
1987             var values = {};
1988             var id = this.getId(n);
1989             for(var j = 0; j < fl; j++){
1990                 f = fi[j];
1991                                 var v = this.ef[j](n);
1992                                 if (!f.convert) {
1993                                         Roo.log('missing convert for ' + f.name);
1994                                         Roo.log(f);
1995                                         continue;
1996                                 }
1997                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1998             }
1999                         if (!Record) {
2000                                 return {
2001                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2002                                         success : false,
2003                                         records : [],
2004                                         totalRecords : 0
2005                                 };
2006                         }
2007             var record = new Record(values, id);
2008             record.json = n;
2009             records[i] = record;
2010         }
2011         return {
2012             raw : o,
2013             success : success,
2014             records : records,
2015             totalRecords : totalRecords
2016         };
2017     },
2018     // used when loading children.. @see loadDataFromChildren
2019     toLoadData: function(rec)
2020     {
2021         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2022         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2023         return { data : data, total : data.length };
2024         
2025     }
2026 });/*
2027  * Based on:
2028  * Ext JS Library 1.1.1
2029  * Copyright(c) 2006-2007, Ext JS, LLC.
2030  *
2031  * Originally Released Under LGPL - original licence link has changed is not relivant.
2032  *
2033  * Fork - LGPL
2034  * <script type="text/javascript">
2035  */
2036
2037 /**
2038  * @class Roo.data.XmlReader
2039  * @extends Roo.data.DataReader
2040  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2041  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2042  * <p>
2043  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2044  * header in the HTTP response must be set to "text/xml".</em>
2045  * <p>
2046  * Example code:
2047  * <pre><code>
2048 var RecordDef = Roo.data.Record.create([
2049    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2050    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2051 ]);
2052 var myReader = new Roo.data.XmlReader({
2053    totalRecords: "results", // The element which contains the total dataset size (optional)
2054    record: "row",           // The repeated element which contains row information
2055    id: "id"                 // The element within the row that provides an ID for the record (optional)
2056 }, RecordDef);
2057 </code></pre>
2058  * <p>
2059  * This would consume an XML file like this:
2060  * <pre><code>
2061 &lt;?xml?>
2062 &lt;dataset>
2063  &lt;results>2&lt;/results>
2064  &lt;row>
2065    &lt;id>1&lt;/id>
2066    &lt;name>Bill&lt;/name>
2067    &lt;occupation>Gardener&lt;/occupation>
2068  &lt;/row>
2069  &lt;row>
2070    &lt;id>2&lt;/id>
2071    &lt;name>Ben&lt;/name>
2072    &lt;occupation>Horticulturalist&lt;/occupation>
2073  &lt;/row>
2074 &lt;/dataset>
2075 </code></pre>
2076  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2077  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2078  * paged from the remote server.
2079  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2080  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2081  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2082  * a record identifier value.
2083  * @constructor
2084  * Create a new XmlReader
2085  * @param {Object} meta Metadata configuration options
2086  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2087  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2088  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2089  */
2090 Roo.data.XmlReader = function(meta, recordType){
2091     meta = meta || {};
2092     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2093 };
2094 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2095     
2096     readerType : 'Xml',
2097     
2098     /**
2099      * This method is only used by a DataProxy which has retrieved data from a remote server.
2100          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2101          * to contain a method called 'responseXML' that returns an XML document object.
2102      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103      * a cache of Roo.data.Records.
2104      */
2105     read : function(response){
2106         var doc = response.responseXML;
2107         if(!doc) {
2108             throw {message: "XmlReader.read: XML Document not available"};
2109         }
2110         return this.readRecords(doc);
2111     },
2112
2113     /**
2114      * Create a data block containing Roo.data.Records from an XML document.
2115          * @param {Object} doc A parsed XML document.
2116      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2117      * a cache of Roo.data.Records.
2118      */
2119     readRecords : function(doc){
2120         /**
2121          * After any data loads/reads, the raw XML Document is available for further custom processing.
2122          * @type XMLDocument
2123          */
2124         this.xmlData = doc;
2125         var root = doc.documentElement || doc;
2126         var q = Roo.DomQuery;
2127         var recordType = this.recordType, fields = recordType.prototype.fields;
2128         var sid = this.meta.id;
2129         var totalRecords = 0, success = true;
2130         if(this.meta.totalRecords){
2131             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2132         }
2133         
2134         if(this.meta.success){
2135             var sv = q.selectValue(this.meta.success, root, true);
2136             success = sv !== false && sv !== 'false';
2137         }
2138         var records = [];
2139         var ns = q.select(this.meta.record, root);
2140         for(var i = 0, len = ns.length; i < len; i++) {
2141                 var n = ns[i];
2142                 var values = {};
2143                 var id = sid ? q.selectValue(sid, n) : undefined;
2144                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2145                     var f = fields.items[j];
2146                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2147                     v = f.convert(v);
2148                     values[f.name] = v;
2149                 }
2150                 var record = new recordType(values, id);
2151                 record.node = n;
2152                 records[records.length] = record;
2153             }
2154
2155             return {
2156                 success : success,
2157                 records : records,
2158                 totalRecords : totalRecords || records.length
2159             };
2160     }
2161 });/*
2162  * Based on:
2163  * Ext JS Library 1.1.1
2164  * Copyright(c) 2006-2007, Ext JS, LLC.
2165  *
2166  * Originally Released Under LGPL - original licence link has changed is not relivant.
2167  *
2168  * Fork - LGPL
2169  * <script type="text/javascript">
2170  */
2171
2172 /**
2173  * @class Roo.data.ArrayReader
2174  * @extends Roo.data.DataReader
2175  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2176  * Each element of that Array represents a row of data fields. The
2177  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2178  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2179  * <p>
2180  * Example code:.
2181  * <pre><code>
2182 var RecordDef = Roo.data.Record.create([
2183     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2184     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2185 ]);
2186 var myReader = new Roo.data.ArrayReader({
2187     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2188 }, RecordDef);
2189 </code></pre>
2190  * <p>
2191  * This would consume an Array like this:
2192  * <pre><code>
2193 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2194   </code></pre>
2195  
2196  * @constructor
2197  * Create a new JsonReader
2198  * @param {Object} meta Metadata configuration options.
2199  * @param {Object|Array} recordType Either an Array of field definition objects
2200  * 
2201  * @cfg {Array} fields Array of field definition objects
2202  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2203  * as specified to {@link Roo.data.Record#create},
2204  * or an {@link Roo.data.Record} object
2205  *
2206  * 
2207  * created using {@link Roo.data.Record#create}.
2208  */
2209 Roo.data.ArrayReader = function(meta, recordType)
2210 {    
2211     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2212 };
2213
2214 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2215     
2216       /**
2217      * Create a data block containing Roo.data.Records from an XML document.
2218      * @param {Object} o An Array of row objects which represents the dataset.
2219      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2220      * a cache of Roo.data.Records.
2221      */
2222     readRecords : function(o)
2223     {
2224         var sid = this.meta ? this.meta.id : null;
2225         var recordType = this.recordType, fields = recordType.prototype.fields;
2226         var records = [];
2227         var root = o;
2228         for(var i = 0; i < root.length; i++){
2229             var n = root[i];
2230             var values = {};
2231             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2232             for(var j = 0, jlen = fields.length; j < jlen; j++){
2233                 var f = fields.items[j];
2234                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2235                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2236                 v = f.convert(v);
2237                 values[f.name] = v;
2238             }
2239             var record = new recordType(values, id);
2240             record.json = n;
2241             records[records.length] = record;
2242         }
2243         return {
2244             records : records,
2245             totalRecords : records.length
2246         };
2247     },
2248     // used when loading children.. @see loadDataFromChildren
2249     toLoadData: function(rec)
2250     {
2251         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2252         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2253         
2254     }
2255     
2256     
2257 });/*
2258  * Based on:
2259  * Ext JS Library 1.1.1
2260  * Copyright(c) 2006-2007, Ext JS, LLC.
2261  *
2262  * Originally Released Under LGPL - original licence link has changed is not relivant.
2263  *
2264  * Fork - LGPL
2265  * <script type="text/javascript">
2266  */
2267
2268
2269 /**
2270  * @class Roo.data.Tree
2271  * @extends Roo.util.Observable
2272  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2273  * in the tree have most standard DOM functionality.
2274  * @constructor
2275  * @param {Node} root (optional) The root node
2276  */
2277 Roo.data.Tree = function(root){
2278    this.nodeHash = {};
2279    /**
2280     * The root node for this tree
2281     * @type Node
2282     */
2283    this.root = null;
2284    if(root){
2285        this.setRootNode(root);
2286    }
2287    this.addEvents({
2288        /**
2289         * @event append
2290         * Fires when a new child node is appended to a node in this tree.
2291         * @param {Tree} tree The owner tree
2292         * @param {Node} parent The parent node
2293         * @param {Node} node The newly appended node
2294         * @param {Number} index The index of the newly appended node
2295         */
2296        "append" : true,
2297        /**
2298         * @event remove
2299         * Fires when a child node is removed from a node in this tree.
2300         * @param {Tree} tree The owner tree
2301         * @param {Node} parent The parent node
2302         * @param {Node} node The child node removed
2303         */
2304        "remove" : true,
2305        /**
2306         * @event move
2307         * Fires when a node is moved to a new location in the tree
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} node The node moved
2310         * @param {Node} oldParent The old parent of this node
2311         * @param {Node} newParent The new parent of this node
2312         * @param {Number} index The index it was moved to
2313         */
2314        "move" : true,
2315        /**
2316         * @event insert
2317         * Fires when a new child node is inserted in a node in this tree.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} parent The parent node
2320         * @param {Node} node The child node inserted
2321         * @param {Node} refNode The child node the node was inserted before
2322         */
2323        "insert" : true,
2324        /**
2325         * @event beforeappend
2326         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2327         * @param {Tree} tree The owner tree
2328         * @param {Node} parent The parent node
2329         * @param {Node} node The child node to be appended
2330         */
2331        "beforeappend" : true,
2332        /**
2333         * @event beforeremove
2334         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2335         * @param {Tree} tree The owner tree
2336         * @param {Node} parent The parent node
2337         * @param {Node} node The child node to be removed
2338         */
2339        "beforeremove" : true,
2340        /**
2341         * @event beforemove
2342         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} node The node being moved
2345         * @param {Node} oldParent The parent of the node
2346         * @param {Node} newParent The new parent the node is moving to
2347         * @param {Number} index The index it is being moved to
2348         */
2349        "beforemove" : true,
2350        /**
2351         * @event beforeinsert
2352         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2353         * @param {Tree} tree The owner tree
2354         * @param {Node} parent The parent node
2355         * @param {Node} node The child node to be inserted
2356         * @param {Node} refNode The child node the node is being inserted before
2357         */
2358        "beforeinsert" : true
2359    });
2360
2361     Roo.data.Tree.superclass.constructor.call(this);
2362 };
2363
2364 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2365     pathSeparator: "/",
2366
2367     proxyNodeEvent : function(){
2368         return this.fireEvent.apply(this, arguments);
2369     },
2370
2371     /**
2372      * Returns the root node for this tree.
2373      * @return {Node}
2374      */
2375     getRootNode : function(){
2376         return this.root;
2377     },
2378
2379     /**
2380      * Sets the root node for this tree.
2381      * @param {Node} node
2382      * @return {Node}
2383      */
2384     setRootNode : function(node){
2385         this.root = node;
2386         node.ownerTree = this;
2387         node.isRoot = true;
2388         this.registerNode(node);
2389         return node;
2390     },
2391
2392     /**
2393      * Gets a node in this tree by its id.
2394      * @param {String} id
2395      * @return {Node}
2396      */
2397     getNodeById : function(id){
2398         return this.nodeHash[id];
2399     },
2400
2401     registerNode : function(node){
2402         this.nodeHash[node.id] = node;
2403     },
2404
2405     unregisterNode : function(node){
2406         delete this.nodeHash[node.id];
2407     },
2408
2409     toString : function(){
2410         return "[Tree"+(this.id?" "+this.id:"")+"]";
2411     }
2412 });
2413
2414 /**
2415  * @class Roo.data.Node
2416  * @extends Roo.util.Observable
2417  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2418  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2419  * @constructor
2420  * @param {Object} attributes The attributes/config for the node
2421  */
2422 Roo.data.Node = function(attributes){
2423     /**
2424      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2425      * @type {Object}
2426      */
2427     this.attributes = attributes || {};
2428     this.leaf = this.attributes.leaf;
2429     /**
2430      * The node id. @type String
2431      */
2432     this.id = this.attributes.id;
2433     if(!this.id){
2434         this.id = Roo.id(null, "ynode-");
2435         this.attributes.id = this.id;
2436     }
2437      
2438     
2439     /**
2440      * All child nodes of this node. @type Array
2441      */
2442     this.childNodes = [];
2443     if(!this.childNodes.indexOf){ // indexOf is a must
2444         this.childNodes.indexOf = function(o){
2445             for(var i = 0, len = this.length; i < len; i++){
2446                 if(this[i] == o) {
2447                     return i;
2448                 }
2449             }
2450             return -1;
2451         };
2452     }
2453     /**
2454      * The parent node for this node. @type Node
2455      */
2456     this.parentNode = null;
2457     /**
2458      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2459      */
2460     this.firstChild = null;
2461     /**
2462      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2463      */
2464     this.lastChild = null;
2465     /**
2466      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2467      */
2468     this.previousSibling = null;
2469     /**
2470      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2471      */
2472     this.nextSibling = null;
2473
2474     this.addEvents({
2475        /**
2476         * @event append
2477         * Fires when a new child node is appended
2478         * @param {Tree} tree The owner tree
2479         * @param {Node} this This node
2480         * @param {Node} node The newly appended node
2481         * @param {Number} index The index of the newly appended node
2482         */
2483        "append" : true,
2484        /**
2485         * @event remove
2486         * Fires when a child node is removed
2487         * @param {Tree} tree The owner tree
2488         * @param {Node} this This node
2489         * @param {Node} node The removed node
2490         */
2491        "remove" : true,
2492        /**
2493         * @event move
2494         * Fires when this node is moved to a new location in the tree
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} oldParent The old parent of this node
2498         * @param {Node} newParent The new parent of this node
2499         * @param {Number} index The index it was moved to
2500         */
2501        "move" : true,
2502        /**
2503         * @event insert
2504         * Fires when a new child node is inserted.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} node The child node inserted
2508         * @param {Node} refNode The child node the node was inserted before
2509         */
2510        "insert" : true,
2511        /**
2512         * @event beforeappend
2513         * Fires before a new child is appended, return false to cancel the append.
2514         * @param {Tree} tree The owner tree
2515         * @param {Node} this This node
2516         * @param {Node} node The child node to be appended
2517         */
2518        "beforeappend" : true,
2519        /**
2520         * @event beforeremove
2521         * Fires before a child is removed, return false to cancel the remove.
2522         * @param {Tree} tree The owner tree
2523         * @param {Node} this This node
2524         * @param {Node} node The child node to be removed
2525         */
2526        "beforeremove" : true,
2527        /**
2528         * @event beforemove
2529         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} oldParent The parent of this node
2533         * @param {Node} newParent The new parent this node is moving to
2534         * @param {Number} index The index it is being moved to
2535         */
2536        "beforemove" : true,
2537        /**
2538         * @event beforeinsert
2539         * Fires before a new child is inserted, return false to cancel the insert.
2540         * @param {Tree} tree The owner tree
2541         * @param {Node} this This node
2542         * @param {Node} node The child node to be inserted
2543         * @param {Node} refNode The child node the node is being inserted before
2544         */
2545        "beforeinsert" : true
2546    });
2547     this.listeners = this.attributes.listeners;
2548     Roo.data.Node.superclass.constructor.call(this);
2549 };
2550
2551 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2552     fireEvent : function(evtName){
2553         // first do standard event for this node
2554         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2555             return false;
2556         }
2557         // then bubble it up to the tree if the event wasn't cancelled
2558         var ot = this.getOwnerTree();
2559         if(ot){
2560             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2561                 return false;
2562             }
2563         }
2564         return true;
2565     },
2566
2567     /**
2568      * Returns true if this node is a leaf
2569      * @return {Boolean}
2570      */
2571     isLeaf : function(){
2572         return this.leaf === true;
2573     },
2574
2575     // private
2576     setFirstChild : function(node){
2577         this.firstChild = node;
2578     },
2579
2580     //private
2581     setLastChild : function(node){
2582         this.lastChild = node;
2583     },
2584
2585
2586     /**
2587      * Returns true if this node is the last child of its parent
2588      * @return {Boolean}
2589      */
2590     isLast : function(){
2591        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2592     },
2593
2594     /**
2595      * Returns true if this node is the first child of its parent
2596      * @return {Boolean}
2597      */
2598     isFirst : function(){
2599        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2600     },
2601
2602     hasChildNodes : function(){
2603         return !this.isLeaf() && this.childNodes.length > 0;
2604     },
2605
2606     /**
2607      * Insert node(s) as the last child node of this node.
2608      * @param {Node/Array} node The node or Array of nodes to append
2609      * @return {Node} The appended node if single append, or null if an array was passed
2610      */
2611     appendChild : function(node){
2612         var multi = false;
2613         if(node instanceof Array){
2614             multi = node;
2615         }else if(arguments.length > 1){
2616             multi = arguments;
2617         }
2618         
2619         // if passed an array or multiple args do them one by one
2620         if(multi){
2621             for(var i = 0, len = multi.length; i < len; i++) {
2622                 this.appendChild(multi[i]);
2623             }
2624         }else{
2625             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2626                 return false;
2627             }
2628             var index = this.childNodes.length;
2629             var oldParent = node.parentNode;
2630             // it's a move, make sure we move it cleanly
2631             if(oldParent){
2632                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2633                     return false;
2634                 }
2635                 oldParent.removeChild(node);
2636             }
2637             
2638             index = this.childNodes.length;
2639             if(index == 0){
2640                 this.setFirstChild(node);
2641             }
2642             this.childNodes.push(node);
2643             node.parentNode = this;
2644             var ps = this.childNodes[index-1];
2645             if(ps){
2646                 node.previousSibling = ps;
2647                 ps.nextSibling = node;
2648             }else{
2649                 node.previousSibling = null;
2650             }
2651             node.nextSibling = null;
2652             this.setLastChild(node);
2653             node.setOwnerTree(this.getOwnerTree());
2654             this.fireEvent("append", this.ownerTree, this, node, index);
2655             if(this.ownerTree) {
2656                 this.ownerTree.fireEvent("appendnode", this, node, index);
2657             }
2658             if(oldParent){
2659                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2660             }
2661             return node;
2662         }
2663     },
2664
2665     /**
2666      * Removes a child node from this node.
2667      * @param {Node} node The node to remove
2668      * @return {Node} The removed node
2669      */
2670     removeChild : function(node){
2671         var index = this.childNodes.indexOf(node);
2672         if(index == -1){
2673             return false;
2674         }
2675         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2676             return false;
2677         }
2678
2679         // remove it from childNodes collection
2680         this.childNodes.splice(index, 1);
2681
2682         // update siblings
2683         if(node.previousSibling){
2684             node.previousSibling.nextSibling = node.nextSibling;
2685         }
2686         if(node.nextSibling){
2687             node.nextSibling.previousSibling = node.previousSibling;
2688         }
2689
2690         // update child refs
2691         if(this.firstChild == node){
2692             this.setFirstChild(node.nextSibling);
2693         }
2694         if(this.lastChild == node){
2695             this.setLastChild(node.previousSibling);
2696         }
2697
2698         node.setOwnerTree(null);
2699         // clear any references from the node
2700         node.parentNode = null;
2701         node.previousSibling = null;
2702         node.nextSibling = null;
2703         this.fireEvent("remove", this.ownerTree, this, node);
2704         return node;
2705     },
2706
2707     /**
2708      * Inserts the first node before the second node in this nodes childNodes collection.
2709      * @param {Node} node The node to insert
2710      * @param {Node} refNode The node to insert before (if null the node is appended)
2711      * @return {Node} The inserted node
2712      */
2713     insertBefore : function(node, refNode){
2714         if(!refNode){ // like standard Dom, refNode can be null for append
2715             return this.appendChild(node);
2716         }
2717         // nothing to do
2718         if(node == refNode){
2719             return false;
2720         }
2721
2722         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2723             return false;
2724         }
2725         var index = this.childNodes.indexOf(refNode);
2726         var oldParent = node.parentNode;
2727         var refIndex = index;
2728
2729         // when moving internally, indexes will change after remove
2730         if(oldParent == this && this.childNodes.indexOf(node) < index){
2731             refIndex--;
2732         }
2733
2734         // it's a move, make sure we move it cleanly
2735         if(oldParent){
2736             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2737                 return false;
2738             }
2739             oldParent.removeChild(node);
2740         }
2741         if(refIndex == 0){
2742             this.setFirstChild(node);
2743         }
2744         this.childNodes.splice(refIndex, 0, node);
2745         node.parentNode = this;
2746         var ps = this.childNodes[refIndex-1];
2747         if(ps){
2748             node.previousSibling = ps;
2749             ps.nextSibling = node;
2750         }else{
2751             node.previousSibling = null;
2752         }
2753         node.nextSibling = refNode;
2754         refNode.previousSibling = node;
2755         node.setOwnerTree(this.getOwnerTree());
2756         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2757         if(oldParent){
2758             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2759         }
2760         return node;
2761     },
2762
2763     /**
2764      * Returns the child node at the specified index.
2765      * @param {Number} index
2766      * @return {Node}
2767      */
2768     item : function(index){
2769         return this.childNodes[index];
2770     },
2771
2772     /**
2773      * Replaces one child node in this node with another.
2774      * @param {Node} newChild The replacement node
2775      * @param {Node} oldChild The node to replace
2776      * @return {Node} The replaced node
2777      */
2778     replaceChild : function(newChild, oldChild){
2779         this.insertBefore(newChild, oldChild);
2780         this.removeChild(oldChild);
2781         return oldChild;
2782     },
2783
2784     /**
2785      * Returns the index of a child node
2786      * @param {Node} node
2787      * @return {Number} The index of the node or -1 if it was not found
2788      */
2789     indexOf : function(child){
2790         return this.childNodes.indexOf(child);
2791     },
2792
2793     /**
2794      * Returns the tree this node is in.
2795      * @return {Tree}
2796      */
2797     getOwnerTree : function(){
2798         // if it doesn't have one, look for one
2799         if(!this.ownerTree){
2800             var p = this;
2801             while(p){
2802                 if(p.ownerTree){
2803                     this.ownerTree = p.ownerTree;
2804                     break;
2805                 }
2806                 p = p.parentNode;
2807             }
2808         }
2809         return this.ownerTree;
2810     },
2811
2812     /**
2813      * Returns depth of this node (the root node has a depth of 0)
2814      * @return {Number}
2815      */
2816     getDepth : function(){
2817         var depth = 0;
2818         var p = this;
2819         while(p.parentNode){
2820             ++depth;
2821             p = p.parentNode;
2822         }
2823         return depth;
2824     },
2825
2826     // private
2827     setOwnerTree : function(tree){
2828         // if it's move, we need to update everyone
2829         if(tree != this.ownerTree){
2830             if(this.ownerTree){
2831                 this.ownerTree.unregisterNode(this);
2832             }
2833             this.ownerTree = tree;
2834             var cs = this.childNodes;
2835             for(var i = 0, len = cs.length; i < len; i++) {
2836                 cs[i].setOwnerTree(tree);
2837             }
2838             if(tree){
2839                 tree.registerNode(this);
2840             }
2841         }
2842     },
2843
2844     /**
2845      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2846      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2847      * @return {String} The path
2848      */
2849     getPath : function(attr){
2850         attr = attr || "id";
2851         var p = this.parentNode;
2852         var b = [this.attributes[attr]];
2853         while(p){
2854             b.unshift(p.attributes[attr]);
2855             p = p.parentNode;
2856         }
2857         var sep = this.getOwnerTree().pathSeparator;
2858         return sep + b.join(sep);
2859     },
2860
2861     /**
2862      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863      * function call will be the scope provided or the current node. The arguments to the function
2864      * will be the args provided or the current node. If the function returns false at any point,
2865      * the bubble is stopped.
2866      * @param {Function} fn The function to call
2867      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2869      */
2870     bubble : function(fn, scope, args){
2871         var p = this;
2872         while(p){
2873             if(fn.call(scope || p, args || p) === false){
2874                 break;
2875             }
2876             p = p.parentNode;
2877         }
2878     },
2879
2880     /**
2881      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2882      * function call will be the scope provided or the current node. The arguments to the function
2883      * will be the args provided or the current node. If the function returns false at any point,
2884      * the cascade is stopped on that branch.
2885      * @param {Function} fn The function to call
2886      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2887      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888      */
2889     cascade : function(fn, scope, args){
2890         if(fn.call(scope || this, args || this) !== false){
2891             var cs = this.childNodes;
2892             for(var i = 0, len = cs.length; i < len; i++) {
2893                 cs[i].cascade(fn, scope, args);
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2900      * function call will be the scope provided or the current node. The arguments to the function
2901      * will be the args provided or the current node. If the function returns false at any point,
2902      * the iteration stops.
2903      * @param {Function} fn The function to call
2904      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2905      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2906      */
2907     eachChild : function(fn, scope, args){
2908         var cs = this.childNodes;
2909         for(var i = 0, len = cs.length; i < len; i++) {
2910                 if(fn.call(scope || this, args || cs[i]) === false){
2911                     break;
2912                 }
2913         }
2914     },
2915
2916     /**
2917      * Finds the first child that has the attribute with the specified value.
2918      * @param {String} attribute The attribute name
2919      * @param {Mixed} value The value to search for
2920      * @return {Node} The found child or null if none was found
2921      */
2922     findChild : function(attribute, value){
2923         var cs = this.childNodes;
2924         for(var i = 0, len = cs.length; i < len; i++) {
2925                 if(cs[i].attributes[attribute] == value){
2926                     return cs[i];
2927                 }
2928         }
2929         return null;
2930     },
2931
2932     /**
2933      * Finds the first child by a custom function. The child matches if the function passed
2934      * returns true.
2935      * @param {Function} fn
2936      * @param {Object} scope (optional)
2937      * @return {Node} The found child or null if none was found
2938      */
2939     findChildBy : function(fn, scope){
2940         var cs = this.childNodes;
2941         for(var i = 0, len = cs.length; i < len; i++) {
2942                 if(fn.call(scope||cs[i], cs[i]) === true){
2943                     return cs[i];
2944                 }
2945         }
2946         return null;
2947     },
2948
2949     /**
2950      * Sorts this nodes children using the supplied sort function
2951      * @param {Function} fn
2952      * @param {Object} scope (optional)
2953      */
2954     sort : function(fn, scope){
2955         var cs = this.childNodes;
2956         var len = cs.length;
2957         if(len > 0){
2958             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2959             cs.sort(sortFn);
2960             for(var i = 0; i < len; i++){
2961                 var n = cs[i];
2962                 n.previousSibling = cs[i-1];
2963                 n.nextSibling = cs[i+1];
2964                 if(i == 0){
2965                     this.setFirstChild(n);
2966                 }
2967                 if(i == len-1){
2968                     this.setLastChild(n);
2969                 }
2970             }
2971         }
2972     },
2973
2974     /**
2975      * Returns true if this node is an ancestor (at any point) of the passed node.
2976      * @param {Node} node
2977      * @return {Boolean}
2978      */
2979     contains : function(node){
2980         return node.isAncestor(this);
2981     },
2982
2983     /**
2984      * Returns true if the passed node is an ancestor (at any point) of this node.
2985      * @param {Node} node
2986      * @return {Boolean}
2987      */
2988     isAncestor : function(node){
2989         var p = this.parentNode;
2990         while(p){
2991             if(p == node){
2992                 return true;
2993             }
2994             p = p.parentNode;
2995         }
2996         return false;
2997     },
2998
2999     toString : function(){
3000         return "[Node"+(this.id?" "+this.id:"")+"]";
3001     }
3002 });/*
3003  * Based on:
3004  * Ext JS Library 1.1.1
3005  * Copyright(c) 2006-2007, Ext JS, LLC.
3006  *
3007  * Originally Released Under LGPL - original licence link has changed is not relivant.
3008  *
3009  * Fork - LGPL
3010  * <script type="text/javascript">
3011  */
3012
3013
3014 /**
3015  * @class Roo.Shadow
3016  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3017  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3018  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3019  * @constructor
3020  * Create a new Shadow
3021  * @param {Object} config The config object
3022  */
3023 Roo.Shadow = function(config){
3024     Roo.apply(this, config);
3025     if(typeof this.mode != "string"){
3026         this.mode = this.defaultMode;
3027     }
3028     var o = this.offset, a = {h: 0};
3029     var rad = Math.floor(this.offset/2);
3030     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3031         case "drop":
3032             a.w = 0;
3033             a.l = a.t = o;
3034             a.t -= 1;
3035             if(Roo.isIE){
3036                 a.l -= this.offset + rad;
3037                 a.t -= this.offset + rad;
3038                 a.w -= rad;
3039                 a.h -= rad;
3040                 a.t += 1;
3041             }
3042         break;
3043         case "sides":
3044             a.w = (o*2);
3045             a.l = -o;
3046             a.t = o-1;
3047             if(Roo.isIE){
3048                 a.l -= (this.offset - rad);
3049                 a.t -= this.offset + rad;
3050                 a.l += 1;
3051                 a.w -= (this.offset - rad)*2;
3052                 a.w -= rad + 1;
3053                 a.h -= 1;
3054             }
3055         break;
3056         case "frame":
3057             a.w = a.h = (o*2);
3058             a.l = a.t = -o;
3059             a.t += 1;
3060             a.h -= 2;
3061             if(Roo.isIE){
3062                 a.l -= (this.offset - rad);
3063                 a.t -= (this.offset - rad);
3064                 a.l += 1;
3065                 a.w -= (this.offset + rad + 1);
3066                 a.h -= (this.offset + rad);
3067                 a.h += 1;
3068             }
3069         break;
3070     };
3071
3072     this.adjusts = a;
3073 };
3074
3075 Roo.Shadow.prototype = {
3076     /**
3077      * @cfg {String} mode
3078      * The shadow display mode.  Supports the following options:<br />
3079      * sides: Shadow displays on both sides and bottom only<br />
3080      * frame: Shadow displays equally on all four sides<br />
3081      * drop: Traditional bottom-right drop shadow (default)
3082      */
3083     mode: false,
3084     /**
3085      * @cfg {String} offset
3086      * The number of pixels to offset the shadow from the element (defaults to 4)
3087      */
3088     offset: 4,
3089
3090     // private
3091     defaultMode: "drop",
3092
3093     /**
3094      * Displays the shadow under the target element
3095      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3096      */
3097     show : function(target){
3098         target = Roo.get(target);
3099         if(!this.el){
3100             this.el = Roo.Shadow.Pool.pull();
3101             if(this.el.dom.nextSibling != target.dom){
3102                 this.el.insertBefore(target);
3103             }
3104         }
3105         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3106         if(Roo.isIE){
3107             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3108         }
3109         this.realign(
3110             target.getLeft(true),
3111             target.getTop(true),
3112             target.getWidth(),
3113             target.getHeight()
3114         );
3115         this.el.dom.style.display = "block";
3116     },
3117
3118     /**
3119      * Returns true if the shadow is visible, else false
3120      */
3121     isVisible : function(){
3122         return this.el ? true : false;  
3123     },
3124
3125     /**
3126      * Direct alignment when values are already available. Show must be called at least once before
3127      * calling this method to ensure it is initialized.
3128      * @param {Number} left The target element left position
3129      * @param {Number} top The target element top position
3130      * @param {Number} width The target element width
3131      * @param {Number} height The target element height
3132      */
3133     realign : function(l, t, w, h){
3134         if(!this.el){
3135             return;
3136         }
3137         var a = this.adjusts, d = this.el.dom, s = d.style;
3138         var iea = 0;
3139         s.left = (l+a.l)+"px";
3140         s.top = (t+a.t)+"px";
3141         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3142  
3143         if(s.width != sws || s.height != shs){
3144             s.width = sws;
3145             s.height = shs;
3146             if(!Roo.isIE){
3147                 var cn = d.childNodes;
3148                 var sww = Math.max(0, (sw-12))+"px";
3149                 cn[0].childNodes[1].style.width = sww;
3150                 cn[1].childNodes[1].style.width = sww;
3151                 cn[2].childNodes[1].style.width = sww;
3152                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3153             }
3154         }
3155     },
3156
3157     /**
3158      * Hides this shadow
3159      */
3160     hide : function(){
3161         if(this.el){
3162             this.el.dom.style.display = "none";
3163             Roo.Shadow.Pool.push(this.el);
3164             delete this.el;
3165         }
3166     },
3167
3168     /**
3169      * Adjust the z-index of this shadow
3170      * @param {Number} zindex The new z-index
3171      */
3172     setZIndex : function(z){
3173         this.zIndex = z;
3174         if(this.el){
3175             this.el.setStyle("z-index", z);
3176         }
3177     }
3178 };
3179
3180 // Private utility class that manages the internal Shadow cache
3181 Roo.Shadow.Pool = function(){
3182     var p = [];
3183     var markup = Roo.isIE ?
3184                  '<div class="x-ie-shadow"></div>' :
3185                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3186     return {
3187         pull : function(){
3188             var sh = p.shift();
3189             if(!sh){
3190                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3191                 sh.autoBoxAdjust = false;
3192             }
3193             return sh;
3194         },
3195
3196         push : function(sh){
3197             p.push(sh);
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210
3211
3212 /**
3213  * @class Roo.SplitBar
3214  * @extends Roo.util.Observable
3215  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3216  * <br><br>
3217  * Usage:
3218  * <pre><code>
3219 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3220                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3221 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3222 split.minSize = 100;
3223 split.maxSize = 600;
3224 split.animate = true;
3225 split.on('moved', splitterMoved);
3226 </code></pre>
3227  * @constructor
3228  * Create a new SplitBar
3229  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3230  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3231  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3232  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3233                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3234                         position of the SplitBar).
3235  */
3236 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3237     
3238     /** @private */
3239     this.el = Roo.get(dragElement, true);
3240     this.el.dom.unselectable = "on";
3241     /** @private */
3242     this.resizingEl = Roo.get(resizingElement, true);
3243
3244     /**
3245      * @private
3246      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3247      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3248      * @type Number
3249      */
3250     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3251     
3252     /**
3253      * The minimum size of the resizing element. (Defaults to 0)
3254      * @type Number
3255      */
3256     this.minSize = 0;
3257     
3258     /**
3259      * The maximum size of the resizing element. (Defaults to 2000)
3260      * @type Number
3261      */
3262     this.maxSize = 2000;
3263     
3264     /**
3265      * Whether to animate the transition to the new size
3266      * @type Boolean
3267      */
3268     this.animate = false;
3269     
3270     /**
3271      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3272      * @type Boolean
3273      */
3274     this.useShim = false;
3275     
3276     /** @private */
3277     this.shim = null;
3278     
3279     if(!existingProxy){
3280         /** @private */
3281         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3282     }else{
3283         this.proxy = Roo.get(existingProxy).dom;
3284     }
3285     /** @private */
3286     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3287     
3288     /** @private */
3289     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3290     
3291     /** @private */
3292     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3293     
3294     /** @private */
3295     this.dragSpecs = {};
3296     
3297     /**
3298      * @private The adapter to use to positon and resize elements
3299      */
3300     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3301     this.adapter.init(this);
3302     
3303     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3304         /** @private */
3305         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3306         this.el.addClass("x-splitbar-h");
3307     }else{
3308         /** @private */
3309         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3310         this.el.addClass("x-splitbar-v");
3311     }
3312     
3313     this.addEvents({
3314         /**
3315          * @event resize
3316          * Fires when the splitter is moved (alias for {@link #event-moved})
3317          * @param {Roo.SplitBar} this
3318          * @param {Number} newSize the new width or height
3319          */
3320         "resize" : true,
3321         /**
3322          * @event moved
3323          * Fires when the splitter is moved
3324          * @param {Roo.SplitBar} this
3325          * @param {Number} newSize the new width or height
3326          */
3327         "moved" : true,
3328         /**
3329          * @event beforeresize
3330          * Fires before the splitter is dragged
3331          * @param {Roo.SplitBar} this
3332          */
3333         "beforeresize" : true,
3334
3335         "beforeapply" : true
3336     });
3337
3338     Roo.util.Observable.call(this);
3339 };
3340
3341 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3342     onStartProxyDrag : function(x, y){
3343         this.fireEvent("beforeresize", this);
3344         if(!this.overlay){
3345             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3346             o.unselectable();
3347             o.enableDisplayMode("block");
3348             // all splitbars share the same overlay
3349             Roo.SplitBar.prototype.overlay = o;
3350         }
3351         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3352         this.overlay.show();
3353         Roo.get(this.proxy).setDisplayed("block");
3354         var size = this.adapter.getElementSize(this);
3355         this.activeMinSize = this.getMinimumSize();;
3356         this.activeMaxSize = this.getMaximumSize();;
3357         var c1 = size - this.activeMinSize;
3358         var c2 = Math.max(this.activeMaxSize - size, 0);
3359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3360             this.dd.resetConstraints();
3361             this.dd.setXConstraint(
3362                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3363                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3364             );
3365             this.dd.setYConstraint(0, 0);
3366         }else{
3367             this.dd.resetConstraints();
3368             this.dd.setXConstraint(0, 0);
3369             this.dd.setYConstraint(
3370                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3371                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3372             );
3373          }
3374         this.dragSpecs.startSize = size;
3375         this.dragSpecs.startPoint = [x, y];
3376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3377     },
3378     
3379     /** 
3380      * @private Called after the drag operation by the DDProxy
3381      */
3382     onEndProxyDrag : function(e){
3383         Roo.get(this.proxy).setDisplayed(false);
3384         var endPoint = Roo.lib.Event.getXY(e);
3385         if(this.overlay){
3386             this.overlay.hide();
3387         }
3388         var newSize;
3389         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3390             newSize = this.dragSpecs.startSize + 
3391                 (this.placement == Roo.SplitBar.LEFT ?
3392                     endPoint[0] - this.dragSpecs.startPoint[0] :
3393                     this.dragSpecs.startPoint[0] - endPoint[0]
3394                 );
3395         }else{
3396             newSize = this.dragSpecs.startSize + 
3397                 (this.placement == Roo.SplitBar.TOP ?
3398                     endPoint[1] - this.dragSpecs.startPoint[1] :
3399                     this.dragSpecs.startPoint[1] - endPoint[1]
3400                 );
3401         }
3402         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3403         if(newSize != this.dragSpecs.startSize){
3404             if(this.fireEvent('beforeapply', this, newSize) !== false){
3405                 this.adapter.setElementSize(this, newSize);
3406                 this.fireEvent("moved", this, newSize);
3407                 this.fireEvent("resize", this, newSize);
3408             }
3409         }
3410     },
3411     
3412     /**
3413      * Get the adapter this SplitBar uses
3414      * @return The adapter object
3415      */
3416     getAdapter : function(){
3417         return this.adapter;
3418     },
3419     
3420     /**
3421      * Set the adapter this SplitBar uses
3422      * @param {Object} adapter A SplitBar adapter object
3423      */
3424     setAdapter : function(adapter){
3425         this.adapter = adapter;
3426         this.adapter.init(this);
3427     },
3428     
3429     /**
3430      * Gets the minimum size for the resizing element
3431      * @return {Number} The minimum size
3432      */
3433     getMinimumSize : function(){
3434         return this.minSize;
3435     },
3436     
3437     /**
3438      * Sets the minimum size for the resizing element
3439      * @param {Number} minSize The minimum size
3440      */
3441     setMinimumSize : function(minSize){
3442         this.minSize = minSize;
3443     },
3444     
3445     /**
3446      * Gets the maximum size for the resizing element
3447      * @return {Number} The maximum size
3448      */
3449     getMaximumSize : function(){
3450         return this.maxSize;
3451     },
3452     
3453     /**
3454      * Sets the maximum size for the resizing element
3455      * @param {Number} maxSize The maximum size
3456      */
3457     setMaximumSize : function(maxSize){
3458         this.maxSize = maxSize;
3459     },
3460     
3461     /**
3462      * Sets the initialize size for the resizing element
3463      * @param {Number} size The initial size
3464      */
3465     setCurrentSize : function(size){
3466         var oldAnimate = this.animate;
3467         this.animate = false;
3468         this.adapter.setElementSize(this, size);
3469         this.animate = oldAnimate;
3470     },
3471     
3472     /**
3473      * Destroy this splitbar. 
3474      * @param {Boolean} removeEl True to remove the element
3475      */
3476     destroy : function(removeEl){
3477         if(this.shim){
3478             this.shim.remove();
3479         }
3480         this.dd.unreg();
3481         this.proxy.parentNode.removeChild(this.proxy);
3482         if(removeEl){
3483             this.el.remove();
3484         }
3485     }
3486 });
3487
3488 /**
3489  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3490  */
3491 Roo.SplitBar.createProxy = function(dir){
3492     var proxy = new Roo.Element(document.createElement("div"));
3493     proxy.unselectable();
3494     var cls = 'x-splitbar-proxy';
3495     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3496     document.body.appendChild(proxy.dom);
3497     return proxy.dom;
3498 };
3499
3500 /** 
3501  * @class Roo.SplitBar.BasicLayoutAdapter
3502  * Default Adapter. It assumes the splitter and resizing element are not positioned
3503  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3504  */
3505 Roo.SplitBar.BasicLayoutAdapter = function(){
3506 };
3507
3508 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3509     // do nothing for now
3510     init : function(s){
3511     
3512     },
3513     /**
3514      * Called before drag operations to get the current size of the resizing element. 
3515      * @param {Roo.SplitBar} s The SplitBar using this adapter
3516      */
3517      getElementSize : function(s){
3518         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3519             return s.resizingEl.getWidth();
3520         }else{
3521             return s.resizingEl.getHeight();
3522         }
3523     },
3524     
3525     /**
3526      * Called after drag operations to set the size of the resizing element.
3527      * @param {Roo.SplitBar} s The SplitBar using this adapter
3528      * @param {Number} newSize The new size to set
3529      * @param {Function} onComplete A function to be invoked when resizing is complete
3530      */
3531     setElementSize : function(s, newSize, onComplete){
3532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3533             if(!s.animate){
3534                 s.resizingEl.setWidth(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }else{
3542             
3543             if(!s.animate){
3544                 s.resizingEl.setHeight(newSize);
3545                 if(onComplete){
3546                     onComplete(s, newSize);
3547                 }
3548             }else{
3549                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3550             }
3551         }
3552     }
3553 };
3554
3555 /** 
3556  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3557  * @extends Roo.SplitBar.BasicLayoutAdapter
3558  * Adapter that  moves the splitter element to align with the resized sizing element. 
3559  * Used with an absolute positioned SplitBar.
3560  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3561  * document.body, make sure you assign an id to the body element.
3562  */
3563 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3564     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3565     this.container = Roo.get(container);
3566 };
3567
3568 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3569     init : function(s){
3570         this.basic.init(s);
3571     },
3572     
3573     getElementSize : function(s){
3574         return this.basic.getElementSize(s);
3575     },
3576     
3577     setElementSize : function(s, newSize, onComplete){
3578         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3579     },
3580     
3581     moveSplitter : function(s){
3582         var yes = Roo.SplitBar;
3583         switch(s.placement){
3584             case yes.LEFT:
3585                 s.el.setX(s.resizingEl.getRight());
3586                 break;
3587             case yes.RIGHT:
3588                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3589                 break;
3590             case yes.TOP:
3591                 s.el.setY(s.resizingEl.getBottom());
3592                 break;
3593             case yes.BOTTOM:
3594                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3595                 break;
3596         }
3597     }
3598 };
3599
3600 /**
3601  * Orientation constant - Create a vertical SplitBar
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.VERTICAL = 1;
3606
3607 /**
3608  * Orientation constant - Create a horizontal SplitBar
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.HORIZONTAL = 2;
3613
3614 /**
3615  * Placement constant - The resizing element is to the left of the splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.LEFT = 1;
3620
3621 /**
3622  * Placement constant - The resizing element is to the right of the splitter element
3623  * @static
3624  * @type Number
3625  */
3626 Roo.SplitBar.RIGHT = 2;
3627
3628 /**
3629  * Placement constant - The resizing element is positioned above the splitter element
3630  * @static
3631  * @type Number
3632  */
3633 Roo.SplitBar.TOP = 3;
3634
3635 /**
3636  * Placement constant - The resizing element is positioned under splitter element
3637  * @static
3638  * @type Number
3639  */
3640 Roo.SplitBar.BOTTOM = 4;
3641 /*
3642  * Based on:
3643  * Ext JS Library 1.1.1
3644  * Copyright(c) 2006-2007, Ext JS, LLC.
3645  *
3646  * Originally Released Under LGPL - original licence link has changed is not relivant.
3647  *
3648  * Fork - LGPL
3649  * <script type="text/javascript">
3650  */
3651
3652 /**
3653  * @class Roo.View
3654  * @extends Roo.util.Observable
3655  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3656  * This class also supports single and multi selection modes. <br>
3657  * Create a data model bound view:
3658  <pre><code>
3659  var store = new Roo.data.Store(...);
3660
3661  var view = new Roo.View({
3662     el : "my-element",
3663     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3664  
3665     singleSelect: true,
3666     selectedClass: "ydataview-selected",
3667     store: store
3668  });
3669
3670  // listen for node click?
3671  view.on("click", function(vw, index, node, e){
3672  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3673  });
3674
3675  // load XML data
3676  dataModel.load("foobar.xml");
3677  </code></pre>
3678  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3679  * <br><br>
3680  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3681  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3682  * 
3683  * Note: old style constructor is still suported (container, template, config)
3684  * 
3685  * @constructor
3686  * Create a new View
3687  * @param {Object} config The config object
3688  * 
3689  */
3690 Roo.View = function(config, depreciated_tpl, depreciated_config){
3691     
3692     this.parent = false;
3693     
3694     if (typeof(depreciated_tpl) == 'undefined') {
3695         // new way.. - universal constructor.
3696         Roo.apply(this, config);
3697         this.el  = Roo.get(this.el);
3698     } else {
3699         // old format..
3700         this.el  = Roo.get(config);
3701         this.tpl = depreciated_tpl;
3702         Roo.apply(this, depreciated_config);
3703     }
3704     this.wrapEl  = this.el.wrap().wrap();
3705     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3706     
3707     
3708     if(typeof(this.tpl) == "string"){
3709         this.tpl = new Roo.Template(this.tpl);
3710     } else {
3711         // support xtype ctors..
3712         this.tpl = new Roo.factory(this.tpl, Roo);
3713     }
3714     
3715     
3716     this.tpl.compile();
3717     
3718     /** @private */
3719     this.addEvents({
3720         /**
3721          * @event beforeclick
3722          * Fires before a click is processed. Returns false to cancel the default action.
3723          * @param {Roo.View} this
3724          * @param {Number} index The index of the target node
3725          * @param {HTMLElement} node The target node
3726          * @param {Roo.EventObject} e The raw event object
3727          */
3728             "beforeclick" : true,
3729         /**
3730          * @event click
3731          * Fires when a template node is clicked.
3732          * @param {Roo.View} this
3733          * @param {Number} index The index of the target node
3734          * @param {HTMLElement} node The target node
3735          * @param {Roo.EventObject} e The raw event object
3736          */
3737             "click" : true,
3738         /**
3739          * @event dblclick
3740          * Fires when a template node is double clicked.
3741          * @param {Roo.View} this
3742          * @param {Number} index The index of the target node
3743          * @param {HTMLElement} node The target node
3744          * @param {Roo.EventObject} e The raw event object
3745          */
3746             "dblclick" : true,
3747         /**
3748          * @event contextmenu
3749          * Fires when a template node is right clicked.
3750          * @param {Roo.View} this
3751          * @param {Number} index The index of the target node
3752          * @param {HTMLElement} node The target node
3753          * @param {Roo.EventObject} e The raw event object
3754          */
3755             "contextmenu" : true,
3756         /**
3757          * @event selectionchange
3758          * Fires when the selected nodes change.
3759          * @param {Roo.View} this
3760          * @param {Array} selections Array of the selected nodes
3761          */
3762             "selectionchange" : true,
3763     
3764         /**
3765          * @event beforeselect
3766          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3767          * @param {Roo.View} this
3768          * @param {HTMLElement} node The node to be selected
3769          * @param {Array} selections Array of currently selected nodes
3770          */
3771             "beforeselect" : true,
3772         /**
3773          * @event preparedata
3774          * Fires on every row to render, to allow you to change the data.
3775          * @param {Roo.View} this
3776          * @param {Object} data to be rendered (change this)
3777          */
3778           "preparedata" : true
3779           
3780           
3781         });
3782
3783
3784
3785     this.el.on({
3786         "click": this.onClick,
3787         "dblclick": this.onDblClick,
3788         "contextmenu": this.onContextMenu,
3789         scope:this
3790     });
3791
3792     this.selections = [];
3793     this.nodes = [];
3794     this.cmp = new Roo.CompositeElementLite([]);
3795     if(this.store){
3796         this.store = Roo.factory(this.store, Roo.data);
3797         this.setStore(this.store, true);
3798     }
3799     
3800     if ( this.footer && this.footer.xtype) {
3801            
3802          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3803         
3804         this.footer.dataSource = this.store;
3805         this.footer.container = fctr;
3806         this.footer = Roo.factory(this.footer, Roo);
3807         fctr.insertFirst(this.el);
3808         
3809         // this is a bit insane - as the paging toolbar seems to detach the el..
3810 //        dom.parentNode.parentNode.parentNode
3811          // they get detached?
3812     }
3813     
3814     
3815     Roo.View.superclass.constructor.call(this);
3816     
3817     
3818 };
3819
3820 Roo.extend(Roo.View, Roo.util.Observable, {
3821     
3822      /**
3823      * @cfg {Roo.data.Store} store Data store to load data from.
3824      */
3825     store : false,
3826     
3827     /**
3828      * @cfg {String|Roo.Element} el The container element.
3829      */
3830     el : '',
3831     
3832     /**
3833      * @cfg {String|Roo.Template} tpl The template used by this View 
3834      */
3835     tpl : false,
3836     /**
3837      * @cfg {String} dataName the named area of the template to use as the data area
3838      *                          Works with domtemplates roo-name="name"
3839      */
3840     dataName: false,
3841     /**
3842      * @cfg {String} selectedClass The css class to add to selected nodes
3843      */
3844     selectedClass : "x-view-selected",
3845      /**
3846      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3847      */
3848     emptyText : "",
3849     
3850     /**
3851      * @cfg {String} text to display on mask (default Loading)
3852      */
3853     mask : false,
3854     /**
3855      * @cfg {Boolean} multiSelect Allow multiple selection
3856      */
3857     multiSelect : false,
3858     /**
3859      * @cfg {Boolean} singleSelect Allow single selection
3860      */
3861     singleSelect:  false,
3862     
3863     /**
3864      * @cfg {Boolean} toggleSelect - selecting 
3865      */
3866     toggleSelect : false,
3867     
3868     /**
3869      * @cfg {Boolean} tickable - selecting 
3870      */
3871     tickable : false,
3872     
3873     /**
3874      * Returns the element this view is bound to.
3875      * @return {Roo.Element}
3876      */
3877     getEl : function(){
3878         return this.wrapEl;
3879     },
3880     
3881     
3882
3883     /**
3884      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3885      */
3886     refresh : function(){
3887         //Roo.log('refresh');
3888         var t = this.tpl;
3889         
3890         // if we are using something like 'domtemplate', then
3891         // the what gets used is:
3892         // t.applySubtemplate(NAME, data, wrapping data..)
3893         // the outer template then get' applied with
3894         //     the store 'extra data'
3895         // and the body get's added to the
3896         //      roo-name="data" node?
3897         //      <span class='roo-tpl-{name}'></span> ?????
3898         
3899         
3900         
3901         this.clearSelections();
3902         this.el.update("");
3903         var html = [];
3904         var records = this.store.getRange();
3905         if(records.length < 1) {
3906             
3907             // is this valid??  = should it render a template??
3908             
3909             this.el.update(this.emptyText);
3910             return;
3911         }
3912         var el = this.el;
3913         if (this.dataName) {
3914             this.el.update(t.apply(this.store.meta)); //????
3915             el = this.el.child('.roo-tpl-' + this.dataName);
3916         }
3917         
3918         for(var i = 0, len = records.length; i < len; i++){
3919             var data = this.prepareData(records[i].data, i, records[i]);
3920             this.fireEvent("preparedata", this, data, i, records[i]);
3921             
3922             var d = Roo.apply({}, data);
3923             
3924             if(this.tickable){
3925                 Roo.apply(d, {'roo-id' : Roo.id()});
3926                 
3927                 var _this = this;
3928             
3929                 Roo.each(this.parent.item, function(item){
3930                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3931                         return;
3932                     }
3933                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3934                 });
3935             }
3936             
3937             html[html.length] = Roo.util.Format.trim(
3938                 this.dataName ?
3939                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3940                     t.apply(d)
3941             );
3942         }
3943         
3944         
3945         
3946         el.update(html.join(""));
3947         this.nodes = el.dom.childNodes;
3948         this.updateIndexes(0);
3949     },
3950     
3951
3952     /**
3953      * Function to override to reformat the data that is sent to
3954      * the template for each node.
3955      * DEPRICATED - use the preparedata event handler.
3956      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3957      * a JSON object for an UpdateManager bound view).
3958      */
3959     prepareData : function(data, index, record)
3960     {
3961         this.fireEvent("preparedata", this, data, index, record);
3962         return data;
3963     },
3964
3965     onUpdate : function(ds, record){
3966         // Roo.log('on update');   
3967         this.clearSelections();
3968         var index = this.store.indexOf(record);
3969         var n = this.nodes[index];
3970         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3971         n.parentNode.removeChild(n);
3972         this.updateIndexes(index, index);
3973     },
3974
3975     
3976     
3977 // --------- FIXME     
3978     onAdd : function(ds, records, index)
3979     {
3980         //Roo.log(['on Add', ds, records, index] );        
3981         this.clearSelections();
3982         if(this.nodes.length == 0){
3983             this.refresh();
3984             return;
3985         }
3986         var n = this.nodes[index];
3987         for(var i = 0, len = records.length; i < len; i++){
3988             var d = this.prepareData(records[i].data, i, records[i]);
3989             if(n){
3990                 this.tpl.insertBefore(n, d);
3991             }else{
3992                 
3993                 this.tpl.append(this.el, d);
3994             }
3995         }
3996         this.updateIndexes(index);
3997     },
3998
3999     onRemove : function(ds, record, index){
4000        // Roo.log('onRemove');
4001         this.clearSelections();
4002         var el = this.dataName  ?
4003             this.el.child('.roo-tpl-' + this.dataName) :
4004             this.el; 
4005         
4006         el.dom.removeChild(this.nodes[index]);
4007         this.updateIndexes(index);
4008     },
4009
4010     /**
4011      * Refresh an individual node.
4012      * @param {Number} index
4013      */
4014     refreshNode : function(index){
4015         this.onUpdate(this.store, this.store.getAt(index));
4016     },
4017
4018     updateIndexes : function(startIndex, endIndex){
4019         var ns = this.nodes;
4020         startIndex = startIndex || 0;
4021         endIndex = endIndex || ns.length - 1;
4022         for(var i = startIndex; i <= endIndex; i++){
4023             ns[i].nodeIndex = i;
4024         }
4025     },
4026
4027     /**
4028      * Changes the data store this view uses and refresh the view.
4029      * @param {Store} store
4030      */
4031     setStore : function(store, initial){
4032         if(!initial && this.store){
4033             this.store.un("datachanged", this.refresh);
4034             this.store.un("add", this.onAdd);
4035             this.store.un("remove", this.onRemove);
4036             this.store.un("update", this.onUpdate);
4037             this.store.un("clear", this.refresh);
4038             this.store.un("beforeload", this.onBeforeLoad);
4039             this.store.un("load", this.onLoad);
4040             this.store.un("loadexception", this.onLoad);
4041         }
4042         if(store){
4043           
4044             store.on("datachanged", this.refresh, this);
4045             store.on("add", this.onAdd, this);
4046             store.on("remove", this.onRemove, this);
4047             store.on("update", this.onUpdate, this);
4048             store.on("clear", this.refresh, this);
4049             store.on("beforeload", this.onBeforeLoad, this);
4050             store.on("load", this.onLoad, this);
4051             store.on("loadexception", this.onLoad, this);
4052         }
4053         
4054         if(store){
4055             this.refresh();
4056         }
4057     },
4058     /**
4059      * onbeforeLoad - masks the loading area.
4060      *
4061      */
4062     onBeforeLoad : function(store,opts)
4063     {
4064          //Roo.log('onBeforeLoad');   
4065         if (!opts.add) {
4066             this.el.update("");
4067         }
4068         this.el.mask(this.mask ? this.mask : "Loading" ); 
4069     },
4070     onLoad : function ()
4071     {
4072         this.el.unmask();
4073     },
4074     
4075
4076     /**
4077      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4078      * @param {HTMLElement} node
4079      * @return {HTMLElement} The template node
4080      */
4081     findItemFromChild : function(node){
4082         var el = this.dataName  ?
4083             this.el.child('.roo-tpl-' + this.dataName,true) :
4084             this.el.dom; 
4085         
4086         if(!node || node.parentNode == el){
4087                     return node;
4088             }
4089             var p = node.parentNode;
4090             while(p && p != el){
4091             if(p.parentNode == el){
4092                 return p;
4093             }
4094             p = p.parentNode;
4095         }
4096             return null;
4097     },
4098
4099     /** @ignore */
4100     onClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             var index = this.indexOf(item);
4104             if(this.onItemClick(item, index, e) !== false){
4105                 this.fireEvent("click", this, index, item, e);
4106             }
4107         }else{
4108             this.clearSelections();
4109         }
4110     },
4111
4112     /** @ignore */
4113     onContextMenu : function(e){
4114         var item = this.findItemFromChild(e.getTarget());
4115         if(item){
4116             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4117         }
4118     },
4119
4120     /** @ignore */
4121     onDblClick : function(e){
4122         var item = this.findItemFromChild(e.getTarget());
4123         if(item){
4124             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4125         }
4126     },
4127
4128     onItemClick : function(item, index, e)
4129     {
4130         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4131             return false;
4132         }
4133         if (this.toggleSelect) {
4134             var m = this.isSelected(item) ? 'unselect' : 'select';
4135             //Roo.log(m);
4136             var _t = this;
4137             _t[m](item, true, false);
4138             return true;
4139         }
4140         if(this.multiSelect || this.singleSelect){
4141             if(this.multiSelect && e.shiftKey && this.lastSelection){
4142                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4143             }else{
4144                 this.select(item, this.multiSelect && e.ctrlKey);
4145                 this.lastSelection = item;
4146             }
4147             
4148             if(!this.tickable){
4149                 e.preventDefault();
4150             }
4151             
4152         }
4153         return true;
4154     },
4155
4156     /**
4157      * Get the number of selected nodes.
4158      * @return {Number}
4159      */
4160     getSelectionCount : function(){
4161         return this.selections.length;
4162     },
4163
4164     /**
4165      * Get the currently selected nodes.
4166      * @return {Array} An array of HTMLElements
4167      */
4168     getSelectedNodes : function(){
4169         return this.selections;
4170     },
4171
4172     /**
4173      * Get the indexes of the selected nodes.
4174      * @return {Array}
4175      */
4176     getSelectedIndexes : function(){
4177         var indexes = [], s = this.selections;
4178         for(var i = 0, len = s.length; i < len; i++){
4179             indexes.push(s[i].nodeIndex);
4180         }
4181         return indexes;
4182     },
4183
4184     /**
4185      * Clear all selections
4186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4187      */
4188     clearSelections : function(suppressEvent){
4189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4190             this.cmp.elements = this.selections;
4191             this.cmp.removeClass(this.selectedClass);
4192             this.selections = [];
4193             if(!suppressEvent){
4194                 this.fireEvent("selectionchange", this, this.selections);
4195             }
4196         }
4197     },
4198
4199     /**
4200      * Returns true if the passed node is selected
4201      * @param {HTMLElement/Number} node The node or node index
4202      * @return {Boolean}
4203      */
4204     isSelected : function(node){
4205         var s = this.selections;
4206         if(s.length < 1){
4207             return false;
4208         }
4209         node = this.getNode(node);
4210         return s.indexOf(node) !== -1;
4211     },
4212
4213     /**
4214      * Selects nodes.
4215      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4216      * @param {Boolean} keepExisting (optional) true to keep existing selections
4217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4218      */
4219     select : function(nodeInfo, keepExisting, suppressEvent){
4220         if(nodeInfo instanceof Array){
4221             if(!keepExisting){
4222                 this.clearSelections(true);
4223             }
4224             for(var i = 0, len = nodeInfo.length; i < len; i++){
4225                 this.select(nodeInfo[i], true, true);
4226             }
4227             return;
4228         } 
4229         var node = this.getNode(nodeInfo);
4230         if(!node || this.isSelected(node)){
4231             return; // already selected.
4232         }
4233         if(!keepExisting){
4234             this.clearSelections(true);
4235         }
4236         
4237         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4238             Roo.fly(node).addClass(this.selectedClass);
4239             this.selections.push(node);
4240             if(!suppressEvent){
4241                 this.fireEvent("selectionchange", this, this.selections);
4242             }
4243         }
4244         
4245         
4246     },
4247       /**
4248      * Unselects nodes.
4249      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4250      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4251      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4252      */
4253     unselect : function(nodeInfo, keepExisting, suppressEvent)
4254     {
4255         if(nodeInfo instanceof Array){
4256             Roo.each(this.selections, function(s) {
4257                 this.unselect(s, nodeInfo);
4258             }, this);
4259             return;
4260         }
4261         var node = this.getNode(nodeInfo);
4262         if(!node || !this.isSelected(node)){
4263             //Roo.log("not selected");
4264             return; // not selected.
4265         }
4266         // fireevent???
4267         var ns = [];
4268         Roo.each(this.selections, function(s) {
4269             if (s == node ) {
4270                 Roo.fly(node).removeClass(this.selectedClass);
4271
4272                 return;
4273             }
4274             ns.push(s);
4275         },this);
4276         
4277         this.selections= ns;
4278         this.fireEvent("selectionchange", this, this.selections);
4279     },
4280
4281     /**
4282      * Gets a template node.
4283      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4284      * @return {HTMLElement} The node or null if it wasn't found
4285      */
4286     getNode : function(nodeInfo){
4287         if(typeof nodeInfo == "string"){
4288             return document.getElementById(nodeInfo);
4289         }else if(typeof nodeInfo == "number"){
4290             return this.nodes[nodeInfo];
4291         }
4292         return nodeInfo;
4293     },
4294
4295     /**
4296      * Gets a range template nodes.
4297      * @param {Number} startIndex
4298      * @param {Number} endIndex
4299      * @return {Array} An array of nodes
4300      */
4301     getNodes : function(start, end){
4302         var ns = this.nodes;
4303         start = start || 0;
4304         end = typeof end == "undefined" ? ns.length - 1 : end;
4305         var nodes = [];
4306         if(start <= end){
4307             for(var i = start; i <= end; i++){
4308                 nodes.push(ns[i]);
4309             }
4310         } else{
4311             for(var i = start; i >= end; i--){
4312                 nodes.push(ns[i]);
4313             }
4314         }
4315         return nodes;
4316     },
4317
4318     /**
4319      * Finds the index of the passed node
4320      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4321      * @return {Number} The index of the node or -1
4322      */
4323     indexOf : function(node){
4324         node = this.getNode(node);
4325         if(typeof node.nodeIndex == "number"){
4326             return node.nodeIndex;
4327         }
4328         var ns = this.nodes;
4329         for(var i = 0, len = ns.length; i < len; i++){
4330             if(ns[i] == node){
4331                 return i;
4332             }
4333         }
4334         return -1;
4335     }
4336 });
4337 /*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348 /**
4349  * @class Roo.JsonView
4350  * @extends Roo.View
4351  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4352 <pre><code>
4353 var view = new Roo.JsonView({
4354     container: "my-element",
4355     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4356     multiSelect: true, 
4357     jsonRoot: "data" 
4358 });
4359
4360 // listen for node click?
4361 view.on("click", function(vw, index, node, e){
4362     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4363 });
4364
4365 // direct load of JSON data
4366 view.load("foobar.php");
4367
4368 // Example from my blog list
4369 var tpl = new Roo.Template(
4370     '&lt;div class="entry"&gt;' +
4371     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4372     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4373     "&lt;/div&gt;&lt;hr /&gt;"
4374 );
4375
4376 var moreView = new Roo.JsonView({
4377     container :  "entry-list", 
4378     template : tpl,
4379     jsonRoot: "posts"
4380 });
4381 moreView.on("beforerender", this.sortEntries, this);
4382 moreView.load({
4383     url: "/blog/get-posts.php",
4384     params: "allposts=true",
4385     text: "Loading Blog Entries..."
4386 });
4387 </code></pre>
4388
4389 * Note: old code is supported with arguments : (container, template, config)
4390
4391
4392  * @constructor
4393  * Create a new JsonView
4394  * 
4395  * @param {Object} config The config object
4396  * 
4397  */
4398 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4399     
4400     
4401     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4402
4403     var um = this.el.getUpdateManager();
4404     um.setRenderer(this);
4405     um.on("update", this.onLoad, this);
4406     um.on("failure", this.onLoadException, this);
4407
4408     /**
4409      * @event beforerender
4410      * Fires before rendering of the downloaded JSON data.
4411      * @param {Roo.JsonView} this
4412      * @param {Object} data The JSON data loaded
4413      */
4414     /**
4415      * @event load
4416      * Fires when data is loaded.
4417      * @param {Roo.JsonView} this
4418      * @param {Object} data The JSON data loaded
4419      * @param {Object} response The raw Connect response object
4420      */
4421     /**
4422      * @event loadexception
4423      * Fires when loading fails.
4424      * @param {Roo.JsonView} this
4425      * @param {Object} response The raw Connect response object
4426      */
4427     this.addEvents({
4428         'beforerender' : true,
4429         'load' : true,
4430         'loadexception' : true
4431     });
4432 };
4433 Roo.extend(Roo.JsonView, Roo.View, {
4434     /**
4435      * @type {String} The root property in the loaded JSON object that contains the data
4436      */
4437     jsonRoot : "",
4438
4439     /**
4440      * Refreshes the view.
4441      */
4442     refresh : function(){
4443         this.clearSelections();
4444         this.el.update("");
4445         var html = [];
4446         var o = this.jsonData;
4447         if(o && o.length > 0){
4448             for(var i = 0, len = o.length; i < len; i++){
4449                 var data = this.prepareData(o[i], i, o);
4450                 html[html.length] = this.tpl.apply(data);
4451             }
4452         }else{
4453             html.push(this.emptyText);
4454         }
4455         this.el.update(html.join(""));
4456         this.nodes = this.el.dom.childNodes;
4457         this.updateIndexes(0);
4458     },
4459
4460     /**
4461      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4462      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4463      <pre><code>
4464      view.load({
4465          url: "your-url.php",
4466          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4467          callback: yourFunction,
4468          scope: yourObject, //(optional scope)
4469          discardUrl: false,
4470          nocache: false,
4471          text: "Loading...",
4472          timeout: 30,
4473          scripts: false
4474      });
4475      </code></pre>
4476      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4477      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4478      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4479      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4480      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4481      */
4482     load : function(){
4483         var um = this.el.getUpdateManager();
4484         um.update.apply(um, arguments);
4485     },
4486
4487     // note - render is a standard framework call...
4488     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4489     render : function(el, response){
4490         
4491         this.clearSelections();
4492         this.el.update("");
4493         var o;
4494         try{
4495             if (response != '') {
4496                 o = Roo.util.JSON.decode(response.responseText);
4497                 if(this.jsonRoot){
4498                     
4499                     o = o[this.jsonRoot];
4500                 }
4501             }
4502         } catch(e){
4503         }
4504         /**
4505          * The current JSON data or null
4506          */
4507         this.jsonData = o;
4508         this.beforeRender();
4509         this.refresh();
4510     },
4511
4512 /**
4513  * Get the number of records in the current JSON dataset
4514  * @return {Number}
4515  */
4516     getCount : function(){
4517         return this.jsonData ? this.jsonData.length : 0;
4518     },
4519
4520 /**
4521  * Returns the JSON object for the specified node(s)
4522  * @param {HTMLElement/Array} node The node or an array of nodes
4523  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4524  * you get the JSON object for the node
4525  */
4526     getNodeData : function(node){
4527         if(node instanceof Array){
4528             var data = [];
4529             for(var i = 0, len = node.length; i < len; i++){
4530                 data.push(this.getNodeData(node[i]));
4531             }
4532             return data;
4533         }
4534         return this.jsonData[this.indexOf(node)] || null;
4535     },
4536
4537     beforeRender : function(){
4538         this.snapshot = this.jsonData;
4539         if(this.sortInfo){
4540             this.sort.apply(this, this.sortInfo);
4541         }
4542         this.fireEvent("beforerender", this, this.jsonData);
4543     },
4544
4545     onLoad : function(el, o){
4546         this.fireEvent("load", this, this.jsonData, o);
4547     },
4548
4549     onLoadException : function(el, o){
4550         this.fireEvent("loadexception", this, o);
4551     },
4552
4553 /**
4554  * Filter the data by a specific property.
4555  * @param {String} property A property on your JSON objects
4556  * @param {String/RegExp} value Either string that the property values
4557  * should start with, or a RegExp to test against the property
4558  */
4559     filter : function(property, value){
4560         if(this.jsonData){
4561             var data = [];
4562             var ss = this.snapshot;
4563             if(typeof value == "string"){
4564                 var vlen = value.length;
4565                 if(vlen == 0){
4566                     this.clearFilter();
4567                     return;
4568                 }
4569                 value = value.toLowerCase();
4570                 for(var i = 0, len = ss.length; i < len; i++){
4571                     var o = ss[i];
4572                     if(o[property].substr(0, vlen).toLowerCase() == value){
4573                         data.push(o);
4574                     }
4575                 }
4576             } else if(value.exec){ // regex?
4577                 for(var i = 0, len = ss.length; i < len; i++){
4578                     var o = ss[i];
4579                     if(value.test(o[property])){
4580                         data.push(o);
4581                     }
4582                 }
4583             } else{
4584                 return;
4585             }
4586             this.jsonData = data;
4587             this.refresh();
4588         }
4589     },
4590
4591 /**
4592  * Filter by a function. The passed function will be called with each
4593  * object in the current dataset. If the function returns true the value is kept,
4594  * otherwise it is filtered.
4595  * @param {Function} fn
4596  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4597  */
4598     filterBy : function(fn, scope){
4599         if(this.jsonData){
4600             var data = [];
4601             var ss = this.snapshot;
4602             for(var i = 0, len = ss.length; i < len; i++){
4603                 var o = ss[i];
4604                 if(fn.call(scope || this, o)){
4605                     data.push(o);
4606                 }
4607             }
4608             this.jsonData = data;
4609             this.refresh();
4610         }
4611     },
4612
4613 /**
4614  * Clears the current filter.
4615  */
4616     clearFilter : function(){
4617         if(this.snapshot && this.jsonData != this.snapshot){
4618             this.jsonData = this.snapshot;
4619             this.refresh();
4620         }
4621     },
4622
4623
4624 /**
4625  * Sorts the data for this view and refreshes it.
4626  * @param {String} property A property on your JSON objects to sort on
4627  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4628  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4629  */
4630     sort : function(property, dir, sortType){
4631         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4632         if(this.jsonData){
4633             var p = property;
4634             var dsc = dir && dir.toLowerCase() == "desc";
4635             var f = function(o1, o2){
4636                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4637                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4638                 ;
4639                 if(v1 < v2){
4640                     return dsc ? +1 : -1;
4641                 } else if(v1 > v2){
4642                     return dsc ? -1 : +1;
4643                 } else{
4644                     return 0;
4645                 }
4646             };
4647             this.jsonData.sort(f);
4648             this.refresh();
4649             if(this.jsonData != this.snapshot){
4650                 this.snapshot.sort(f);
4651             }
4652         }
4653     }
4654 });/*
4655  * Based on:
4656  * Ext JS Library 1.1.1
4657  * Copyright(c) 2006-2007, Ext JS, LLC.
4658  *
4659  * Originally Released Under LGPL - original licence link has changed is not relivant.
4660  *
4661  * Fork - LGPL
4662  * <script type="text/javascript">
4663  */
4664  
4665
4666 /**
4667  * @class Roo.ColorPalette
4668  * @extends Roo.Component
4669  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4670  * Here's an example of typical usage:
4671  * <pre><code>
4672 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4673 cp.render('my-div');
4674
4675 cp.on('select', function(palette, selColor){
4676     // do something with selColor
4677 });
4678 </code></pre>
4679  * @constructor
4680  * Create a new ColorPalette
4681  * @param {Object} config The config object
4682  */
4683 Roo.ColorPalette = function(config){
4684     Roo.ColorPalette.superclass.constructor.call(this, config);
4685     this.addEvents({
4686         /**
4687              * @event select
4688              * Fires when a color is selected
4689              * @param {ColorPalette} this
4690              * @param {String} color The 6-digit color hex code (without the # symbol)
4691              */
4692         select: true
4693     });
4694
4695     if(this.handler){
4696         this.on("select", this.handler, this.scope, true);
4697     }
4698 };
4699 Roo.extend(Roo.ColorPalette, Roo.Component, {
4700     /**
4701      * @cfg {String} itemCls
4702      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4703      */
4704     itemCls : "x-color-palette",
4705     /**
4706      * @cfg {String} value
4707      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4708      * the hex codes are case-sensitive.
4709      */
4710     value : null,
4711     clickEvent:'click',
4712     // private
4713     ctype: "Roo.ColorPalette",
4714
4715     /**
4716      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4717      */
4718     allowReselect : false,
4719
4720     /**
4721      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4722      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4723      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4724      * of colors with the width setting until the box is symmetrical.</p>
4725      * <p>You can override individual colors if needed:</p>
4726      * <pre><code>
4727 var cp = new Roo.ColorPalette();
4728 cp.colors[0] = "FF0000";  // change the first box to red
4729 </code></pre>
4730
4731 Or you can provide a custom array of your own for complete control:
4732 <pre><code>
4733 var cp = new Roo.ColorPalette();
4734 cp.colors = ["000000", "993300", "333300"];
4735 </code></pre>
4736      * @type Array
4737      */
4738     colors : [
4739         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4740         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4741         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4742         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4743         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4744     ],
4745
4746     // private
4747     onRender : function(container, position){
4748         var t = new Roo.MasterTemplate(
4749             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4750         );
4751         var c = this.colors;
4752         for(var i = 0, len = c.length; i < len; i++){
4753             t.add([c[i]]);
4754         }
4755         var el = document.createElement("div");
4756         el.className = this.itemCls;
4757         t.overwrite(el);
4758         container.dom.insertBefore(el, position);
4759         this.el = Roo.get(el);
4760         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4761         if(this.clickEvent != 'click'){
4762             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4763         }
4764     },
4765
4766     // private
4767     afterRender : function(){
4768         Roo.ColorPalette.superclass.afterRender.call(this);
4769         if(this.value){
4770             var s = this.value;
4771             this.value = null;
4772             this.select(s);
4773         }
4774     },
4775
4776     // private
4777     handleClick : function(e, t){
4778         e.preventDefault();
4779         if(!this.disabled){
4780             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4781             this.select(c.toUpperCase());
4782         }
4783     },
4784
4785     /**
4786      * Selects the specified color in the palette (fires the select event)
4787      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4788      */
4789     select : function(color){
4790         color = color.replace("#", "");
4791         if(color != this.value || this.allowReselect){
4792             var el = this.el;
4793             if(this.value){
4794                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4795             }
4796             el.child("a.color-"+color).addClass("x-color-palette-sel");
4797             this.value = color;
4798             this.fireEvent("select", this, color);
4799         }
4800     }
4801 });/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812 /**
4813  * @class Roo.DatePicker
4814  * @extends Roo.Component
4815  * Simple date picker class.
4816  * @constructor
4817  * Create a new DatePicker
4818  * @param {Object} config The config object
4819  */
4820 Roo.DatePicker = function(config){
4821     Roo.DatePicker.superclass.constructor.call(this, config);
4822
4823     this.value = config && config.value ?
4824                  config.value.clearTime() : new Date().clearTime();
4825
4826     this.addEvents({
4827         /**
4828              * @event select
4829              * Fires when a date is selected
4830              * @param {DatePicker} this
4831              * @param {Date} date The selected date
4832              */
4833         'select': true,
4834         /**
4835              * @event monthchange
4836              * Fires when the displayed month changes 
4837              * @param {DatePicker} this
4838              * @param {Date} date The selected month
4839              */
4840         'monthchange': true
4841     });
4842
4843     if(this.handler){
4844         this.on("select", this.handler,  this.scope || this);
4845     }
4846     // build the disabledDatesRE
4847     if(!this.disabledDatesRE && this.disabledDates){
4848         var dd = this.disabledDates;
4849         var re = "(?:";
4850         for(var i = 0; i < dd.length; i++){
4851             re += dd[i];
4852             if(i != dd.length-1) {
4853                 re += "|";
4854             }
4855         }
4856         this.disabledDatesRE = new RegExp(re + ")");
4857     }
4858 };
4859
4860 Roo.extend(Roo.DatePicker, Roo.Component, {
4861     /**
4862      * @cfg {String} todayText
4863      * The text to display on the button that selects the current date (defaults to "Today")
4864      */
4865     todayText : "Today",
4866     /**
4867      * @cfg {String} okText
4868      * The text to display on the ok button
4869      */
4870     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4871     /**
4872      * @cfg {String} cancelText
4873      * The text to display on the cancel button
4874      */
4875     cancelText : "Cancel",
4876     /**
4877      * @cfg {String} todayTip
4878      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4879      */
4880     todayTip : "{0} (Spacebar)",
4881     /**
4882      * @cfg {Date} minDate
4883      * Minimum allowable date (JavaScript date object, defaults to null)
4884      */
4885     minDate : null,
4886     /**
4887      * @cfg {Date} maxDate
4888      * Maximum allowable date (JavaScript date object, defaults to null)
4889      */
4890     maxDate : null,
4891     /**
4892      * @cfg {String} minText
4893      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4894      */
4895     minText : "This date is before the minimum date",
4896     /**
4897      * @cfg {String} maxText
4898      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4899      */
4900     maxText : "This date is after the maximum date",
4901     /**
4902      * @cfg {String} format
4903      * The default date format string which can be overriden for localization support.  The format must be
4904      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4905      */
4906     format : "m/d/y",
4907     /**
4908      * @cfg {Array} disabledDays
4909      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4910      */
4911     disabledDays : null,
4912     /**
4913      * @cfg {String} disabledDaysText
4914      * The tooltip to display when the date falls on a disabled day (defaults to "")
4915      */
4916     disabledDaysText : "",
4917     /**
4918      * @cfg {RegExp} disabledDatesRE
4919      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4920      */
4921     disabledDatesRE : null,
4922     /**
4923      * @cfg {String} disabledDatesText
4924      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4925      */
4926     disabledDatesText : "",
4927     /**
4928      * @cfg {Boolean} constrainToViewport
4929      * True to constrain the date picker to the viewport (defaults to true)
4930      */
4931     constrainToViewport : true,
4932     /**
4933      * @cfg {Array} monthNames
4934      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4935      */
4936     monthNames : Date.monthNames,
4937     /**
4938      * @cfg {Array} dayNames
4939      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4940      */
4941     dayNames : Date.dayNames,
4942     /**
4943      * @cfg {String} nextText
4944      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4945      */
4946     nextText: 'Next Month (Control+Right)',
4947     /**
4948      * @cfg {String} prevText
4949      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4950      */
4951     prevText: 'Previous Month (Control+Left)',
4952     /**
4953      * @cfg {String} monthYearText
4954      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4955      */
4956     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4957     /**
4958      * @cfg {Number} startDay
4959      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4960      */
4961     startDay : 0,
4962     /**
4963      * @cfg {Bool} showClear
4964      * Show a clear button (usefull for date form elements that can be blank.)
4965      */
4966     
4967     showClear: false,
4968     
4969     /**
4970      * Sets the value of the date field
4971      * @param {Date} value The date to set
4972      */
4973     setValue : function(value){
4974         var old = this.value;
4975         
4976         if (typeof(value) == 'string') {
4977          
4978             value = Date.parseDate(value, this.format);
4979         }
4980         if (!value) {
4981             value = new Date();
4982         }
4983         
4984         this.value = value.clearTime(true);
4985         if(this.el){
4986             this.update(this.value);
4987         }
4988     },
4989
4990     /**
4991      * Gets the current selected value of the date field
4992      * @return {Date} The selected date
4993      */
4994     getValue : function(){
4995         return this.value;
4996     },
4997
4998     // private
4999     focus : function(){
5000         if(this.el){
5001             this.update(this.activeDate);
5002         }
5003     },
5004
5005     // privateval
5006     onRender : function(container, position){
5007         
5008         var m = [
5009              '<table cellspacing="0">',
5010                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5011                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5012         var dn = this.dayNames;
5013         for(var i = 0; i < 7; i++){
5014             var d = this.startDay+i;
5015             if(d > 6){
5016                 d = d-7;
5017             }
5018             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5019         }
5020         m[m.length] = "</tr></thead><tbody><tr>";
5021         for(var i = 0; i < 42; i++) {
5022             if(i % 7 == 0 && i != 0){
5023                 m[m.length] = "</tr><tr>";
5024             }
5025             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5026         }
5027         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5028             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5029
5030         var el = document.createElement("div");
5031         el.className = "x-date-picker";
5032         el.innerHTML = m.join("");
5033
5034         container.dom.insertBefore(el, position);
5035
5036         this.el = Roo.get(el);
5037         this.eventEl = Roo.get(el.firstChild);
5038
5039         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5040             handler: this.showPrevMonth,
5041             scope: this,
5042             preventDefault:true,
5043             stopDefault:true
5044         });
5045
5046         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5047             handler: this.showNextMonth,
5048             scope: this,
5049             preventDefault:true,
5050             stopDefault:true
5051         });
5052
5053         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5054
5055         this.monthPicker = this.el.down('div.x-date-mp');
5056         this.monthPicker.enableDisplayMode('block');
5057         
5058         var kn = new Roo.KeyNav(this.eventEl, {
5059             "left" : function(e){
5060                 e.ctrlKey ?
5061                     this.showPrevMonth() :
5062                     this.update(this.activeDate.add("d", -1));
5063             },
5064
5065             "right" : function(e){
5066                 e.ctrlKey ?
5067                     this.showNextMonth() :
5068                     this.update(this.activeDate.add("d", 1));
5069             },
5070
5071             "up" : function(e){
5072                 e.ctrlKey ?
5073                     this.showNextYear() :
5074                     this.update(this.activeDate.add("d", -7));
5075             },
5076
5077             "down" : function(e){
5078                 e.ctrlKey ?
5079                     this.showPrevYear() :
5080                     this.update(this.activeDate.add("d", 7));
5081             },
5082
5083             "pageUp" : function(e){
5084                 this.showNextMonth();
5085             },
5086
5087             "pageDown" : function(e){
5088                 this.showPrevMonth();
5089             },
5090
5091             "enter" : function(e){
5092                 e.stopPropagation();
5093                 return true;
5094             },
5095
5096             scope : this
5097         });
5098
5099         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5100
5101         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5102
5103         this.el.unselectable();
5104         
5105         this.cells = this.el.select("table.x-date-inner tbody td");
5106         this.textNodes = this.el.query("table.x-date-inner tbody span");
5107
5108         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5109             text: "&#160;",
5110             tooltip: this.monthYearText
5111         });
5112
5113         this.mbtn.on('click', this.showMonthPicker, this);
5114         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5115
5116
5117         var today = (new Date()).dateFormat(this.format);
5118         
5119         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5120         if (this.showClear) {
5121             baseTb.add( new Roo.Toolbar.Fill());
5122         }
5123         baseTb.add({
5124             text: String.format(this.todayText, today),
5125             tooltip: String.format(this.todayTip, today),
5126             handler: this.selectToday,
5127             scope: this
5128         });
5129         
5130         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5131             
5132         //});
5133         if (this.showClear) {
5134             
5135             baseTb.add( new Roo.Toolbar.Fill());
5136             baseTb.add({
5137                 text: '&#160;',
5138                 cls: 'x-btn-icon x-btn-clear',
5139                 handler: function() {
5140                     //this.value = '';
5141                     this.fireEvent("select", this, '');
5142                 },
5143                 scope: this
5144             });
5145         }
5146         
5147         
5148         if(Roo.isIE){
5149             this.el.repaint();
5150         }
5151         this.update(this.value);
5152     },
5153
5154     createMonthPicker : function(){
5155         if(!this.monthPicker.dom.firstChild){
5156             var buf = ['<table border="0" cellspacing="0">'];
5157             for(var i = 0; i < 6; i++){
5158                 buf.push(
5159                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5160                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5161                     i == 0 ?
5162                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5163                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5164                 );
5165             }
5166             buf.push(
5167                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5168                     this.okText,
5169                     '</button><button type="button" class="x-date-mp-cancel">',
5170                     this.cancelText,
5171                     '</button></td></tr>',
5172                 '</table>'
5173             );
5174             this.monthPicker.update(buf.join(''));
5175             this.monthPicker.on('click', this.onMonthClick, this);
5176             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5177
5178             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5179             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5180
5181             this.mpMonths.each(function(m, a, i){
5182                 i += 1;
5183                 if((i%2) == 0){
5184                     m.dom.xmonth = 5 + Math.round(i * .5);
5185                 }else{
5186                     m.dom.xmonth = Math.round((i-1) * .5);
5187                 }
5188             });
5189         }
5190     },
5191
5192     showMonthPicker : function(){
5193         this.createMonthPicker();
5194         var size = this.el.getSize();
5195         this.monthPicker.setSize(size);
5196         this.monthPicker.child('table').setSize(size);
5197
5198         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5199         this.updateMPMonth(this.mpSelMonth);
5200         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5201         this.updateMPYear(this.mpSelYear);
5202
5203         this.monthPicker.slideIn('t', {duration:.2});
5204     },
5205
5206     updateMPYear : function(y){
5207         this.mpyear = y;
5208         var ys = this.mpYears.elements;
5209         for(var i = 1; i <= 10; i++){
5210             var td = ys[i-1], y2;
5211             if((i%2) == 0){
5212                 y2 = y + Math.round(i * .5);
5213                 td.firstChild.innerHTML = y2;
5214                 td.xyear = y2;
5215             }else{
5216                 y2 = y - (5-Math.round(i * .5));
5217                 td.firstChild.innerHTML = y2;
5218                 td.xyear = y2;
5219             }
5220             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5221         }
5222     },
5223
5224     updateMPMonth : function(sm){
5225         this.mpMonths.each(function(m, a, i){
5226             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5227         });
5228     },
5229
5230     selectMPMonth: function(m){
5231         
5232     },
5233
5234     onMonthClick : function(e, t){
5235         e.stopEvent();
5236         var el = new Roo.Element(t), pn;
5237         if(el.is('button.x-date-mp-cancel')){
5238             this.hideMonthPicker();
5239         }
5240         else if(el.is('button.x-date-mp-ok')){
5241             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.mpMonths.removeClass('x-date-mp-sel');
5246             pn.addClass('x-date-mp-sel');
5247             this.mpSelMonth = pn.dom.xmonth;
5248         }
5249         else if(pn = el.up('td.x-date-mp-year', 2)){
5250             this.mpYears.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelYear = pn.dom.xyear;
5253         }
5254         else if(el.is('a.x-date-mp-prev')){
5255             this.updateMPYear(this.mpyear-10);
5256         }
5257         else if(el.is('a.x-date-mp-next')){
5258             this.updateMPYear(this.mpyear+10);
5259         }
5260     },
5261
5262     onMonthDblClick : function(e, t){
5263         e.stopEvent();
5264         var el = new Roo.Element(t), pn;
5265         if(pn = el.up('td.x-date-mp-month', 2)){
5266             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5267             this.hideMonthPicker();
5268         }
5269         else if(pn = el.up('td.x-date-mp-year', 2)){
5270             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5271             this.hideMonthPicker();
5272         }
5273     },
5274
5275     hideMonthPicker : function(disableAnim){
5276         if(this.monthPicker){
5277             if(disableAnim === true){
5278                 this.monthPicker.hide();
5279             }else{
5280                 this.monthPicker.slideOut('t', {duration:.2});
5281             }
5282         }
5283     },
5284
5285     // private
5286     showPrevMonth : function(e){
5287         this.update(this.activeDate.add("mo", -1));
5288     },
5289
5290     // private
5291     showNextMonth : function(e){
5292         this.update(this.activeDate.add("mo", 1));
5293     },
5294
5295     // private
5296     showPrevYear : function(){
5297         this.update(this.activeDate.add("y", -1));
5298     },
5299
5300     // private
5301     showNextYear : function(){
5302         this.update(this.activeDate.add("y", 1));
5303     },
5304
5305     // private
5306     handleMouseWheel : function(e){
5307         var delta = e.getWheelDelta();
5308         if(delta > 0){
5309             this.showPrevMonth();
5310             e.stopEvent();
5311         } else if(delta < 0){
5312             this.showNextMonth();
5313             e.stopEvent();
5314         }
5315     },
5316
5317     // private
5318     handleDateClick : function(e, t){
5319         e.stopEvent();
5320         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5321             this.setValue(new Date(t.dateValue));
5322             this.fireEvent("select", this, this.value);
5323         }
5324     },
5325
5326     // private
5327     selectToday : function(){
5328         this.setValue(new Date().clearTime());
5329         this.fireEvent("select", this, this.value);
5330     },
5331
5332     // private
5333     update : function(date)
5334     {
5335         var vd = this.activeDate;
5336         this.activeDate = date;
5337         if(vd && this.el){
5338             var t = date.getTime();
5339             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5340                 this.cells.removeClass("x-date-selected");
5341                 this.cells.each(function(c){
5342                    if(c.dom.firstChild.dateValue == t){
5343                        c.addClass("x-date-selected");
5344                        setTimeout(function(){
5345                             try{c.dom.firstChild.focus();}catch(e){}
5346                        }, 50);
5347                        return false;
5348                    }
5349                 });
5350                 return;
5351             }
5352         }
5353         
5354         var days = date.getDaysInMonth();
5355         var firstOfMonth = date.getFirstDateOfMonth();
5356         var startingPos = firstOfMonth.getDay()-this.startDay;
5357
5358         if(startingPos <= this.startDay){
5359             startingPos += 7;
5360         }
5361
5362         var pm = date.add("mo", -1);
5363         var prevStart = pm.getDaysInMonth()-startingPos;
5364
5365         var cells = this.cells.elements;
5366         var textEls = this.textNodes;
5367         days += startingPos;
5368
5369         // convert everything to numbers so it's fast
5370         var day = 86400000;
5371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5372         var today = new Date().clearTime().getTime();
5373         var sel = date.clearTime().getTime();
5374         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5375         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5376         var ddMatch = this.disabledDatesRE;
5377         var ddText = this.disabledDatesText;
5378         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5379         var ddaysText = this.disabledDaysText;
5380         var format = this.format;
5381
5382         var setCellClass = function(cal, cell){
5383             cell.title = "";
5384             var t = d.getTime();
5385             cell.firstChild.dateValue = t;
5386             if(t == today){
5387                 cell.className += " x-date-today";
5388                 cell.title = cal.todayText;
5389             }
5390             if(t == sel){
5391                 cell.className += " x-date-selected";
5392                 setTimeout(function(){
5393                     try{cell.firstChild.focus();}catch(e){}
5394                 }, 50);
5395             }
5396             // disabling
5397             if(t < min) {
5398                 cell.className = " x-date-disabled";
5399                 cell.title = cal.minText;
5400                 return;
5401             }
5402             if(t > max) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.maxText;
5405                 return;
5406             }
5407             if(ddays){
5408                 if(ddays.indexOf(d.getDay()) != -1){
5409                     cell.title = ddaysText;
5410                     cell.className = " x-date-disabled";
5411                 }
5412             }
5413             if(ddMatch && format){
5414                 var fvalue = d.dateFormat(format);
5415                 if(ddMatch.test(fvalue)){
5416                     cell.title = ddText.replace("%0", fvalue);
5417                     cell.className = " x-date-disabled";
5418                 }
5419             }
5420         };
5421
5422         var i = 0;
5423         for(; i < startingPos; i++) {
5424             textEls[i].innerHTML = (++prevStart);
5425             d.setDate(d.getDate()+1);
5426             cells[i].className = "x-date-prevday";
5427             setCellClass(this, cells[i]);
5428         }
5429         for(; i < days; i++){
5430             intDay = i - startingPos + 1;
5431             textEls[i].innerHTML = (intDay);
5432             d.setDate(d.getDate()+1);
5433             cells[i].className = "x-date-active";
5434             setCellClass(this, cells[i]);
5435         }
5436         var extraDays = 0;
5437         for(; i < 42; i++) {
5438              textEls[i].innerHTML = (++extraDays);
5439              d.setDate(d.getDate()+1);
5440              cells[i].className = "x-date-nextday";
5441              setCellClass(this, cells[i]);
5442         }
5443
5444         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5445         this.fireEvent('monthchange', this, date);
5446         
5447         if(!this.internalRender){
5448             var main = this.el.dom.firstChild;
5449             var w = main.offsetWidth;
5450             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5451             Roo.fly(main).setWidth(w);
5452             this.internalRender = true;
5453             // opera does not respect the auto grow header center column
5454             // then, after it gets a width opera refuses to recalculate
5455             // without a second pass
5456             if(Roo.isOpera && !this.secondPass){
5457                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5458                 this.secondPass = true;
5459                 this.update.defer(10, this, [date]);
5460             }
5461         }
5462         
5463         
5464     }
5465 });        /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475 /**
5476  * @class Roo.TabPanel
5477  * @extends Roo.util.Observable
5478  * A lightweight tab container.
5479  * <br><br>
5480  * Usage:
5481  * <pre><code>
5482 // basic tabs 1, built from existing content
5483 var tabs = new Roo.TabPanel("tabs1");
5484 tabs.addTab("script", "View Script");
5485 tabs.addTab("markup", "View Markup");
5486 tabs.activate("script");
5487
5488 // more advanced tabs, built from javascript
5489 var jtabs = new Roo.TabPanel("jtabs");
5490 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5491
5492 // set up the UpdateManager
5493 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5494 var updater = tab2.getUpdateManager();
5495 updater.setDefaultUrl("ajax1.htm");
5496 tab2.on('activate', updater.refresh, updater, true);
5497
5498 // Use setUrl for Ajax loading
5499 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5500 tab3.setUrl("ajax2.htm", null, true);
5501
5502 // Disabled tab
5503 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5504 tab4.disable();
5505
5506 jtabs.activate("jtabs-1");
5507  * </code></pre>
5508  * @constructor
5509  * Create a new TabPanel.
5510  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5511  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5512  */
5513 Roo.TabPanel = function(container, config){
5514     /**
5515     * The container element for this TabPanel.
5516     * @type Roo.Element
5517     */
5518     this.el = Roo.get(container, true);
5519     if(config){
5520         if(typeof config == "boolean"){
5521             this.tabPosition = config ? "bottom" : "top";
5522         }else{
5523             Roo.apply(this, config);
5524         }
5525     }
5526     if(this.tabPosition == "bottom"){
5527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5528         this.el.addClass("x-tabs-bottom");
5529     }
5530     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5531     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5532     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5533     if(Roo.isIE){
5534         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5535     }
5536     if(this.tabPosition != "bottom"){
5537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5538          * @type Roo.Element
5539          */
5540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5541         this.el.addClass("x-tabs-top");
5542     }
5543     this.items = [];
5544
5545     this.bodyEl.setStyle("position", "relative");
5546
5547     this.active = null;
5548     this.activateDelegate = this.activate.createDelegate(this);
5549
5550     this.addEvents({
5551         /**
5552          * @event tabchange
5553          * Fires when the active tab changes
5554          * @param {Roo.TabPanel} this
5555          * @param {Roo.TabPanelItem} activePanel The new active tab
5556          */
5557         "tabchange": true,
5558         /**
5559          * @event beforetabchange
5560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5561          * @param {Roo.TabPanel} this
5562          * @param {Object} e Set cancel to true on this object to cancel the tab change
5563          * @param {Roo.TabPanelItem} tab The tab being changed to
5564          */
5565         "beforetabchange" : true
5566     });
5567
5568     Roo.EventManager.onWindowResize(this.onResize, this);
5569     this.cpad = this.el.getPadding("lr");
5570     this.hiddenCount = 0;
5571
5572
5573     // toolbar on the tabbar support...
5574     if (this.toolbar) {
5575         var tcfg = this.toolbar;
5576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5577         this.toolbar = new Roo.Toolbar(tcfg);
5578         if (Roo.isSafari) {
5579             var tbl = tcfg.container.child('table', true);
5580             tbl.setAttribute('width', '100%');
5581         }
5582         
5583     }
5584    
5585
5586
5587     Roo.TabPanel.superclass.constructor.call(this);
5588 };
5589
5590 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5591     /*
5592      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5593      */
5594     tabPosition : "top",
5595     /*
5596      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5597      */
5598     currentTabWidth : 0,
5599     /*
5600      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5601      */
5602     minTabWidth : 40,
5603     /*
5604      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5605      */
5606     maxTabWidth : 250,
5607     /*
5608      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5609      */
5610     preferredTabWidth : 175,
5611     /*
5612      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5613      */
5614     resizeTabs : false,
5615     /*
5616      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5617      */
5618     monitorResize : true,
5619     /*
5620      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5621      */
5622     toolbar : false,
5623
5624     /**
5625      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5626      * @param {String} id The id of the div to use <b>or create</b>
5627      * @param {String} text The text for the tab
5628      * @param {String} content (optional) Content to put in the TabPanelItem body
5629      * @param {Boolean} closable (optional) True to create a close icon on the tab
5630      * @return {Roo.TabPanelItem} The created TabPanelItem
5631      */
5632     addTab : function(id, text, content, closable){
5633         var item = new Roo.TabPanelItem(this, id, text, closable);
5634         this.addTabItem(item);
5635         if(content){
5636             item.setContent(content);
5637         }
5638         return item;
5639     },
5640
5641     /**
5642      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5643      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5644      * @return {Roo.TabPanelItem}
5645      */
5646     getTab : function(id){
5647         return this.items[id];
5648     },
5649
5650     /**
5651      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5652      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5653      */
5654     hideTab : function(id){
5655         var t = this.items[id];
5656         if(!t.isHidden()){
5657            t.setHidden(true);
5658            this.hiddenCount++;
5659            this.autoSizeTabs();
5660         }
5661     },
5662
5663     /**
5664      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5665      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5666      */
5667     unhideTab : function(id){
5668         var t = this.items[id];
5669         if(t.isHidden()){
5670            t.setHidden(false);
5671            this.hiddenCount--;
5672            this.autoSizeTabs();
5673         }
5674     },
5675
5676     /**
5677      * Adds an existing {@link Roo.TabPanelItem}.
5678      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5679      */
5680     addTabItem : function(item){
5681         this.items[item.id] = item;
5682         this.items.push(item);
5683         if(this.resizeTabs){
5684            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5685            this.autoSizeTabs();
5686         }else{
5687             item.autoSize();
5688         }
5689     },
5690
5691     /**
5692      * Removes a {@link Roo.TabPanelItem}.
5693      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5694      */
5695     removeTab : function(id){
5696         var items = this.items;
5697         var tab = items[id];
5698         if(!tab) { return; }
5699         var index = items.indexOf(tab);
5700         if(this.active == tab && items.length > 1){
5701             var newTab = this.getNextAvailable(index);
5702             if(newTab) {
5703                 newTab.activate();
5704             }
5705         }
5706         this.stripEl.dom.removeChild(tab.pnode.dom);
5707         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5708             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5709         }
5710         items.splice(index, 1);
5711         delete this.items[tab.id];
5712         tab.fireEvent("close", tab);
5713         tab.purgeListeners();
5714         this.autoSizeTabs();
5715     },
5716
5717     getNextAvailable : function(start){
5718         var items = this.items;
5719         var index = start;
5720         // look for a next tab that will slide over to
5721         // replace the one being removed
5722         while(index < items.length){
5723             var item = items[++index];
5724             if(item && !item.isHidden()){
5725                 return item;
5726             }
5727         }
5728         // if one isn't found select the previous tab (on the left)
5729         index = start;
5730         while(index >= 0){
5731             var item = items[--index];
5732             if(item && !item.isHidden()){
5733                 return item;
5734             }
5735         }
5736         return null;
5737     },
5738
5739     /**
5740      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5741      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5742      */
5743     disableTab : function(id){
5744         var tab = this.items[id];
5745         if(tab && this.active != tab){
5746             tab.disable();
5747         }
5748     },
5749
5750     /**
5751      * Enables a {@link Roo.TabPanelItem} that is disabled.
5752      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5753      */
5754     enableTab : function(id){
5755         var tab = this.items[id];
5756         tab.enable();
5757     },
5758
5759     /**
5760      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5761      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5762      * @return {Roo.TabPanelItem} The TabPanelItem.
5763      */
5764     activate : function(id){
5765         var tab = this.items[id];
5766         if(!tab){
5767             return null;
5768         }
5769         if(tab == this.active || tab.disabled){
5770             return tab;
5771         }
5772         var e = {};
5773         this.fireEvent("beforetabchange", this, e, tab);
5774         if(e.cancel !== true && !tab.disabled){
5775             if(this.active){
5776                 this.active.hide();
5777             }
5778             this.active = this.items[id];
5779             this.active.show();
5780             this.fireEvent("tabchange", this, this.active);
5781         }
5782         return tab;
5783     },
5784
5785     /**
5786      * Gets the active {@link Roo.TabPanelItem}.
5787      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5788      */
5789     getActiveTab : function(){
5790         return this.active;
5791     },
5792
5793     /**
5794      * Updates the tab body element to fit the height of the container element
5795      * for overflow scrolling
5796      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5797      */
5798     syncHeight : function(targetHeight){
5799         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5800         var bm = this.bodyEl.getMargins();
5801         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5802         this.bodyEl.setHeight(newHeight);
5803         return newHeight;
5804     },
5805
5806     onResize : function(){
5807         if(this.monitorResize){
5808             this.autoSizeTabs();
5809         }
5810     },
5811
5812     /**
5813      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5814      */
5815     beginUpdate : function(){
5816         this.updating = true;
5817     },
5818
5819     /**
5820      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5821      */
5822     endUpdate : function(){
5823         this.updating = false;
5824         this.autoSizeTabs();
5825     },
5826
5827     /**
5828      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5829      */
5830     autoSizeTabs : function(){
5831         var count = this.items.length;
5832         var vcount = count - this.hiddenCount;
5833         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5834             return;
5835         }
5836         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5837         var availWidth = Math.floor(w / vcount);
5838         var b = this.stripBody;
5839         if(b.getWidth() > w){
5840             var tabs = this.items;
5841             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5842             if(availWidth < this.minTabWidth){
5843                 /*if(!this.sleft){    // incomplete scrolling code
5844                     this.createScrollButtons();
5845                 }
5846                 this.showScroll();
5847                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5848             }
5849         }else{
5850             if(this.currentTabWidth < this.preferredTabWidth){
5851                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Returns the number of tabs in this TabPanel.
5858      * @return {Number}
5859      */
5860      getCount : function(){
5861          return this.items.length;
5862      },
5863
5864     /**
5865      * Resizes all the tabs to the passed width
5866      * @param {Number} The new width
5867      */
5868     setTabWidth : function(width){
5869         this.currentTabWidth = width;
5870         for(var i = 0, len = this.items.length; i < len; i++) {
5871                 if(!this.items[i].isHidden()) {
5872                 this.items[i].setWidth(width);
5873             }
5874         }
5875     },
5876
5877     /**
5878      * Destroys this TabPanel
5879      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5880      */
5881     destroy : function(removeEl){
5882         Roo.EventManager.removeResizeListener(this.onResize, this);
5883         for(var i = 0, len = this.items.length; i < len; i++){
5884             this.items[i].purgeListeners();
5885         }
5886         if(removeEl === true){
5887             this.el.update("");
5888             this.el.remove();
5889         }
5890     }
5891 });
5892
5893 /**
5894  * @class Roo.TabPanelItem
5895  * @extends Roo.util.Observable
5896  * Represents an individual item (tab plus body) in a TabPanel.
5897  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5898  * @param {String} id The id of this TabPanelItem
5899  * @param {String} text The text for the tab of this TabPanelItem
5900  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5901  */
5902 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5903     /**
5904      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5905      * @type Roo.TabPanel
5906      */
5907     this.tabPanel = tabPanel;
5908     /**
5909      * The id for this TabPanelItem
5910      * @type String
5911      */
5912     this.id = id;
5913     /** @private */
5914     this.disabled = false;
5915     /** @private */
5916     this.text = text;
5917     /** @private */
5918     this.loaded = false;
5919     this.closable = closable;
5920
5921     /**
5922      * The body element for this TabPanelItem.
5923      * @type Roo.Element
5924      */
5925     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5926     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5927     this.bodyEl.setStyle("display", "block");
5928     this.bodyEl.setStyle("zoom", "1");
5929     this.hideAction();
5930
5931     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5932     /** @private */
5933     this.el = Roo.get(els.el, true);
5934     this.inner = Roo.get(els.inner, true);
5935     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5936     this.pnode = Roo.get(els.el.parentNode, true);
5937     this.el.on("mousedown", this.onTabMouseDown, this);
5938     this.el.on("click", this.onTabClick, this);
5939     /** @private */
5940     if(closable){
5941         var c = Roo.get(els.close, true);
5942         c.dom.title = this.closeText;
5943         c.addClassOnOver("close-over");
5944         c.on("click", this.closeClick, this);
5945      }
5946
5947     this.addEvents({
5948          /**
5949          * @event activate
5950          * Fires when this tab becomes the active tab.
5951          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5952          * @param {Roo.TabPanelItem} this
5953          */
5954         "activate": true,
5955         /**
5956          * @event beforeclose
5957          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5958          * @param {Roo.TabPanelItem} this
5959          * @param {Object} e Set cancel to true on this object to cancel the close.
5960          */
5961         "beforeclose": true,
5962         /**
5963          * @event close
5964          * Fires when this tab is closed.
5965          * @param {Roo.TabPanelItem} this
5966          */
5967          "close": true,
5968         /**
5969          * @event deactivate
5970          * Fires when this tab is no longer the active tab.
5971          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5972          * @param {Roo.TabPanelItem} this
5973          */
5974          "deactivate" : true
5975     });
5976     this.hidden = false;
5977
5978     Roo.TabPanelItem.superclass.constructor.call(this);
5979 };
5980
5981 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5982     purgeListeners : function(){
5983        Roo.util.Observable.prototype.purgeListeners.call(this);
5984        this.el.removeAllListeners();
5985     },
5986     /**
5987      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5988      */
5989     show : function(){
5990         this.pnode.addClass("on");
5991         this.showAction();
5992         if(Roo.isOpera){
5993             this.tabPanel.stripWrap.repaint();
5994         }
5995         this.fireEvent("activate", this.tabPanel, this);
5996     },
5997
5998     /**
5999      * Returns true if this tab is the active tab.
6000      * @return {Boolean}
6001      */
6002     isActive : function(){
6003         return this.tabPanel.getActiveTab() == this;
6004     },
6005
6006     /**
6007      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6008      */
6009     hide : function(){
6010         this.pnode.removeClass("on");
6011         this.hideAction();
6012         this.fireEvent("deactivate", this.tabPanel, this);
6013     },
6014
6015     hideAction : function(){
6016         this.bodyEl.hide();
6017         this.bodyEl.setStyle("position", "absolute");
6018         this.bodyEl.setLeft("-20000px");
6019         this.bodyEl.setTop("-20000px");
6020     },
6021
6022     showAction : function(){
6023         this.bodyEl.setStyle("position", "relative");
6024         this.bodyEl.setTop("");
6025         this.bodyEl.setLeft("");
6026         this.bodyEl.show();
6027     },
6028
6029     /**
6030      * Set the tooltip for the tab.
6031      * @param {String} tooltip The tab's tooltip
6032      */
6033     setTooltip : function(text){
6034         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6035             this.textEl.dom.qtip = text;
6036             this.textEl.dom.removeAttribute('title');
6037         }else{
6038             this.textEl.dom.title = text;
6039         }
6040     },
6041
6042     onTabClick : function(e){
6043         e.preventDefault();
6044         this.tabPanel.activate(this.id);
6045     },
6046
6047     onTabMouseDown : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     getWidth : function(){
6053         return this.inner.getWidth();
6054     },
6055
6056     setWidth : function(width){
6057         var iwidth = width - this.pnode.getPadding("lr");
6058         this.inner.setWidth(iwidth);
6059         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6060         this.pnode.setWidth(width);
6061     },
6062
6063     /**
6064      * Show or hide the tab
6065      * @param {Boolean} hidden True to hide or false to show.
6066      */
6067     setHidden : function(hidden){
6068         this.hidden = hidden;
6069         this.pnode.setStyle("display", hidden ? "none" : "");
6070     },
6071
6072     /**
6073      * Returns true if this tab is "hidden"
6074      * @return {Boolean}
6075      */
6076     isHidden : function(){
6077         return this.hidden;
6078     },
6079
6080     /**
6081      * Returns the text for this tab
6082      * @return {String}
6083      */
6084     getText : function(){
6085         return this.text;
6086     },
6087
6088     autoSize : function(){
6089         //this.el.beginMeasure();
6090         this.textEl.setWidth(1);
6091         /*
6092          *  #2804 [new] Tabs in Roojs
6093          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6094          */
6095         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6096         //this.el.endMeasure();
6097     },
6098
6099     /**
6100      * Sets the text for the tab (Note: this also sets the tooltip text)
6101      * @param {String} text The tab's text and tooltip
6102      */
6103     setText : function(text){
6104         this.text = text;
6105         this.textEl.update(text);
6106         this.setTooltip(text);
6107         if(!this.tabPanel.resizeTabs){
6108             this.autoSize();
6109         }
6110     },
6111     /**
6112      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6113      */
6114     activate : function(){
6115         this.tabPanel.activate(this.id);
6116     },
6117
6118     /**
6119      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6120      */
6121     disable : function(){
6122         if(this.tabPanel.active != this){
6123             this.disabled = true;
6124             this.pnode.addClass("disabled");
6125         }
6126     },
6127
6128     /**
6129      * Enables this TabPanelItem if it was previously disabled.
6130      */
6131     enable : function(){
6132         this.disabled = false;
6133         this.pnode.removeClass("disabled");
6134     },
6135
6136     /**
6137      * Sets the content for this TabPanelItem.
6138      * @param {String} content The content
6139      * @param {Boolean} loadScripts true to look for and load scripts
6140      */
6141     setContent : function(content, loadScripts){
6142         this.bodyEl.update(content, loadScripts);
6143     },
6144
6145     /**
6146      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6147      * @return {Roo.UpdateManager} The UpdateManager
6148      */
6149     getUpdateManager : function(){
6150         return this.bodyEl.getUpdateManager();
6151     },
6152
6153     /**
6154      * Set a URL to be used to load the content for this TabPanelItem.
6155      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6156      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6157      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6158      * @return {Roo.UpdateManager} The UpdateManager
6159      */
6160     setUrl : function(url, params, loadOnce){
6161         if(this.refreshDelegate){
6162             this.un('activate', this.refreshDelegate);
6163         }
6164         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6165         this.on("activate", this.refreshDelegate);
6166         return this.bodyEl.getUpdateManager();
6167     },
6168
6169     /** @private */
6170     _handleRefresh : function(url, params, loadOnce){
6171         if(!loadOnce || !this.loaded){
6172             var updater = this.bodyEl.getUpdateManager();
6173             updater.update(url, params, this._setLoaded.createDelegate(this));
6174         }
6175     },
6176
6177     /**
6178      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6179      *   Will fail silently if the setUrl method has not been called.
6180      *   This does not activate the panel, just updates its content.
6181      */
6182     refresh : function(){
6183         if(this.refreshDelegate){
6184            this.loaded = false;
6185            this.refreshDelegate();
6186         }
6187     },
6188
6189     /** @private */
6190     _setLoaded : function(){
6191         this.loaded = true;
6192     },
6193
6194     /** @private */
6195     closeClick : function(e){
6196         var o = {};
6197         e.stopEvent();
6198         this.fireEvent("beforeclose", this, o);
6199         if(o.cancel !== true){
6200             this.tabPanel.removeTab(this.id);
6201         }
6202     },
6203     /**
6204      * The text displayed in the tooltip for the close icon.
6205      * @type String
6206      */
6207     closeText : "Close this tab"
6208 });
6209
6210 /** @private */
6211 Roo.TabPanel.prototype.createStrip = function(container){
6212     var strip = document.createElement("div");
6213     strip.className = "x-tabs-wrap";
6214     container.appendChild(strip);
6215     return strip;
6216 };
6217 /** @private */
6218 Roo.TabPanel.prototype.createStripList = function(strip){
6219     // div wrapper for retard IE
6220     // returns the "tr" element.
6221     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6222         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6223         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6224     return strip.firstChild.firstChild.firstChild.firstChild;
6225 };
6226 /** @private */
6227 Roo.TabPanel.prototype.createBody = function(container){
6228     var body = document.createElement("div");
6229     Roo.id(body, "tab-body");
6230     Roo.fly(body).addClass("x-tabs-body");
6231     container.appendChild(body);
6232     return body;
6233 };
6234 /** @private */
6235 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6236     var body = Roo.getDom(id);
6237     if(!body){
6238         body = document.createElement("div");
6239         body.id = id;
6240     }
6241     Roo.fly(body).addClass("x-tabs-item-body");
6242     bodyEl.insertBefore(body, bodyEl.firstChild);
6243     return body;
6244 };
6245 /** @private */
6246 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6247     var td = document.createElement("td");
6248     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6249     //stripEl.appendChild(td);
6250     if(closable){
6251         td.className = "x-tabs-closable";
6252         if(!this.closeTpl){
6253             this.closeTpl = new Roo.Template(
6254                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6255                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6256                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6257             );
6258         }
6259         var el = this.closeTpl.overwrite(td, {"text": text});
6260         var close = el.getElementsByTagName("div")[0];
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "close": close, "inner": inner};
6263     } else {
6264         if(!this.tabTpl){
6265             this.tabTpl = new Roo.Template(
6266                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6267                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6268             );
6269         }
6270         var el = this.tabTpl.overwrite(td, {"text": text});
6271         var inner = el.getElementsByTagName("em")[0];
6272         return {"el": el, "inner": inner};
6273     }
6274 };/*
6275  * Based on:
6276  * Ext JS Library 1.1.1
6277  * Copyright(c) 2006-2007, Ext JS, LLC.
6278  *
6279  * Originally Released Under LGPL - original licence link has changed is not relivant.
6280  *
6281  * Fork - LGPL
6282  * <script type="text/javascript">
6283  */
6284
6285 /**
6286  * @class Roo.Button
6287  * @extends Roo.util.Observable
6288  * Simple Button class
6289  * @cfg {String} text The button text
6290  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6291  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6292  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6293  * @cfg {Object} scope The scope of the handler
6294  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6295  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6296  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6297  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6298  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6299  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6300    applies if enableToggle = true)
6301  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6302  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6303   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6304  * @constructor
6305  * Create a new button
6306  * @param {Object} config The config object
6307  */
6308 Roo.Button = function(renderTo, config)
6309 {
6310     if (!config) {
6311         config = renderTo;
6312         renderTo = config.renderTo || false;
6313     }
6314     
6315     Roo.apply(this, config);
6316     this.addEvents({
6317         /**
6318              * @event click
6319              * Fires when this button is clicked
6320              * @param {Button} this
6321              * @param {EventObject} e The click event
6322              */
6323             "click" : true,
6324         /**
6325              * @event toggle
6326              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6327              * @param {Button} this
6328              * @param {Boolean} pressed
6329              */
6330             "toggle" : true,
6331         /**
6332              * @event mouseover
6333              * Fires when the mouse hovers over the button
6334              * @param {Button} this
6335              * @param {Event} e The event object
6336              */
6337         'mouseover' : true,
6338         /**
6339              * @event mouseout
6340              * Fires when the mouse exits the button
6341              * @param {Button} this
6342              * @param {Event} e The event object
6343              */
6344         'mouseout': true,
6345          /**
6346              * @event render
6347              * Fires when the button is rendered
6348              * @param {Button} this
6349              */
6350         'render': true
6351     });
6352     if(this.menu){
6353         this.menu = Roo.menu.MenuMgr.get(this.menu);
6354     }
6355     // register listeners first!!  - so render can be captured..
6356     Roo.util.Observable.call(this);
6357     if(renderTo){
6358         this.render(renderTo);
6359     }
6360     
6361   
6362 };
6363
6364 Roo.extend(Roo.Button, Roo.util.Observable, {
6365     /**
6366      * 
6367      */
6368     
6369     /**
6370      * Read-only. True if this button is hidden
6371      * @type Boolean
6372      */
6373     hidden : false,
6374     /**
6375      * Read-only. True if this button is disabled
6376      * @type Boolean
6377      */
6378     disabled : false,
6379     /**
6380      * Read-only. True if this button is pressed (only if enableToggle = true)
6381      * @type Boolean
6382      */
6383     pressed : false,
6384
6385     /**
6386      * @cfg {Number} tabIndex 
6387      * The DOM tabIndex for this button (defaults to undefined)
6388      */
6389     tabIndex : undefined,
6390
6391     /**
6392      * @cfg {Boolean} enableToggle
6393      * True to enable pressed/not pressed toggling (defaults to false)
6394      */
6395     enableToggle: false,
6396     /**
6397      * @cfg {Roo.menu.Menu} menu
6398      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6399      */
6400     menu : undefined,
6401     /**
6402      * @cfg {String} menuAlign
6403      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6404      */
6405     menuAlign : "tl-bl?",
6406
6407     /**
6408      * @cfg {String} iconCls
6409      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6410      */
6411     iconCls : undefined,
6412     /**
6413      * @cfg {String} type
6414      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6415      */
6416     type : 'button',
6417
6418     // private
6419     menuClassTarget: 'tr',
6420
6421     /**
6422      * @cfg {String} clickEvent
6423      * The type of event to map to the button's event handler (defaults to 'click')
6424      */
6425     clickEvent : 'click',
6426
6427     /**
6428      * @cfg {Boolean} handleMouseEvents
6429      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6430      */
6431     handleMouseEvents : true,
6432
6433     /**
6434      * @cfg {String} tooltipType
6435      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6436      */
6437     tooltipType : 'qtip',
6438
6439     /**
6440      * @cfg {String} cls
6441      * A CSS class to apply to the button's main element.
6442      */
6443     
6444     /**
6445      * @cfg {Roo.Template} template (Optional)
6446      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6447      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6448      * require code modifications if required elements (e.g. a button) aren't present.
6449      */
6450
6451     // private
6452     render : function(renderTo){
6453         var btn;
6454         if(this.hideParent){
6455             this.parentEl = Roo.get(renderTo);
6456         }
6457         if(!this.dhconfig){
6458             if(!this.template){
6459                 if(!Roo.Button.buttonTemplate){
6460                     // hideous table template
6461                     Roo.Button.buttonTemplate = new Roo.Template(
6462                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6463                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6464                         "</tr></tbody></table>");
6465                 }
6466                 this.template = Roo.Button.buttonTemplate;
6467             }
6468             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6469             var btnEl = btn.child("button:first");
6470             btnEl.on('focus', this.onFocus, this);
6471             btnEl.on('blur', this.onBlur, this);
6472             if(this.cls){
6473                 btn.addClass(this.cls);
6474             }
6475             if(this.icon){
6476                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6477             }
6478             if(this.iconCls){
6479                 btnEl.addClass(this.iconCls);
6480                 if(!this.cls){
6481                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6482                 }
6483             }
6484             if(this.tabIndex !== undefined){
6485                 btnEl.dom.tabIndex = this.tabIndex;
6486             }
6487             if(this.tooltip){
6488                 if(typeof this.tooltip == 'object'){
6489                     Roo.QuickTips.tips(Roo.apply({
6490                           target: btnEl.id
6491                     }, this.tooltip));
6492                 } else {
6493                     btnEl.dom[this.tooltipType] = this.tooltip;
6494                 }
6495             }
6496         }else{
6497             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6498         }
6499         this.el = btn;
6500         if(this.id){
6501             this.el.dom.id = this.el.id = this.id;
6502         }
6503         if(this.menu){
6504             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6505             this.menu.on("show", this.onMenuShow, this);
6506             this.menu.on("hide", this.onMenuHide, this);
6507         }
6508         btn.addClass("x-btn");
6509         if(Roo.isIE && !Roo.isIE7){
6510             this.autoWidth.defer(1, this);
6511         }else{
6512             this.autoWidth();
6513         }
6514         if(this.handleMouseEvents){
6515             btn.on("mouseover", this.onMouseOver, this);
6516             btn.on("mouseout", this.onMouseOut, this);
6517             btn.on("mousedown", this.onMouseDown, this);
6518         }
6519         btn.on(this.clickEvent, this.onClick, this);
6520         //btn.on("mouseup", this.onMouseUp, this);
6521         if(this.hidden){
6522             this.hide();
6523         }
6524         if(this.disabled){
6525             this.disable();
6526         }
6527         Roo.ButtonToggleMgr.register(this);
6528         if(this.pressed){
6529             this.el.addClass("x-btn-pressed");
6530         }
6531         if(this.repeat){
6532             var repeater = new Roo.util.ClickRepeater(btn,
6533                 typeof this.repeat == "object" ? this.repeat : {}
6534             );
6535             repeater.on("click", this.onClick,  this);
6536         }
6537         
6538         this.fireEvent('render', this);
6539         
6540     },
6541     /**
6542      * Returns the button's underlying element
6543      * @return {Roo.Element} The element
6544      */
6545     getEl : function(){
6546         return this.el;  
6547     },
6548     
6549     /**
6550      * Destroys this Button and removes any listeners.
6551      */
6552     destroy : function(){
6553         Roo.ButtonToggleMgr.unregister(this);
6554         this.el.removeAllListeners();
6555         this.purgeListeners();
6556         this.el.remove();
6557     },
6558
6559     // private
6560     autoWidth : function(){
6561         if(this.el){
6562             this.el.setWidth("auto");
6563             if(Roo.isIE7 && Roo.isStrict){
6564                 var ib = this.el.child('button');
6565                 if(ib && ib.getWidth() > 20){
6566                     ib.clip();
6567                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6568                 }
6569             }
6570             if(this.minWidth){
6571                 if(this.hidden){
6572                     this.el.beginMeasure();
6573                 }
6574                 if(this.el.getWidth() < this.minWidth){
6575                     this.el.setWidth(this.minWidth);
6576                 }
6577                 if(this.hidden){
6578                     this.el.endMeasure();
6579                 }
6580             }
6581         }
6582     },
6583
6584     /**
6585      * Assigns this button's click handler
6586      * @param {Function} handler The function to call when the button is clicked
6587      * @param {Object} scope (optional) Scope for the function passed in
6588      */
6589     setHandler : function(handler, scope){
6590         this.handler = handler;
6591         this.scope = scope;  
6592     },
6593     
6594     /**
6595      * Sets this button's text
6596      * @param {String} text The button text
6597      */
6598     setText : function(text){
6599         this.text = text;
6600         if(this.el){
6601             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6602         }
6603         this.autoWidth();
6604     },
6605     
6606     /**
6607      * Gets the text for this button
6608      * @return {String} The button text
6609      */
6610     getText : function(){
6611         return this.text;  
6612     },
6613     
6614     /**
6615      * Show this button
6616      */
6617     show: function(){
6618         this.hidden = false;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6621         }
6622     },
6623     
6624     /**
6625      * Hide this button
6626      */
6627     hide: function(){
6628         this.hidden = true;
6629         if(this.el){
6630             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6631         }
6632     },
6633     
6634     /**
6635      * Convenience function for boolean show/hide
6636      * @param {Boolean} visible True to show, false to hide
6637      */
6638     setVisible: function(visible){
6639         if(visible) {
6640             this.show();
6641         }else{
6642             this.hide();
6643         }
6644     },
6645     
6646     /**
6647      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6648      * @param {Boolean} state (optional) Force a particular state
6649      */
6650     toggle : function(state){
6651         state = state === undefined ? !this.pressed : state;
6652         if(state != this.pressed){
6653             if(state){
6654                 this.el.addClass("x-btn-pressed");
6655                 this.pressed = true;
6656                 this.fireEvent("toggle", this, true);
6657             }else{
6658                 this.el.removeClass("x-btn-pressed");
6659                 this.pressed = false;
6660                 this.fireEvent("toggle", this, false);
6661             }
6662             if(this.toggleHandler){
6663                 this.toggleHandler.call(this.scope || this, this, state);
6664             }
6665         }
6666     },
6667     
6668     /**
6669      * Focus the button
6670      */
6671     focus : function(){
6672         this.el.child('button:first').focus();
6673     },
6674     
6675     /**
6676      * Disable this button
6677      */
6678     disable : function(){
6679         if(this.el){
6680             this.el.addClass("x-btn-disabled");
6681         }
6682         this.disabled = true;
6683     },
6684     
6685     /**
6686      * Enable this button
6687      */
6688     enable : function(){
6689         if(this.el){
6690             this.el.removeClass("x-btn-disabled");
6691         }
6692         this.disabled = false;
6693     },
6694
6695     /**
6696      * Convenience function for boolean enable/disable
6697      * @param {Boolean} enabled True to enable, false to disable
6698      */
6699     setDisabled : function(v){
6700         this[v !== true ? "enable" : "disable"]();
6701     },
6702
6703     // private
6704     onClick : function(e)
6705     {
6706         if(e){
6707             e.preventDefault();
6708         }
6709         if(e.button != 0){
6710             return;
6711         }
6712         if(!this.disabled){
6713             if(this.enableToggle){
6714                 this.toggle();
6715             }
6716             if(this.menu && !this.menu.isVisible()){
6717                 this.menu.show(this.el, this.menuAlign);
6718             }
6719             this.fireEvent("click", this, e);
6720             if(this.handler){
6721                 this.el.removeClass("x-btn-over");
6722                 this.handler.call(this.scope || this, this, e);
6723             }
6724         }
6725     },
6726     // private
6727     onMouseOver : function(e){
6728         if(!this.disabled){
6729             this.el.addClass("x-btn-over");
6730             this.fireEvent('mouseover', this, e);
6731         }
6732     },
6733     // private
6734     onMouseOut : function(e){
6735         if(!e.within(this.el,  true)){
6736             this.el.removeClass("x-btn-over");
6737             this.fireEvent('mouseout', this, e);
6738         }
6739     },
6740     // private
6741     onFocus : function(e){
6742         if(!this.disabled){
6743             this.el.addClass("x-btn-focus");
6744         }
6745     },
6746     // private
6747     onBlur : function(e){
6748         this.el.removeClass("x-btn-focus");
6749     },
6750     // private
6751     onMouseDown : function(e){
6752         if(!this.disabled && e.button == 0){
6753             this.el.addClass("x-btn-click");
6754             Roo.get(document).on('mouseup', this.onMouseUp, this);
6755         }
6756     },
6757     // private
6758     onMouseUp : function(e){
6759         if(e.button == 0){
6760             this.el.removeClass("x-btn-click");
6761             Roo.get(document).un('mouseup', this.onMouseUp, this);
6762         }
6763     },
6764     // private
6765     onMenuShow : function(e){
6766         this.el.addClass("x-btn-menu-active");
6767     },
6768     // private
6769     onMenuHide : function(e){
6770         this.el.removeClass("x-btn-menu-active");
6771     }   
6772 });
6773
6774 // Private utility class used by Button
6775 Roo.ButtonToggleMgr = function(){
6776    var groups = {};
6777    
6778    function toggleGroup(btn, state){
6779        if(state){
6780            var g = groups[btn.toggleGroup];
6781            for(var i = 0, l = g.length; i < l; i++){
6782                if(g[i] != btn){
6783                    g[i].toggle(false);
6784                }
6785            }
6786        }
6787    }
6788    
6789    return {
6790        register : function(btn){
6791            if(!btn.toggleGroup){
6792                return;
6793            }
6794            var g = groups[btn.toggleGroup];
6795            if(!g){
6796                g = groups[btn.toggleGroup] = [];
6797            }
6798            g.push(btn);
6799            btn.on("toggle", toggleGroup);
6800        },
6801        
6802        unregister : function(btn){
6803            if(!btn.toggleGroup){
6804                return;
6805            }
6806            var g = groups[btn.toggleGroup];
6807            if(g){
6808                g.remove(btn);
6809                btn.un("toggle", toggleGroup);
6810            }
6811        }
6812    };
6813 }();/*
6814  * Based on:
6815  * Ext JS Library 1.1.1
6816  * Copyright(c) 2006-2007, Ext JS, LLC.
6817  *
6818  * Originally Released Under LGPL - original licence link has changed is not relivant.
6819  *
6820  * Fork - LGPL
6821  * <script type="text/javascript">
6822  */
6823  
6824 /**
6825  * @class Roo.SplitButton
6826  * @extends Roo.Button
6827  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6828  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6829  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6830  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6831  * @cfg {String} arrowTooltip The title attribute of the arrow
6832  * @constructor
6833  * Create a new menu button
6834  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6835  * @param {Object} config The config object
6836  */
6837 Roo.SplitButton = function(renderTo, config){
6838     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6839     /**
6840      * @event arrowclick
6841      * Fires when this button's arrow is clicked
6842      * @param {SplitButton} this
6843      * @param {EventObject} e The click event
6844      */
6845     this.addEvents({"arrowclick":true});
6846 };
6847
6848 Roo.extend(Roo.SplitButton, Roo.Button, {
6849     render : function(renderTo){
6850         // this is one sweet looking template!
6851         var tpl = new Roo.Template(
6852             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6853             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6854             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6855             "</tbody></table></td><td>",
6856             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6857             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6858             "</tbody></table></td></tr></table>"
6859         );
6860         var btn = tpl.append(renderTo, [this.text, this.type], true);
6861         var btnEl = btn.child("button");
6862         if(this.cls){
6863             btn.addClass(this.cls);
6864         }
6865         if(this.icon){
6866             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6867         }
6868         if(this.iconCls){
6869             btnEl.addClass(this.iconCls);
6870             if(!this.cls){
6871                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6872             }
6873         }
6874         this.el = btn;
6875         if(this.handleMouseEvents){
6876             btn.on("mouseover", this.onMouseOver, this);
6877             btn.on("mouseout", this.onMouseOut, this);
6878             btn.on("mousedown", this.onMouseDown, this);
6879             btn.on("mouseup", this.onMouseUp, this);
6880         }
6881         btn.on(this.clickEvent, this.onClick, this);
6882         if(this.tooltip){
6883             if(typeof this.tooltip == 'object'){
6884                 Roo.QuickTips.tips(Roo.apply({
6885                       target: btnEl.id
6886                 }, this.tooltip));
6887             } else {
6888                 btnEl.dom[this.tooltipType] = this.tooltip;
6889             }
6890         }
6891         if(this.arrowTooltip){
6892             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6893         }
6894         if(this.hidden){
6895             this.hide();
6896         }
6897         if(this.disabled){
6898             this.disable();
6899         }
6900         if(this.pressed){
6901             this.el.addClass("x-btn-pressed");
6902         }
6903         if(Roo.isIE && !Roo.isIE7){
6904             this.autoWidth.defer(1, this);
6905         }else{
6906             this.autoWidth();
6907         }
6908         if(this.menu){
6909             this.menu.on("show", this.onMenuShow, this);
6910             this.menu.on("hide", this.onMenuHide, this);
6911         }
6912         this.fireEvent('render', this);
6913     },
6914
6915     // private
6916     autoWidth : function(){
6917         if(this.el){
6918             var tbl = this.el.child("table:first");
6919             var tbl2 = this.el.child("table:last");
6920             this.el.setWidth("auto");
6921             tbl.setWidth("auto");
6922             if(Roo.isIE7 && Roo.isStrict){
6923                 var ib = this.el.child('button:first');
6924                 if(ib && ib.getWidth() > 20){
6925                     ib.clip();
6926                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6927                 }
6928             }
6929             if(this.minWidth){
6930                 if(this.hidden){
6931                     this.el.beginMeasure();
6932                 }
6933                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6934                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6935                 }
6936                 if(this.hidden){
6937                     this.el.endMeasure();
6938                 }
6939             }
6940             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6941         } 
6942     },
6943     /**
6944      * Sets this button's click handler
6945      * @param {Function} handler The function to call when the button is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setHandler : function(handler, scope){
6949         this.handler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Sets this button's arrow click handler
6955      * @param {Function} handler The function to call when the arrow is clicked
6956      * @param {Object} scope (optional) Scope for the function passed above
6957      */
6958     setArrowHandler : function(handler, scope){
6959         this.arrowHandler = handler;
6960         this.scope = scope;  
6961     },
6962     
6963     /**
6964      * Focus the button
6965      */
6966     focus : function(){
6967         if(this.el){
6968             this.el.child("button:first").focus();
6969         }
6970     },
6971
6972     // private
6973     onClick : function(e){
6974         e.preventDefault();
6975         if(!this.disabled){
6976             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6977                 if(this.menu && !this.menu.isVisible()){
6978                     this.menu.show(this.el, this.menuAlign);
6979                 }
6980                 this.fireEvent("arrowclick", this, e);
6981                 if(this.arrowHandler){
6982                     this.arrowHandler.call(this.scope || this, this, e);
6983                 }
6984             }else{
6985                 this.fireEvent("click", this, e);
6986                 if(this.handler){
6987                     this.handler.call(this.scope || this, this, e);
6988                 }
6989             }
6990         }
6991     },
6992     // private
6993     onMouseDown : function(e){
6994         if(!this.disabled){
6995             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6996         }
6997     },
6998     // private
6999     onMouseUp : function(e){
7000         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7001     }   
7002 });
7003
7004
7005 // backwards compat
7006 Roo.MenuButton = Roo.SplitButton;/*
7007  * Based on:
7008  * Ext JS Library 1.1.1
7009  * Copyright(c) 2006-2007, Ext JS, LLC.
7010  *
7011  * Originally Released Under LGPL - original licence link has changed is not relivant.
7012  *
7013  * Fork - LGPL
7014  * <script type="text/javascript">
7015  */
7016
7017 /**
7018  * @class Roo.Toolbar
7019  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7020  * Basic Toolbar class.
7021  * @constructor
7022  * Creates a new Toolbar
7023  * @param {Object} container The config object
7024  */ 
7025 Roo.Toolbar = function(container, buttons, config)
7026 {
7027     /// old consturctor format still supported..
7028     if(container instanceof Array){ // omit the container for later rendering
7029         buttons = container;
7030         config = buttons;
7031         container = null;
7032     }
7033     if (typeof(container) == 'object' && container.xtype) {
7034         config = container;
7035         container = config.container;
7036         buttons = config.buttons || []; // not really - use items!!
7037     }
7038     var xitems = [];
7039     if (config && config.items) {
7040         xitems = config.items;
7041         delete config.items;
7042     }
7043     Roo.apply(this, config);
7044     this.buttons = buttons;
7045     
7046     if(container){
7047         this.render(container);
7048     }
7049     this.xitems = xitems;
7050     Roo.each(xitems, function(b) {
7051         this.add(b);
7052     }, this);
7053     
7054 };
7055
7056 Roo.Toolbar.prototype = {
7057     /**
7058      * @cfg {Array} items
7059      * array of button configs or elements to add (will be converted to a MixedCollection)
7060      */
7061     items: false,
7062     /**
7063      * @cfg {String/HTMLElement/Element} container
7064      * The id or element that will contain the toolbar
7065      */
7066     // private
7067     render : function(ct){
7068         this.el = Roo.get(ct);
7069         if(this.cls){
7070             this.el.addClass(this.cls);
7071         }
7072         // using a table allows for vertical alignment
7073         // 100% width is needed by Safari...
7074         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7075         this.tr = this.el.child("tr", true);
7076         var autoId = 0;
7077         this.items = new Roo.util.MixedCollection(false, function(o){
7078             return o.id || ("item" + (++autoId));
7079         });
7080         if(this.buttons){
7081             this.add.apply(this, this.buttons);
7082             delete this.buttons;
7083         }
7084     },
7085
7086     /**
7087      * Adds element(s) to the toolbar -- this function takes a variable number of 
7088      * arguments of mixed type and adds them to the toolbar.
7089      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7090      * <ul>
7091      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7092      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7093      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7094      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7095      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7096      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7097      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7098      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7099      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7100      * </ul>
7101      * @param {Mixed} arg2
7102      * @param {Mixed} etc.
7103      */
7104     add : function(){
7105         var a = arguments, l = a.length;
7106         for(var i = 0; i < l; i++){
7107             this._add(a[i]);
7108         }
7109     },
7110     // private..
7111     _add : function(el) {
7112         
7113         if (el.xtype) {
7114             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7115         }
7116         
7117         if (el.applyTo){ // some kind of form field
7118             return this.addField(el);
7119         } 
7120         if (el.render){ // some kind of Toolbar.Item
7121             return this.addItem(el);
7122         }
7123         if (typeof el == "string"){ // string
7124             if(el == "separator" || el == "-"){
7125                 return this.addSeparator();
7126             }
7127             if (el == " "){
7128                 return this.addSpacer();
7129             }
7130             if(el == "->"){
7131                 return this.addFill();
7132             }
7133             return this.addText(el);
7134             
7135         }
7136         if(el.tagName){ // element
7137             return this.addElement(el);
7138         }
7139         if(typeof el == "object"){ // must be button config?
7140             return this.addButton(el);
7141         }
7142         // and now what?!?!
7143         return false;
7144         
7145     },
7146     
7147     /**
7148      * Add an Xtype element
7149      * @param {Object} xtype Xtype Object
7150      * @return {Object} created Object
7151      */
7152     addxtype : function(e){
7153         return this.add(e);  
7154     },
7155     
7156     /**
7157      * Returns the Element for this toolbar.
7158      * @return {Roo.Element}
7159      */
7160     getEl : function(){
7161         return this.el;  
7162     },
7163     
7164     /**
7165      * Adds a separator
7166      * @return {Roo.Toolbar.Item} The separator item
7167      */
7168     addSeparator : function(){
7169         return this.addItem(new Roo.Toolbar.Separator());
7170     },
7171
7172     /**
7173      * Adds a spacer element
7174      * @return {Roo.Toolbar.Spacer} The spacer item
7175      */
7176     addSpacer : function(){
7177         return this.addItem(new Roo.Toolbar.Spacer());
7178     },
7179
7180     /**
7181      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7182      * @return {Roo.Toolbar.Fill} The fill item
7183      */
7184     addFill : function(){
7185         return this.addItem(new Roo.Toolbar.Fill());
7186     },
7187
7188     /**
7189      * Adds any standard HTML element to the toolbar
7190      * @param {String/HTMLElement/Element} el The element or id of the element to add
7191      * @return {Roo.Toolbar.Item} The element's item
7192      */
7193     addElement : function(el){
7194         return this.addItem(new Roo.Toolbar.Item(el));
7195     },
7196     /**
7197      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7198      * @type Roo.util.MixedCollection  
7199      */
7200     items : false,
7201      
7202     /**
7203      * Adds any Toolbar.Item or subclass
7204      * @param {Roo.Toolbar.Item} item
7205      * @return {Roo.Toolbar.Item} The item
7206      */
7207     addItem : function(item){
7208         var td = this.nextBlock();
7209         item.render(td);
7210         this.items.add(item);
7211         return item;
7212     },
7213     
7214     /**
7215      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7216      * @param {Object/Array} config A button config or array of configs
7217      * @return {Roo.Toolbar.Button/Array}
7218      */
7219     addButton : function(config){
7220         if(config instanceof Array){
7221             var buttons = [];
7222             for(var i = 0, len = config.length; i < len; i++) {
7223                 buttons.push(this.addButton(config[i]));
7224             }
7225             return buttons;
7226         }
7227         var b = config;
7228         if(!(config instanceof Roo.Toolbar.Button)){
7229             b = config.split ?
7230                 new Roo.Toolbar.SplitButton(config) :
7231                 new Roo.Toolbar.Button(config);
7232         }
7233         var td = this.nextBlock();
7234         b.render(td);
7235         this.items.add(b);
7236         return b;
7237     },
7238     
7239     /**
7240      * Adds text to the toolbar
7241      * @param {String} text The text to add
7242      * @return {Roo.Toolbar.Item} The element's item
7243      */
7244     addText : function(text){
7245         return this.addItem(new Roo.Toolbar.TextItem(text));
7246     },
7247     
7248     /**
7249      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7250      * @param {Number} index The index where the item is to be inserted
7251      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7252      * @return {Roo.Toolbar.Button/Item}
7253      */
7254     insertButton : function(index, item){
7255         if(item instanceof Array){
7256             var buttons = [];
7257             for(var i = 0, len = item.length; i < len; i++) {
7258                buttons.push(this.insertButton(index + i, item[i]));
7259             }
7260             return buttons;
7261         }
7262         if (!(item instanceof Roo.Toolbar.Button)){
7263            item = new Roo.Toolbar.Button(item);
7264         }
7265         var td = document.createElement("td");
7266         this.tr.insertBefore(td, this.tr.childNodes[index]);
7267         item.render(td);
7268         this.items.insert(index, item);
7269         return item;
7270     },
7271     
7272     /**
7273      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7274      * @param {Object} config
7275      * @return {Roo.Toolbar.Item} The element's item
7276      */
7277     addDom : function(config, returnEl){
7278         var td = this.nextBlock();
7279         Roo.DomHelper.overwrite(td, config);
7280         var ti = new Roo.Toolbar.Item(td.firstChild);
7281         ti.render(td);
7282         this.items.add(ti);
7283         return ti;
7284     },
7285
7286     /**
7287      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7288      * @type Roo.util.MixedCollection  
7289      */
7290     fields : false,
7291     
7292     /**
7293      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7294      * Note: the field should not have been rendered yet. For a field that has already been
7295      * rendered, use {@link #addElement}.
7296      * @param {Roo.form.Field} field
7297      * @return {Roo.ToolbarItem}
7298      */
7299      
7300       
7301     addField : function(field) {
7302         if (!this.fields) {
7303             var autoId = 0;
7304             this.fields = new Roo.util.MixedCollection(false, function(o){
7305                 return o.id || ("item" + (++autoId));
7306             });
7307
7308         }
7309         
7310         var td = this.nextBlock();
7311         field.render(td);
7312         var ti = new Roo.Toolbar.Item(td.firstChild);
7313         ti.render(td);
7314         this.items.add(ti);
7315         this.fields.add(field);
7316         return ti;
7317     },
7318     /**
7319      * Hide the toolbar
7320      * @method hide
7321      */
7322      
7323       
7324     hide : function()
7325     {
7326         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7327         this.el.child('div').hide();
7328     },
7329     /**
7330      * Show the toolbar
7331      * @method show
7332      */
7333     show : function()
7334     {
7335         this.el.child('div').show();
7336     },
7337       
7338     // private
7339     nextBlock : function(){
7340         var td = document.createElement("td");
7341         this.tr.appendChild(td);
7342         return td;
7343     },
7344
7345     // private
7346     destroy : function(){
7347         if(this.items){ // rendered?
7348             Roo.destroy.apply(Roo, this.items.items);
7349         }
7350         if(this.fields){ // rendered?
7351             Roo.destroy.apply(Roo, this.fields.items);
7352         }
7353         Roo.Element.uncache(this.el, this.tr);
7354     }
7355 };
7356
7357 /**
7358  * @class Roo.Toolbar.Item
7359  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7360  * @constructor
7361  * Creates a new Item
7362  * @param {HTMLElement} el 
7363  */
7364 Roo.Toolbar.Item = function(el){
7365     var cfg = {};
7366     if (typeof (el.xtype) != 'undefined') {
7367         cfg = el;
7368         el = cfg.el;
7369     }
7370     
7371     this.el = Roo.getDom(el);
7372     this.id = Roo.id(this.el);
7373     this.hidden = false;
7374     
7375     this.addEvents({
7376          /**
7377              * @event render
7378              * Fires when the button is rendered
7379              * @param {Button} this
7380              */
7381         'render': true
7382     });
7383     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7384 };
7385 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7386 //Roo.Toolbar.Item.prototype = {
7387     
7388     /**
7389      * Get this item's HTML Element
7390      * @return {HTMLElement}
7391      */
7392     getEl : function(){
7393        return this.el;  
7394     },
7395
7396     // private
7397     render : function(td){
7398         
7399          this.td = td;
7400         td.appendChild(this.el);
7401         
7402         this.fireEvent('render', this);
7403     },
7404     
7405     /**
7406      * Removes and destroys this item.
7407      */
7408     destroy : function(){
7409         this.td.parentNode.removeChild(this.td);
7410     },
7411     
7412     /**
7413      * Shows this item.
7414      */
7415     show: function(){
7416         this.hidden = false;
7417         this.td.style.display = "";
7418     },
7419     
7420     /**
7421      * Hides this item.
7422      */
7423     hide: function(){
7424         this.hidden = true;
7425         this.td.style.display = "none";
7426     },
7427     
7428     /**
7429      * Convenience function for boolean show/hide.
7430      * @param {Boolean} visible true to show/false to hide
7431      */
7432     setVisible: function(visible){
7433         if(visible) {
7434             this.show();
7435         }else{
7436             this.hide();
7437         }
7438     },
7439     
7440     /**
7441      * Try to focus this item.
7442      */
7443     focus : function(){
7444         Roo.fly(this.el).focus();
7445     },
7446     
7447     /**
7448      * Disables this item.
7449      */
7450     disable : function(){
7451         Roo.fly(this.td).addClass("x-item-disabled");
7452         this.disabled = true;
7453         this.el.disabled = true;
7454     },
7455     
7456     /**
7457      * Enables this item.
7458      */
7459     enable : function(){
7460         Roo.fly(this.td).removeClass("x-item-disabled");
7461         this.disabled = false;
7462         this.el.disabled = false;
7463     }
7464 });
7465
7466
7467 /**
7468  * @class Roo.Toolbar.Separator
7469  * @extends Roo.Toolbar.Item
7470  * A simple toolbar separator class
7471  * @constructor
7472  * Creates a new Separator
7473  */
7474 Roo.Toolbar.Separator = function(cfg){
7475     
7476     var s = document.createElement("span");
7477     s.className = "ytb-sep";
7478     if (cfg) {
7479         cfg.el = s;
7480     }
7481     
7482     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Spacer
7492  * @extends Roo.Toolbar.Item
7493  * A simple element that adds extra horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Spacer = function(cfg){
7498     var s = document.createElement("div");
7499     s.className = "ytb-spacer";
7500     if (cfg) {
7501         cfg.el = s;
7502     }
7503     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7504 };
7505 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7506     enable:Roo.emptyFn,
7507     disable:Roo.emptyFn,
7508     focus:Roo.emptyFn
7509 });
7510
7511 /**
7512  * @class Roo.Toolbar.Fill
7513  * @extends Roo.Toolbar.Spacer
7514  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7515  * @constructor
7516  * Creates a new Spacer
7517  */
7518 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7519     // private
7520     render : function(td){
7521         td.style.width = '100%';
7522         Roo.Toolbar.Fill.superclass.render.call(this, td);
7523     }
7524 });
7525
7526 /**
7527  * @class Roo.Toolbar.TextItem
7528  * @extends Roo.Toolbar.Item
7529  * A simple class that renders text directly into a toolbar.
7530  * @constructor
7531  * Creates a new TextItem
7532  * @cfg {string} text 
7533  */
7534 Roo.Toolbar.TextItem = function(cfg){
7535     var  text = cfg || "";
7536     if (typeof(cfg) == 'object') {
7537         text = cfg.text || "";
7538     }  else {
7539         cfg = null;
7540     }
7541     var s = document.createElement("span");
7542     s.className = "ytb-text";
7543     s.innerHTML = text;
7544     if (cfg) {
7545         cfg.el  = s;
7546     }
7547     
7548     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7549 };
7550 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7551     
7552      
7553     enable:Roo.emptyFn,
7554     disable:Roo.emptyFn,
7555     focus:Roo.emptyFn,
7556      /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.el.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.el.style.display = "none";
7570     }
7571     
7572 });
7573
7574 /**
7575  * @class Roo.Toolbar.Button
7576  * @extends Roo.Button
7577  * A button that renders into a toolbar.
7578  * @constructor
7579  * Creates a new Button
7580  * @param {Object} config A standard {@link Roo.Button} config object
7581  */
7582 Roo.Toolbar.Button = function(config){
7583     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7584 };
7585 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7586 {
7587     
7588     
7589     render : function(td){
7590         this.td = td;
7591         Roo.Toolbar.Button.superclass.render.call(this, td);
7592     },
7593     
7594     /**
7595      * Removes and destroys this button
7596      */
7597     destroy : function(){
7598         Roo.Toolbar.Button.superclass.destroy.call(this);
7599         this.td.parentNode.removeChild(this.td);
7600     },
7601     
7602     /**
7603      * Shows this button
7604      */
7605     show: function(){
7606         this.hidden = false;
7607         this.td.style.display = "";
7608     },
7609     
7610     /**
7611      * Hides this button
7612      */
7613     hide: function(){
7614         this.hidden = true;
7615         this.td.style.display = "none";
7616     },
7617
7618     /**
7619      * Disables this item
7620      */
7621     disable : function(){
7622         Roo.fly(this.td).addClass("x-item-disabled");
7623         this.disabled = true;
7624     },
7625
7626     /**
7627      * Enables this item
7628      */
7629     enable : function(){
7630         Roo.fly(this.td).removeClass("x-item-disabled");
7631         this.disabled = false;
7632     }
7633 });
7634 // backwards compat
7635 Roo.ToolbarButton = Roo.Toolbar.Button;
7636
7637 /**
7638  * @class Roo.Toolbar.SplitButton
7639  * @extends Roo.SplitButton
7640  * A menu button that renders into a toolbar.
7641  * @constructor
7642  * Creates a new SplitButton
7643  * @param {Object} config A standard {@link Roo.SplitButton} config object
7644  */
7645 Roo.Toolbar.SplitButton = function(config){
7646     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7647 };
7648 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7649     render : function(td){
7650         this.td = td;
7651         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7652     },
7653     
7654     /**
7655      * Removes and destroys this button
7656      */
7657     destroy : function(){
7658         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7659         this.td.parentNode.removeChild(this.td);
7660     },
7661     
7662     /**
7663      * Shows this button
7664      */
7665     show: function(){
7666         this.hidden = false;
7667         this.td.style.display = "";
7668     },
7669     
7670     /**
7671      * Hides this button
7672      */
7673     hide: function(){
7674         this.hidden = true;
7675         this.td.style.display = "none";
7676     }
7677 });
7678
7679 // backwards compat
7680 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  
7691 /**
7692  * @class Roo.PagingToolbar
7693  * @extends Roo.Toolbar
7694  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7695  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7696  * @constructor
7697  * Create a new PagingToolbar
7698  * @param {Object} config The config object
7699  */
7700 Roo.PagingToolbar = function(el, ds, config)
7701 {
7702     // old args format still supported... - xtype is prefered..
7703     if (typeof(el) == 'object' && el.xtype) {
7704         // created from xtype...
7705         config = el;
7706         ds = el.dataSource;
7707         el = config.container;
7708     }
7709     var items = [];
7710     if (config.items) {
7711         items = config.items;
7712         config.items = [];
7713     }
7714     
7715     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7716     this.ds = ds;
7717     this.cursor = 0;
7718     this.renderButtons(this.el);
7719     this.bind(ds);
7720     
7721     // supprot items array.
7722    
7723     Roo.each(items, function(e) {
7724         this.add(Roo.factory(e));
7725     },this);
7726     
7727 };
7728
7729 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7730    
7731     /**
7732      * @cfg {String/HTMLElement/Element} container
7733      * container The id or element that will contain the toolbar
7734      */
7735     /**
7736      * @cfg {Boolean} displayInfo
7737      * True to display the displayMsg (defaults to false)
7738      */
7739     
7740     
7741     /**
7742      * @cfg {Number} pageSize
7743      * The number of records to display per page (defaults to 20)
7744      */
7745     pageSize: 20,
7746     /**
7747      * @cfg {String} displayMsg
7748      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7749      */
7750     displayMsg : 'Displaying {0} - {1} of {2}',
7751     /**
7752      * @cfg {String} emptyMsg
7753      * The message to display when no records are found (defaults to "No data to display")
7754      */
7755     emptyMsg : 'No data to display',
7756     /**
7757      * Customizable piece of the default paging text (defaults to "Page")
7758      * @type String
7759      */
7760     beforePageText : "Page",
7761     /**
7762      * Customizable piece of the default paging text (defaults to "of %0")
7763      * @type String
7764      */
7765     afterPageText : "of {0}",
7766     /**
7767      * Customizable piece of the default paging text (defaults to "First Page")
7768      * @type String
7769      */
7770     firstText : "First Page",
7771     /**
7772      * Customizable piece of the default paging text (defaults to "Previous Page")
7773      * @type String
7774      */
7775     prevText : "Previous Page",
7776     /**
7777      * Customizable piece of the default paging text (defaults to "Next Page")
7778      * @type String
7779      */
7780     nextText : "Next Page",
7781     /**
7782      * Customizable piece of the default paging text (defaults to "Last Page")
7783      * @type String
7784      */
7785     lastText : "Last Page",
7786     /**
7787      * Customizable piece of the default paging text (defaults to "Refresh")
7788      * @type String
7789      */
7790     refreshText : "Refresh",
7791
7792     // private
7793     renderButtons : function(el){
7794         Roo.PagingToolbar.superclass.render.call(this, el);
7795         this.first = this.addButton({
7796             tooltip: this.firstText,
7797             cls: "x-btn-icon x-grid-page-first",
7798             disabled: true,
7799             handler: this.onClick.createDelegate(this, ["first"])
7800         });
7801         this.prev = this.addButton({
7802             tooltip: this.prevText,
7803             cls: "x-btn-icon x-grid-page-prev",
7804             disabled: true,
7805             handler: this.onClick.createDelegate(this, ["prev"])
7806         });
7807         //this.addSeparator();
7808         this.add(this.beforePageText);
7809         this.field = Roo.get(this.addDom({
7810            tag: "input",
7811            type: "text",
7812            size: "3",
7813            value: "1",
7814            cls: "x-grid-page-number"
7815         }).el);
7816         this.field.on("keydown", this.onPagingKeydown, this);
7817         this.field.on("focus", function(){this.dom.select();});
7818         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7819         this.field.setHeight(18);
7820         //this.addSeparator();
7821         this.next = this.addButton({
7822             tooltip: this.nextText,
7823             cls: "x-btn-icon x-grid-page-next",
7824             disabled: true,
7825             handler: this.onClick.createDelegate(this, ["next"])
7826         });
7827         this.last = this.addButton({
7828             tooltip: this.lastText,
7829             cls: "x-btn-icon x-grid-page-last",
7830             disabled: true,
7831             handler: this.onClick.createDelegate(this, ["last"])
7832         });
7833         //this.addSeparator();
7834         this.loading = this.addButton({
7835             tooltip: this.refreshText,
7836             cls: "x-btn-icon x-grid-loading",
7837             handler: this.onClick.createDelegate(this, ["refresh"])
7838         });
7839
7840         if(this.displayInfo){
7841             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7842         }
7843     },
7844
7845     // private
7846     updateInfo : function(){
7847         if(this.displayEl){
7848             var count = this.ds.getCount();
7849             var msg = count == 0 ?
7850                 this.emptyMsg :
7851                 String.format(
7852                     this.displayMsg,
7853                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7854                 );
7855             this.displayEl.update(msg);
7856         }
7857     },
7858
7859     // private
7860     onLoad : function(ds, r, o){
7861        this.cursor = o.params ? o.params.start : 0;
7862        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7863
7864        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7865        this.field.dom.value = ap;
7866        this.first.setDisabled(ap == 1);
7867        this.prev.setDisabled(ap == 1);
7868        this.next.setDisabled(ap == ps);
7869        this.last.setDisabled(ap == ps);
7870        this.loading.enable();
7871        this.updateInfo();
7872     },
7873
7874     // private
7875     getPageData : function(){
7876         var total = this.ds.getTotalCount();
7877         return {
7878             total : total,
7879             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7880             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7881         };
7882     },
7883
7884     // private
7885     onLoadError : function(){
7886         this.loading.enable();
7887     },
7888
7889     // private
7890     onPagingKeydown : function(e){
7891         var k = e.getKey();
7892         var d = this.getPageData();
7893         if(k == e.RETURN){
7894             var v = this.field.dom.value, pageNum;
7895             if(!v || isNaN(pageNum = parseInt(v, 10))){
7896                 this.field.dom.value = d.activePage;
7897                 return;
7898             }
7899             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7900             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7901             e.stopEvent();
7902         }
7903         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
7904         {
7905           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7906           this.field.dom.value = pageNum;
7907           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7908           e.stopEvent();
7909         }
7910         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7911         {
7912           var v = this.field.dom.value, pageNum; 
7913           var increment = (e.shiftKey) ? 10 : 1;
7914           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7915             increment *= -1;
7916           }
7917           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7918             this.field.dom.value = d.activePage;
7919             return;
7920           }
7921           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7922           {
7923             this.field.dom.value = parseInt(v, 10) + increment;
7924             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7925             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7926           }
7927           e.stopEvent();
7928         }
7929     },
7930
7931     // private
7932     beforeLoad : function(){
7933         if(this.loading){
7934             this.loading.disable();
7935         }
7936     },
7937
7938     // private
7939     onClick : function(which){
7940         var ds = this.ds;
7941         switch(which){
7942             case "first":
7943                 ds.load({params:{start: 0, limit: this.pageSize}});
7944             break;
7945             case "prev":
7946                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7947             break;
7948             case "next":
7949                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7950             break;
7951             case "last":
7952                 var total = ds.getTotalCount();
7953                 var extra = total % this.pageSize;
7954                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7955                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7956             break;
7957             case "refresh":
7958                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7959             break;
7960         }
7961     },
7962
7963     /**
7964      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7965      * @param {Roo.data.Store} store The data store to unbind
7966      */
7967     unbind : function(ds){
7968         ds.un("beforeload", this.beforeLoad, this);
7969         ds.un("load", this.onLoad, this);
7970         ds.un("loadexception", this.onLoadError, this);
7971         ds.un("remove", this.updateInfo, this);
7972         ds.un("add", this.updateInfo, this);
7973         this.ds = undefined;
7974     },
7975
7976     /**
7977      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7978      * @param {Roo.data.Store} store The data store to bind
7979      */
7980     bind : function(ds){
7981         ds.on("beforeload", this.beforeLoad, this);
7982         ds.on("load", this.onLoad, this);
7983         ds.on("loadexception", this.onLoadError, this);
7984         ds.on("remove", this.updateInfo, this);
7985         ds.on("add", this.updateInfo, this);
7986         this.ds = ds;
7987     }
7988 });/*
7989  * Based on:
7990  * Ext JS Library 1.1.1
7991  * Copyright(c) 2006-2007, Ext JS, LLC.
7992  *
7993  * Originally Released Under LGPL - original licence link has changed is not relivant.
7994  *
7995  * Fork - LGPL
7996  * <script type="text/javascript">
7997  */
7998
7999 /**
8000  * @class Roo.Resizable
8001  * @extends Roo.util.Observable
8002  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8003  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8004  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8005  * the element will be wrapped for you automatically.</p>
8006  * <p>Here is the list of valid resize handles:</p>
8007  * <pre>
8008 Value   Description
8009 ------  -------------------
8010  'n'     north
8011  's'     south
8012  'e'     east
8013  'w'     west
8014  'nw'    northwest
8015  'sw'    southwest
8016  'se'    southeast
8017  'ne'    northeast
8018  'hd'    horizontal drag
8019  'all'   all
8020 </pre>
8021  * <p>Here's an example showing the creation of a typical Resizable:</p>
8022  * <pre><code>
8023 var resizer = new Roo.Resizable("element-id", {
8024     handles: 'all',
8025     minWidth: 200,
8026     minHeight: 100,
8027     maxWidth: 500,
8028     maxHeight: 400,
8029     pinned: true
8030 });
8031 resizer.on("resize", myHandler);
8032 </code></pre>
8033  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8034  * resizer.east.setDisplayed(false);</p>
8035  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8036  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8037  * resize operation's new size (defaults to [0, 0])
8038  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8039  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8040  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8041  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8042  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8043  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8044  * @cfg {Number} width The width of the element in pixels (defaults to null)
8045  * @cfg {Number} height The height of the element in pixels (defaults to null)
8046  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8047  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8048  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8049  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8050  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8051  * in favor of the handles config option (defaults to false)
8052  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8053  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8054  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8055  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8056  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8057  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8058  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8059  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8060  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8061  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8062  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8063  * @constructor
8064  * Create a new resizable component
8065  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8066  * @param {Object} config configuration options
8067   */
8068 Roo.Resizable = function(el, config)
8069 {
8070     this.el = Roo.get(el);
8071
8072     if(config && config.wrap){
8073         config.resizeChild = this.el;
8074         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8075         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8076         this.el.setStyle("overflow", "hidden");
8077         this.el.setPositioning(config.resizeChild.getPositioning());
8078         config.resizeChild.clearPositioning();
8079         if(!config.width || !config.height){
8080             var csize = config.resizeChild.getSize();
8081             this.el.setSize(csize.width, csize.height);
8082         }
8083         if(config.pinned && !config.adjustments){
8084             config.adjustments = "auto";
8085         }
8086     }
8087
8088     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8089     this.proxy.unselectable();
8090     this.proxy.enableDisplayMode('block');
8091
8092     Roo.apply(this, config);
8093
8094     if(this.pinned){
8095         this.disableTrackOver = true;
8096         this.el.addClass("x-resizable-pinned");
8097     }
8098     // if the element isn't positioned, make it relative
8099     var position = this.el.getStyle("position");
8100     if(position != "absolute" && position != "fixed"){
8101         this.el.setStyle("position", "relative");
8102     }
8103     if(!this.handles){ // no handles passed, must be legacy style
8104         this.handles = 's,e,se';
8105         if(this.multiDirectional){
8106             this.handles += ',n,w';
8107         }
8108     }
8109     if(this.handles == "all"){
8110         this.handles = "n s e w ne nw se sw";
8111     }
8112     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8113     var ps = Roo.Resizable.positions;
8114     for(var i = 0, len = hs.length; i < len; i++){
8115         if(hs[i] && ps[hs[i]]){
8116             var pos = ps[hs[i]];
8117             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8118         }
8119     }
8120     // legacy
8121     this.corner = this.southeast;
8122     
8123     // updateBox = the box can move..
8124     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8125         this.updateBox = true;
8126     }
8127
8128     this.activeHandle = null;
8129
8130     if(this.resizeChild){
8131         if(typeof this.resizeChild == "boolean"){
8132             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8133         }else{
8134             this.resizeChild = Roo.get(this.resizeChild, true);
8135         }
8136     }
8137     
8138     if(this.adjustments == "auto"){
8139         var rc = this.resizeChild;
8140         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8141         if(rc && (hw || hn)){
8142             rc.position("relative");
8143             rc.setLeft(hw ? hw.el.getWidth() : 0);
8144             rc.setTop(hn ? hn.el.getHeight() : 0);
8145         }
8146         this.adjustments = [
8147             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8148             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8149         ];
8150     }
8151
8152     if(this.draggable){
8153         this.dd = this.dynamic ?
8154             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8155         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8156     }
8157
8158     // public events
8159     this.addEvents({
8160         /**
8161          * @event beforeresize
8162          * Fired before resize is allowed. Set enabled to false to cancel resize.
8163          * @param {Roo.Resizable} this
8164          * @param {Roo.EventObject} e The mousedown event
8165          */
8166         "beforeresize" : true,
8167         /**
8168          * @event resizing
8169          * Fired a resizing.
8170          * @param {Roo.Resizable} this
8171          * @param {Number} x The new x position
8172          * @param {Number} y The new y position
8173          * @param {Number} w The new w width
8174          * @param {Number} h The new h hight
8175          * @param {Roo.EventObject} e The mouseup event
8176          */
8177         "resizing" : true,
8178         /**
8179          * @event resize
8180          * Fired after a resize.
8181          * @param {Roo.Resizable} this
8182          * @param {Number} width The new width
8183          * @param {Number} height The new height
8184          * @param {Roo.EventObject} e The mouseup event
8185          */
8186         "resize" : true
8187     });
8188
8189     if(this.width !== null && this.height !== null){
8190         this.resizeTo(this.width, this.height);
8191     }else{
8192         this.updateChildSize();
8193     }
8194     if(Roo.isIE){
8195         this.el.dom.style.zoom = 1;
8196     }
8197     Roo.Resizable.superclass.constructor.call(this);
8198 };
8199
8200 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8201         resizeChild : false,
8202         adjustments : [0, 0],
8203         minWidth : 5,
8204         minHeight : 5,
8205         maxWidth : 10000,
8206         maxHeight : 10000,
8207         enabled : true,
8208         animate : false,
8209         duration : .35,
8210         dynamic : false,
8211         handles : false,
8212         multiDirectional : false,
8213         disableTrackOver : false,
8214         easing : 'easeOutStrong',
8215         widthIncrement : 0,
8216         heightIncrement : 0,
8217         pinned : false,
8218         width : null,
8219         height : null,
8220         preserveRatio : false,
8221         transparent: false,
8222         minX: 0,
8223         minY: 0,
8224         draggable: false,
8225
8226         /**
8227          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8228          */
8229         constrainTo: undefined,
8230         /**
8231          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8232          */
8233         resizeRegion: undefined,
8234
8235
8236     /**
8237      * Perform a manual resize
8238      * @param {Number} width
8239      * @param {Number} height
8240      */
8241     resizeTo : function(width, height){
8242         this.el.setSize(width, height);
8243         this.updateChildSize();
8244         this.fireEvent("resize", this, width, height, null);
8245     },
8246
8247     // private
8248     startSizing : function(e, handle){
8249         this.fireEvent("beforeresize", this, e);
8250         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8251
8252             if(!this.overlay){
8253                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8254                 this.overlay.unselectable();
8255                 this.overlay.enableDisplayMode("block");
8256                 this.overlay.on("mousemove", this.onMouseMove, this);
8257                 this.overlay.on("mouseup", this.onMouseUp, this);
8258             }
8259             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8260
8261             this.resizing = true;
8262             this.startBox = this.el.getBox();
8263             this.startPoint = e.getXY();
8264             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8265                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8266
8267             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8268             this.overlay.show();
8269
8270             if(this.constrainTo) {
8271                 var ct = Roo.get(this.constrainTo);
8272                 this.resizeRegion = ct.getRegion().adjust(
8273                     ct.getFrameWidth('t'),
8274                     ct.getFrameWidth('l'),
8275                     -ct.getFrameWidth('b'),
8276                     -ct.getFrameWidth('r')
8277                 );
8278             }
8279
8280             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8281             this.proxy.show();
8282             this.proxy.setBox(this.startBox);
8283             if(!this.dynamic){
8284                 this.proxy.setStyle('visibility', 'visible');
8285             }
8286         }
8287     },
8288
8289     // private
8290     onMouseDown : function(handle, e){
8291         if(this.enabled){
8292             e.stopEvent();
8293             this.activeHandle = handle;
8294             this.startSizing(e, handle);
8295         }
8296     },
8297
8298     // private
8299     onMouseUp : function(e){
8300         var size = this.resizeElement();
8301         this.resizing = false;
8302         this.handleOut();
8303         this.overlay.hide();
8304         this.proxy.hide();
8305         this.fireEvent("resize", this, size.width, size.height, e);
8306     },
8307
8308     // private
8309     updateChildSize : function(){
8310         
8311         if(this.resizeChild){
8312             var el = this.el;
8313             var child = this.resizeChild;
8314             var adj = this.adjustments;
8315             if(el.dom.offsetWidth){
8316                 var b = el.getSize(true);
8317                 child.setSize(b.width+adj[0], b.height+adj[1]);
8318             }
8319             // Second call here for IE
8320             // The first call enables instant resizing and
8321             // the second call corrects scroll bars if they
8322             // exist
8323             if(Roo.isIE){
8324                 setTimeout(function(){
8325                     if(el.dom.offsetWidth){
8326                         var b = el.getSize(true);
8327                         child.setSize(b.width+adj[0], b.height+adj[1]);
8328                     }
8329                 }, 10);
8330             }
8331         }
8332     },
8333
8334     // private
8335     snap : function(value, inc, min){
8336         if(!inc || !value) {
8337             return value;
8338         }
8339         var newValue = value;
8340         var m = value % inc;
8341         if(m > 0){
8342             if(m > (inc/2)){
8343                 newValue = value + (inc-m);
8344             }else{
8345                 newValue = value - m;
8346             }
8347         }
8348         return Math.max(min, newValue);
8349     },
8350
8351     // private
8352     resizeElement : function(){
8353         var box = this.proxy.getBox();
8354         if(this.updateBox){
8355             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8356         }else{
8357             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8358         }
8359         this.updateChildSize();
8360         if(!this.dynamic){
8361             this.proxy.hide();
8362         }
8363         return box;
8364     },
8365
8366     // private
8367     constrain : function(v, diff, m, mx){
8368         if(v - diff < m){
8369             diff = v - m;
8370         }else if(v - diff > mx){
8371             diff = mx - v;
8372         }
8373         return diff;
8374     },
8375
8376     // private
8377     onMouseMove : function(e){
8378         
8379         if(this.enabled){
8380             try{// try catch so if something goes wrong the user doesn't get hung
8381
8382             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8383                 return;
8384             }
8385
8386             //var curXY = this.startPoint;
8387             var curSize = this.curSize || this.startBox;
8388             var x = this.startBox.x, y = this.startBox.y;
8389             var ox = x, oy = y;
8390             var w = curSize.width, h = curSize.height;
8391             var ow = w, oh = h;
8392             var mw = this.minWidth, mh = this.minHeight;
8393             var mxw = this.maxWidth, mxh = this.maxHeight;
8394             var wi = this.widthIncrement;
8395             var hi = this.heightIncrement;
8396
8397             var eventXY = e.getXY();
8398             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8399             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8400
8401             var pos = this.activeHandle.position;
8402
8403             switch(pos){
8404                 case "east":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     break;
8408              
8409                 case "south":
8410                     h += diffY;
8411                     h = Math.min(Math.max(mh, h), mxh);
8412                     break;
8413                 case "southeast":
8414                     w += diffX;
8415                     h += diffY;
8416                     w = Math.min(Math.max(mw, w), mxw);
8417                     h = Math.min(Math.max(mh, h), mxh);
8418                     break;
8419                 case "north":
8420                     diffY = this.constrain(h, diffY, mh, mxh);
8421                     y += diffY;
8422                     h -= diffY;
8423                     break;
8424                 case "hdrag":
8425                     
8426                     if (wi) {
8427                         var adiffX = Math.abs(diffX);
8428                         var sub = (adiffX % wi); // how much 
8429                         if (sub > (wi/2)) { // far enough to snap
8430                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8431                         } else {
8432                             // remove difference.. 
8433                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8434                         }
8435                     }
8436                     x += diffX;
8437                     x = Math.max(this.minX, x);
8438                     break;
8439                 case "west":
8440                     diffX = this.constrain(w, diffX, mw, mxw);
8441                     x += diffX;
8442                     w -= diffX;
8443                     break;
8444                 case "northeast":
8445                     w += diffX;
8446                     w = Math.min(Math.max(mw, w), mxw);
8447                     diffY = this.constrain(h, diffY, mh, mxh);
8448                     y += diffY;
8449                     h -= diffY;
8450                     break;
8451                 case "northwest":
8452                     diffX = this.constrain(w, diffX, mw, mxw);
8453                     diffY = this.constrain(h, diffY, mh, mxh);
8454                     y += diffY;
8455                     h -= diffY;
8456                     x += diffX;
8457                     w -= diffX;
8458                     break;
8459                case "southwest":
8460                     diffX = this.constrain(w, diffX, mw, mxw);
8461                     h += diffY;
8462                     h = Math.min(Math.max(mh, h), mxh);
8463                     x += diffX;
8464                     w -= diffX;
8465                     break;
8466             }
8467
8468             var sw = this.snap(w, wi, mw);
8469             var sh = this.snap(h, hi, mh);
8470             if(sw != w || sh != h){
8471                 switch(pos){
8472                     case "northeast":
8473                         y -= sh - h;
8474                     break;
8475                     case "north":
8476                         y -= sh - h;
8477                         break;
8478                     case "southwest":
8479                         x -= sw - w;
8480                     break;
8481                     case "west":
8482                         x -= sw - w;
8483                         break;
8484                     case "northwest":
8485                         x -= sw - w;
8486                         y -= sh - h;
8487                     break;
8488                 }
8489                 w = sw;
8490                 h = sh;
8491             }
8492
8493             if(this.preserveRatio){
8494                 switch(pos){
8495                     case "southeast":
8496                     case "east":
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                        break;
8501                     case "south":
8502                         w = ow * (h/oh);
8503                         w = Math.min(Math.max(mw, w), mxw);
8504                         h = oh * (w/ow);
8505                         break;
8506                     case "northeast":
8507                         w = ow * (h/oh);
8508                         w = Math.min(Math.max(mw, w), mxw);
8509                         h = oh * (w/ow);
8510                     break;
8511                     case "north":
8512                         var tw = w;
8513                         w = ow * (h/oh);
8514                         w = Math.min(Math.max(mw, w), mxw);
8515                         h = oh * (w/ow);
8516                         x += (tw - w) / 2;
8517                         break;
8518                     case "southwest":
8519                         h = oh * (w/ow);
8520                         h = Math.min(Math.max(mh, h), mxh);
8521                         var tw = w;
8522                         w = ow * (h/oh);
8523                         x += tw - w;
8524                         break;
8525                     case "west":
8526                         var th = h;
8527                         h = oh * (w/ow);
8528                         h = Math.min(Math.max(mh, h), mxh);
8529                         y += (th - h) / 2;
8530                         var tw = w;
8531                         w = ow * (h/oh);
8532                         x += tw - w;
8533                        break;
8534                     case "northwest":
8535                         var tw = w;
8536                         var th = h;
8537                         h = oh * (w/ow);
8538                         h = Math.min(Math.max(mh, h), mxh);
8539                         w = ow * (h/oh);
8540                         y += th - h;
8541                         x += tw - w;
8542                        break;
8543
8544                 }
8545             }
8546             if (pos == 'hdrag') {
8547                 w = ow;
8548             }
8549             this.proxy.setBounds(x, y, w, h);
8550             if(this.dynamic){
8551                 this.resizeElement();
8552             }
8553             }catch(e){}
8554         }
8555         this.fireEvent("resizing", this, x, y, w, h, e);
8556     },
8557
8558     // private
8559     handleOver : function(){
8560         if(this.enabled){
8561             this.el.addClass("x-resizable-over");
8562         }
8563     },
8564
8565     // private
8566     handleOut : function(){
8567         if(!this.resizing){
8568             this.el.removeClass("x-resizable-over");
8569         }
8570     },
8571
8572     /**
8573      * Returns the element this component is bound to.
8574      * @return {Roo.Element}
8575      */
8576     getEl : function(){
8577         return this.el;
8578     },
8579
8580     /**
8581      * Returns the resizeChild element (or null).
8582      * @return {Roo.Element}
8583      */
8584     getResizeChild : function(){
8585         return this.resizeChild;
8586     },
8587     groupHandler : function()
8588     {
8589         
8590     },
8591     /**
8592      * Destroys this resizable. If the element was wrapped and
8593      * removeEl is not true then the element remains.
8594      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8595      */
8596     destroy : function(removeEl){
8597         this.proxy.remove();
8598         if(this.overlay){
8599             this.overlay.removeAllListeners();
8600             this.overlay.remove();
8601         }
8602         var ps = Roo.Resizable.positions;
8603         for(var k in ps){
8604             if(typeof ps[k] != "function" && this[ps[k]]){
8605                 var h = this[ps[k]];
8606                 h.el.removeAllListeners();
8607                 h.el.remove();
8608             }
8609         }
8610         if(removeEl){
8611             this.el.update("");
8612             this.el.remove();
8613         }
8614     }
8615 });
8616
8617 // private
8618 // hash to map config positions to true positions
8619 Roo.Resizable.positions = {
8620     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8621     hd: "hdrag"
8622 };
8623
8624 // private
8625 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8626     if(!this.tpl){
8627         // only initialize the template if resizable is used
8628         var tpl = Roo.DomHelper.createTemplate(
8629             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8630         );
8631         tpl.compile();
8632         Roo.Resizable.Handle.prototype.tpl = tpl;
8633     }
8634     this.position = pos;
8635     this.rz = rz;
8636     // show north drag fro topdra
8637     var handlepos = pos == 'hdrag' ? 'north' : pos;
8638     
8639     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8640     if (pos == 'hdrag') {
8641         this.el.setStyle('cursor', 'pointer');
8642     }
8643     this.el.unselectable();
8644     if(transparent){
8645         this.el.setOpacity(0);
8646     }
8647     this.el.on("mousedown", this.onMouseDown, this);
8648     if(!disableTrackOver){
8649         this.el.on("mouseover", this.onMouseOver, this);
8650         this.el.on("mouseout", this.onMouseOut, this);
8651     }
8652 };
8653
8654 // private
8655 Roo.Resizable.Handle.prototype = {
8656     afterResize : function(rz){
8657         Roo.log('after?');
8658         // do nothing
8659     },
8660     // private
8661     onMouseDown : function(e){
8662         this.rz.onMouseDown(this, e);
8663     },
8664     // private
8665     onMouseOver : function(e){
8666         this.rz.handleOver(this, e);
8667     },
8668     // private
8669     onMouseOut : function(e){
8670         this.rz.handleOut(this, e);
8671     }
8672 };/*
8673  * Based on:
8674  * Ext JS Library 1.1.1
8675  * Copyright(c) 2006-2007, Ext JS, LLC.
8676  *
8677  * Originally Released Under LGPL - original licence link has changed is not relivant.
8678  *
8679  * Fork - LGPL
8680  * <script type="text/javascript">
8681  */
8682
8683 /**
8684  * @class Roo.Editor
8685  * @extends Roo.Component
8686  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8687  * @constructor
8688  * Create a new Editor
8689  * @param {Roo.form.Field} field The Field object (or descendant)
8690  * @param {Object} config The config object
8691  */
8692 Roo.Editor = function(field, config){
8693     Roo.Editor.superclass.constructor.call(this, config);
8694     this.field = field;
8695     this.addEvents({
8696         /**
8697              * @event beforestartedit
8698              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8699              * false from the handler of this event.
8700              * @param {Editor} this
8701              * @param {Roo.Element} boundEl The underlying element bound to this editor
8702              * @param {Mixed} value The field value being set
8703              */
8704         "beforestartedit" : true,
8705         /**
8706              * @event startedit
8707              * Fires when this editor is displayed
8708              * @param {Roo.Element} boundEl The underlying element bound to this editor
8709              * @param {Mixed} value The starting field value
8710              */
8711         "startedit" : true,
8712         /**
8713              * @event beforecomplete
8714              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8715              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8716              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8717              * event will not fire since no edit actually occurred.
8718              * @param {Editor} this
8719              * @param {Mixed} value The current field value
8720              * @param {Mixed} startValue The original field value
8721              */
8722         "beforecomplete" : true,
8723         /**
8724              * @event complete
8725              * Fires after editing is complete and any changed value has been written to the underlying field.
8726              * @param {Editor} this
8727              * @param {Mixed} value The current field value
8728              * @param {Mixed} startValue The original field value
8729              */
8730         "complete" : true,
8731         /**
8732          * @event specialkey
8733          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8734          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8735          * @param {Roo.form.Field} this
8736          * @param {Roo.EventObject} e The event object
8737          */
8738         "specialkey" : true
8739     });
8740 };
8741
8742 Roo.extend(Roo.Editor, Roo.Component, {
8743     /**
8744      * @cfg {Boolean/String} autosize
8745      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8746      * or "height" to adopt the height only (defaults to false)
8747      */
8748     /**
8749      * @cfg {Boolean} revertInvalid
8750      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8751      * validation fails (defaults to true)
8752      */
8753     /**
8754      * @cfg {Boolean} ignoreNoChange
8755      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8756      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8757      * will never be ignored.
8758      */
8759     /**
8760      * @cfg {Boolean} hideEl
8761      * False to keep the bound element visible while the editor is displayed (defaults to true)
8762      */
8763     /**
8764      * @cfg {Mixed} value
8765      * The data value of the underlying field (defaults to "")
8766      */
8767     value : "",
8768     /**
8769      * @cfg {String} alignment
8770      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8771      */
8772     alignment: "c-c?",
8773     /**
8774      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8775      * for bottom-right shadow (defaults to "frame")
8776      */
8777     shadow : "frame",
8778     /**
8779      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8780      */
8781     constrain : false,
8782     /**
8783      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8784      */
8785     completeOnEnter : false,
8786     /**
8787      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8788      */
8789     cancelOnEsc : false,
8790     /**
8791      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8792      */
8793     updateEl : false,
8794
8795     // private
8796     onRender : function(ct, position){
8797         this.el = new Roo.Layer({
8798             shadow: this.shadow,
8799             cls: "x-editor",
8800             parentEl : ct,
8801             shim : this.shim,
8802             shadowOffset:4,
8803             id: this.id,
8804             constrain: this.constrain
8805         });
8806         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8807         if(this.field.msgTarget != 'title'){
8808             this.field.msgTarget = 'qtip';
8809         }
8810         this.field.render(this.el);
8811         if(Roo.isGecko){
8812             this.field.el.dom.setAttribute('autocomplete', 'off');
8813         }
8814         this.field.on("specialkey", this.onSpecialKey, this);
8815         if(this.swallowKeys){
8816             this.field.el.swallowEvent(['keydown','keypress']);
8817         }
8818         this.field.show();
8819         this.field.on("blur", this.onBlur, this);
8820         if(this.field.grow){
8821             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8822         }
8823     },
8824
8825     onSpecialKey : function(field, e)
8826     {
8827         //Roo.log('editor onSpecialKey');
8828         if(this.completeOnEnter && e.getKey() == e.ENTER){
8829             e.stopEvent();
8830             this.completeEdit();
8831             return;
8832         }
8833         // do not fire special key otherwise it might hide close the editor...
8834         if(e.getKey() == e.ENTER){    
8835             return;
8836         }
8837         if(this.cancelOnEsc && e.getKey() == e.ESC){
8838             this.cancelEdit();
8839             return;
8840         } 
8841         this.fireEvent('specialkey', field, e);
8842     
8843     },
8844
8845     /**
8846      * Starts the editing process and shows the editor.
8847      * @param {String/HTMLElement/Element} el The element to edit
8848      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8849       * to the innerHTML of el.
8850      */
8851     startEdit : function(el, value){
8852         if(this.editing){
8853             this.completeEdit();
8854         }
8855         this.boundEl = Roo.get(el);
8856         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8857         if(!this.rendered){
8858             this.render(this.parentEl || document.body);
8859         }
8860         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8861             return;
8862         }
8863         this.startValue = v;
8864         this.field.setValue(v);
8865         if(this.autoSize){
8866             var sz = this.boundEl.getSize();
8867             switch(this.autoSize){
8868                 case "width":
8869                 this.setSize(sz.width,  "");
8870                 break;
8871                 case "height":
8872                 this.setSize("",  sz.height);
8873                 break;
8874                 default:
8875                 this.setSize(sz.width,  sz.height);
8876             }
8877         }
8878         this.el.alignTo(this.boundEl, this.alignment);
8879         this.editing = true;
8880         if(Roo.QuickTips){
8881             Roo.QuickTips.disable();
8882         }
8883         this.show();
8884     },
8885
8886     /**
8887      * Sets the height and width of this editor.
8888      * @param {Number} width The new width
8889      * @param {Number} height The new height
8890      */
8891     setSize : function(w, h){
8892         this.field.setSize(w, h);
8893         if(this.el){
8894             this.el.sync();
8895         }
8896     },
8897
8898     /**
8899      * Realigns the editor to the bound field based on the current alignment config value.
8900      */
8901     realign : function(){
8902         this.el.alignTo(this.boundEl, this.alignment);
8903     },
8904
8905     /**
8906      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8907      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8908      */
8909     completeEdit : function(remainVisible){
8910         if(!this.editing){
8911             return;
8912         }
8913         var v = this.getValue();
8914         if(this.revertInvalid !== false && !this.field.isValid()){
8915             v = this.startValue;
8916             this.cancelEdit(true);
8917         }
8918         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8919             this.editing = false;
8920             this.hide();
8921             return;
8922         }
8923         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8924             this.editing = false;
8925             if(this.updateEl && this.boundEl){
8926                 this.boundEl.update(v);
8927             }
8928             if(remainVisible !== true){
8929                 this.hide();
8930             }
8931             this.fireEvent("complete", this, v, this.startValue);
8932         }
8933     },
8934
8935     // private
8936     onShow : function(){
8937         this.el.show();
8938         if(this.hideEl !== false){
8939             this.boundEl.hide();
8940         }
8941         this.field.show();
8942         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8943             this.fixIEFocus = true;
8944             this.deferredFocus.defer(50, this);
8945         }else{
8946             this.field.focus();
8947         }
8948         this.fireEvent("startedit", this.boundEl, this.startValue);
8949     },
8950
8951     deferredFocus : function(){
8952         if(this.editing){
8953             this.field.focus();
8954         }
8955     },
8956
8957     /**
8958      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8959      * reverted to the original starting value.
8960      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8961      * cancel (defaults to false)
8962      */
8963     cancelEdit : function(remainVisible){
8964         if(this.editing){
8965             this.setValue(this.startValue);
8966             if(remainVisible !== true){
8967                 this.hide();
8968             }
8969         }
8970     },
8971
8972     // private
8973     onBlur : function(){
8974         if(this.allowBlur !== true && this.editing){
8975             this.completeEdit();
8976         }
8977     },
8978
8979     // private
8980     onHide : function(){
8981         if(this.editing){
8982             this.completeEdit();
8983             return;
8984         }
8985         this.field.blur();
8986         if(this.field.collapse){
8987             this.field.collapse();
8988         }
8989         this.el.hide();
8990         if(this.hideEl !== false){
8991             this.boundEl.show();
8992         }
8993         if(Roo.QuickTips){
8994             Roo.QuickTips.enable();
8995         }
8996     },
8997
8998     /**
8999      * Sets the data value of the editor
9000      * @param {Mixed} value Any valid value supported by the underlying field
9001      */
9002     setValue : function(v){
9003         this.field.setValue(v);
9004     },
9005
9006     /**
9007      * Gets the data value of the editor
9008      * @return {Mixed} The data value
9009      */
9010     getValue : function(){
9011         return this.field.getValue();
9012     }
9013 });/*
9014  * Based on:
9015  * Ext JS Library 1.1.1
9016  * Copyright(c) 2006-2007, Ext JS, LLC.
9017  *
9018  * Originally Released Under LGPL - original licence link has changed is not relivant.
9019  *
9020  * Fork - LGPL
9021  * <script type="text/javascript">
9022  */
9023  
9024 /**
9025  * @class Roo.BasicDialog
9026  * @extends Roo.util.Observable
9027  * @parent none builder
9028  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9029  * <pre><code>
9030 var dlg = new Roo.BasicDialog("my-dlg", {
9031     height: 200,
9032     width: 300,
9033     minHeight: 100,
9034     minWidth: 150,
9035     modal: true,
9036     proxyDrag: true,
9037     shadow: true
9038 });
9039 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9040 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9041 dlg.addButton('Cancel', dlg.hide, dlg);
9042 dlg.show();
9043 </code></pre>
9044   <b>A Dialog should always be a direct child of the body element.</b>
9045  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9046  * @cfg {String} title Default text to display in the title bar (defaults to null)
9047  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9048  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9049  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9050  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9051  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9052  * (defaults to null with no animation)
9053  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9054  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9055  * property for valid values (defaults to 'all')
9056  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9057  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9058  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9059  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9060  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9061  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9062  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9063  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9064  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9065  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9066  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9067  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9068  * draggable = true (defaults to false)
9069  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9070  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9071  * shadow (defaults to false)
9072  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9073  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9074  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9075  * @cfg {Array} buttons Array of buttons
9076  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9077  * @constructor
9078  * Create a new BasicDialog.
9079  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9080  * @param {Object} config Configuration options
9081  */
9082 Roo.BasicDialog = function(el, config){
9083     this.el = Roo.get(el);
9084     var dh = Roo.DomHelper;
9085     if(!this.el && config && config.autoCreate){
9086         if(typeof config.autoCreate == "object"){
9087             if(!config.autoCreate.id){
9088                 config.autoCreate.id = el;
9089             }
9090             this.el = dh.append(document.body,
9091                         config.autoCreate, true);
9092         }else{
9093             this.el = dh.append(document.body,
9094                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9095         }
9096     }
9097     el = this.el;
9098     el.setDisplayed(true);
9099     el.hide = this.hideAction;
9100     this.id = el.id;
9101     el.addClass("x-dlg");
9102
9103     Roo.apply(this, config);
9104
9105     this.proxy = el.createProxy("x-dlg-proxy");
9106     this.proxy.hide = this.hideAction;
9107     this.proxy.setOpacity(.5);
9108     this.proxy.hide();
9109
9110     if(config.width){
9111         el.setWidth(config.width);
9112     }
9113     if(config.height){
9114         el.setHeight(config.height);
9115     }
9116     this.size = el.getSize();
9117     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9118         this.xy = [config.x,config.y];
9119     }else{
9120         this.xy = el.getCenterXY(true);
9121     }
9122     /** The header element @type Roo.Element */
9123     this.header = el.child("> .x-dlg-hd");
9124     /** The body element @type Roo.Element */
9125     this.body = el.child("> .x-dlg-bd");
9126     /** The footer element @type Roo.Element */
9127     this.footer = el.child("> .x-dlg-ft");
9128
9129     if(!this.header){
9130         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9131     }
9132     if(!this.body){
9133         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9134     }
9135
9136     this.header.unselectable();
9137     if(this.title){
9138         this.header.update(this.title);
9139     }
9140     // this element allows the dialog to be focused for keyboard event
9141     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9142     this.focusEl.swallowEvent("click", true);
9143
9144     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9145
9146     // wrap the body and footer for special rendering
9147     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9148     if(this.footer){
9149         this.bwrap.dom.appendChild(this.footer.dom);
9150     }
9151
9152     this.bg = this.el.createChild({
9153         tag: "div", cls:"x-dlg-bg",
9154         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9155     });
9156     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9157
9158
9159     if(this.autoScroll !== false && !this.autoTabs){
9160         this.body.setStyle("overflow", "auto");
9161     }
9162
9163     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9164
9165     if(this.closable !== false){
9166         this.el.addClass("x-dlg-closable");
9167         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9168         this.close.on("click", this.closeClick, this);
9169         this.close.addClassOnOver("x-dlg-close-over");
9170     }
9171     if(this.collapsible !== false){
9172         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9173         this.collapseBtn.on("click", this.collapseClick, this);
9174         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9175         this.header.on("dblclick", this.collapseClick, this);
9176     }
9177     if(this.resizable !== false){
9178         this.el.addClass("x-dlg-resizable");
9179         this.resizer = new Roo.Resizable(el, {
9180             minWidth: this.minWidth || 80,
9181             minHeight:this.minHeight || 80,
9182             handles: this.resizeHandles || "all",
9183             pinned: true
9184         });
9185         this.resizer.on("beforeresize", this.beforeResize, this);
9186         this.resizer.on("resize", this.onResize, this);
9187     }
9188     if(this.draggable !== false){
9189         el.addClass("x-dlg-draggable");
9190         if (!this.proxyDrag) {
9191             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9192         }
9193         else {
9194             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9195         }
9196         dd.setHandleElId(this.header.id);
9197         dd.endDrag = this.endMove.createDelegate(this);
9198         dd.startDrag = this.startMove.createDelegate(this);
9199         dd.onDrag = this.onDrag.createDelegate(this);
9200         dd.scroll = false;
9201         this.dd = dd;
9202     }
9203     if(this.modal){
9204         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9205         this.mask.enableDisplayMode("block");
9206         this.mask.hide();
9207         this.el.addClass("x-dlg-modal");
9208     }
9209     if(this.shadow){
9210         this.shadow = new Roo.Shadow({
9211             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9212             offset : this.shadowOffset
9213         });
9214     }else{
9215         this.shadowOffset = 0;
9216     }
9217     if(Roo.useShims && this.shim !== false){
9218         this.shim = this.el.createShim();
9219         this.shim.hide = this.hideAction;
9220         this.shim.hide();
9221     }else{
9222         this.shim = false;
9223     }
9224     if(this.autoTabs){
9225         this.initTabs();
9226     }
9227     if (this.buttons) { 
9228         var bts= this.buttons;
9229         this.buttons = [];
9230         Roo.each(bts, function(b) {
9231             this.addButton(b);
9232         }, this);
9233     }
9234     
9235     
9236     this.addEvents({
9237         /**
9238          * @event keydown
9239          * Fires when a key is pressed
9240          * @param {Roo.BasicDialog} this
9241          * @param {Roo.EventObject} e
9242          */
9243         "keydown" : true,
9244         /**
9245          * @event move
9246          * Fires when this dialog is moved by the user.
9247          * @param {Roo.BasicDialog} this
9248          * @param {Number} x The new page X
9249          * @param {Number} y The new page Y
9250          */
9251         "move" : true,
9252         /**
9253          * @event resize
9254          * Fires when this dialog is resized by the user.
9255          * @param {Roo.BasicDialog} this
9256          * @param {Number} width The new width
9257          * @param {Number} height The new height
9258          */
9259         "resize" : true,
9260         /**
9261          * @event beforehide
9262          * Fires before this dialog is hidden.
9263          * @param {Roo.BasicDialog} this
9264          */
9265         "beforehide" : true,
9266         /**
9267          * @event hide
9268          * Fires when this dialog is hidden.
9269          * @param {Roo.BasicDialog} this
9270          */
9271         "hide" : true,
9272         /**
9273          * @event beforeshow
9274          * Fires before this dialog is shown.
9275          * @param {Roo.BasicDialog} this
9276          */
9277         "beforeshow" : true,
9278         /**
9279          * @event show
9280          * Fires when this dialog is shown.
9281          * @param {Roo.BasicDialog} this
9282          */
9283         "show" : true
9284     });
9285     el.on("keydown", this.onKeyDown, this);
9286     el.on("mousedown", this.toFront, this);
9287     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9288     this.el.hide();
9289     Roo.DialogManager.register(this);
9290     Roo.BasicDialog.superclass.constructor.call(this);
9291 };
9292
9293 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9294     shadowOffset: Roo.isIE ? 6 : 5,
9295     minHeight: 80,
9296     minWidth: 200,
9297     minButtonWidth: 75,
9298     defaultButton: null,
9299     buttonAlign: "right",
9300     tabTag: 'div',
9301     firstShow: true,
9302
9303     /**
9304      * Sets the dialog title text
9305      * @param {String} text The title text to display
9306      * @return {Roo.BasicDialog} this
9307      */
9308     setTitle : function(text){
9309         this.header.update(text);
9310         return this;
9311     },
9312
9313     // private
9314     closeClick : function(){
9315         this.hide();
9316     },
9317
9318     // private
9319     collapseClick : function(){
9320         this[this.collapsed ? "expand" : "collapse"]();
9321     },
9322
9323     /**
9324      * Collapses the dialog to its minimized state (only the title bar is visible).
9325      * Equivalent to the user clicking the collapse dialog button.
9326      */
9327     collapse : function(){
9328         if(!this.collapsed){
9329             this.collapsed = true;
9330             this.el.addClass("x-dlg-collapsed");
9331             this.restoreHeight = this.el.getHeight();
9332             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9333         }
9334     },
9335
9336     /**
9337      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9338      * clicking the expand dialog button.
9339      */
9340     expand : function(){
9341         if(this.collapsed){
9342             this.collapsed = false;
9343             this.el.removeClass("x-dlg-collapsed");
9344             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9345         }
9346     },
9347
9348     /**
9349      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9350      * @return {Roo.TabPanel} The tabs component
9351      */
9352     initTabs : function(){
9353         var tabs = this.getTabs();
9354         while(tabs.getTab(0)){
9355             tabs.removeTab(0);
9356         }
9357         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9358             var dom = el.dom;
9359             tabs.addTab(Roo.id(dom), dom.title);
9360             dom.title = "";
9361         });
9362         tabs.activate(0);
9363         return tabs;
9364     },
9365
9366     // private
9367     beforeResize : function(){
9368         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9369     },
9370
9371     // private
9372     onResize : function(){
9373         this.refreshSize();
9374         this.syncBodyHeight();
9375         this.adjustAssets();
9376         this.focus();
9377         this.fireEvent("resize", this, this.size.width, this.size.height);
9378     },
9379
9380     // private
9381     onKeyDown : function(e){
9382         if(this.isVisible()){
9383             this.fireEvent("keydown", this, e);
9384         }
9385     },
9386
9387     /**
9388      * Resizes the dialog.
9389      * @param {Number} width
9390      * @param {Number} height
9391      * @return {Roo.BasicDialog} this
9392      */
9393     resizeTo : function(width, height){
9394         this.el.setSize(width, height);
9395         this.size = {width: width, height: height};
9396         this.syncBodyHeight();
9397         if(this.fixedcenter){
9398             this.center();
9399         }
9400         if(this.isVisible()){
9401             this.constrainXY();
9402             this.adjustAssets();
9403         }
9404         this.fireEvent("resize", this, width, height);
9405         return this;
9406     },
9407
9408
9409     /**
9410      * Resizes the dialog to fit the specified content size.
9411      * @param {Number} width
9412      * @param {Number} height
9413      * @return {Roo.BasicDialog} this
9414      */
9415     setContentSize : function(w, h){
9416         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9417         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9418         //if(!this.el.isBorderBox()){
9419             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9420             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9421         //}
9422         if(this.tabs){
9423             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9424             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9425         }
9426         this.resizeTo(w, h);
9427         return this;
9428     },
9429
9430     /**
9431      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9432      * executed in response to a particular key being pressed while the dialog is active.
9433      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9434      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9435      * @param {Function} fn The function to call
9436      * @param {Object} scope (optional) The scope of the function
9437      * @return {Roo.BasicDialog} this
9438      */
9439     addKeyListener : function(key, fn, scope){
9440         var keyCode, shift, ctrl, alt;
9441         if(typeof key == "object" && !(key instanceof Array)){
9442             keyCode = key["key"];
9443             shift = key["shift"];
9444             ctrl = key["ctrl"];
9445             alt = key["alt"];
9446         }else{
9447             keyCode = key;
9448         }
9449         var handler = function(dlg, e){
9450             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9451                 var k = e.getKey();
9452                 if(keyCode instanceof Array){
9453                     for(var i = 0, len = keyCode.length; i < len; i++){
9454                         if(keyCode[i] == k){
9455                           fn.call(scope || window, dlg, k, e);
9456                           return;
9457                         }
9458                     }
9459                 }else{
9460                     if(k == keyCode){
9461                         fn.call(scope || window, dlg, k, e);
9462                     }
9463                 }
9464             }
9465         };
9466         this.on("keydown", handler);
9467         return this;
9468     },
9469
9470     /**
9471      * Returns the TabPanel component (creates it if it doesn't exist).
9472      * Note: If you wish to simply check for the existence of tabs without creating them,
9473      * check for a null 'tabs' property.
9474      * @return {Roo.TabPanel} The tabs component
9475      */
9476     getTabs : function(){
9477         if(!this.tabs){
9478             this.el.addClass("x-dlg-auto-tabs");
9479             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9480             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9481         }
9482         return this.tabs;
9483     },
9484
9485     /**
9486      * Adds a button to the footer section of the dialog.
9487      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9488      * object or a valid Roo.DomHelper element config
9489      * @param {Function} handler The function called when the button is clicked
9490      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9491      * @return {Roo.Button} The new button
9492      */
9493     addButton : function(config, handler, scope){
9494         var dh = Roo.DomHelper;
9495         if(!this.footer){
9496             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9497         }
9498         if(!this.btnContainer){
9499             var tb = this.footer.createChild({
9500
9501                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9502                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9503             }, null, true);
9504             this.btnContainer = tb.firstChild.firstChild.firstChild;
9505         }
9506         var bconfig = {
9507             handler: handler,
9508             scope: scope,
9509             minWidth: this.minButtonWidth,
9510             hideParent:true
9511         };
9512         if(typeof config == "string"){
9513             bconfig.text = config;
9514         }else{
9515             if(config.tag){
9516                 bconfig.dhconfig = config;
9517             }else{
9518                 Roo.apply(bconfig, config);
9519             }
9520         }
9521         var fc = false;
9522         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9523             bconfig.position = Math.max(0, bconfig.position);
9524             fc = this.btnContainer.childNodes[bconfig.position];
9525         }
9526          
9527         var btn = new Roo.Button(
9528             fc ? 
9529                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9530                 : this.btnContainer.appendChild(document.createElement("td")),
9531             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9532             bconfig
9533         );
9534         this.syncBodyHeight();
9535         if(!this.buttons){
9536             /**
9537              * Array of all the buttons that have been added to this dialog via addButton
9538              * @type Array
9539              */
9540             this.buttons = [];
9541         }
9542         this.buttons.push(btn);
9543         return btn;
9544     },
9545
9546     /**
9547      * Sets the default button to be focused when the dialog is displayed.
9548      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9549      * @return {Roo.BasicDialog} this
9550      */
9551     setDefaultButton : function(btn){
9552         this.defaultButton = btn;
9553         return this;
9554     },
9555
9556     // private
9557     getHeaderFooterHeight : function(safe){
9558         var height = 0;
9559         if(this.header){
9560            height += this.header.getHeight();
9561         }
9562         if(this.footer){
9563            var fm = this.footer.getMargins();
9564             height += (this.footer.getHeight()+fm.top+fm.bottom);
9565         }
9566         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9567         height += this.centerBg.getPadding("tb");
9568         return height;
9569     },
9570
9571     // private
9572     syncBodyHeight : function()
9573     {
9574         var bd = this.body, // the text
9575             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9576             bw = this.bwrap;
9577         var height = this.size.height - this.getHeaderFooterHeight(false);
9578         bd.setHeight(height-bd.getMargins("tb"));
9579         var hh = this.header.getHeight();
9580         var h = this.size.height-hh;
9581         cb.setHeight(h);
9582         
9583         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9584         bw.setHeight(h-cb.getPadding("tb"));
9585         
9586         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9587         bd.setWidth(bw.getWidth(true));
9588         if(this.tabs){
9589             this.tabs.syncHeight();
9590             if(Roo.isIE){
9591                 this.tabs.el.repaint();
9592             }
9593         }
9594     },
9595
9596     /**
9597      * Restores the previous state of the dialog if Roo.state is configured.
9598      * @return {Roo.BasicDialog} this
9599      */
9600     restoreState : function(){
9601         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9602         if(box && box.width){
9603             this.xy = [box.x, box.y];
9604             this.resizeTo(box.width, box.height);
9605         }
9606         return this;
9607     },
9608
9609     // private
9610     beforeShow : function(){
9611         this.expand();
9612         if(this.fixedcenter){
9613             this.xy = this.el.getCenterXY(true);
9614         }
9615         if(this.modal){
9616             Roo.get(document.body).addClass("x-body-masked");
9617             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9618             this.mask.show();
9619         }
9620         this.constrainXY();
9621     },
9622
9623     // private
9624     animShow : function(){
9625         var b = Roo.get(this.animateTarget).getBox();
9626         this.proxy.setSize(b.width, b.height);
9627         this.proxy.setLocation(b.x, b.y);
9628         this.proxy.show();
9629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9630                     true, .35, this.showEl.createDelegate(this));
9631     },
9632
9633     /**
9634      * Shows the dialog.
9635      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9636      * @return {Roo.BasicDialog} this
9637      */
9638     show : function(animateTarget){
9639         if (this.fireEvent("beforeshow", this) === false){
9640             return;
9641         }
9642         if(this.syncHeightBeforeShow){
9643             this.syncBodyHeight();
9644         }else if(this.firstShow){
9645             this.firstShow = false;
9646             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9647         }
9648         this.animateTarget = animateTarget || this.animateTarget;
9649         if(!this.el.isVisible()){
9650             this.beforeShow();
9651             if(this.animateTarget && Roo.get(this.animateTarget)){
9652                 this.animShow();
9653             }else{
9654                 this.showEl();
9655             }
9656         }
9657         return this;
9658     },
9659
9660     // private
9661     showEl : function(){
9662         this.proxy.hide();
9663         this.el.setXY(this.xy);
9664         this.el.show();
9665         this.adjustAssets(true);
9666         this.toFront();
9667         this.focus();
9668         // IE peekaboo bug - fix found by Dave Fenwick
9669         if(Roo.isIE){
9670             this.el.repaint();
9671         }
9672         this.fireEvent("show", this);
9673     },
9674
9675     /**
9676      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9677      * dialog itself will receive focus.
9678      */
9679     focus : function(){
9680         if(this.defaultButton){
9681             this.defaultButton.focus();
9682         }else{
9683             this.focusEl.focus();
9684         }
9685     },
9686
9687     // private
9688     constrainXY : function(){
9689         if(this.constraintoviewport !== false){
9690             if(!this.viewSize){
9691                 if(this.container){
9692                     var s = this.container.getSize();
9693                     this.viewSize = [s.width, s.height];
9694                 }else{
9695                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9696                 }
9697             }
9698             var s = Roo.get(this.container||document).getScroll();
9699
9700             var x = this.xy[0], y = this.xy[1];
9701             var w = this.size.width, h = this.size.height;
9702             var vw = this.viewSize[0], vh = this.viewSize[1];
9703             // only move it if it needs it
9704             var moved = false;
9705             // first validate right/bottom
9706             if(x + w > vw+s.left){
9707                 x = vw - w;
9708                 moved = true;
9709             }
9710             if(y + h > vh+s.top){
9711                 y = vh - h;
9712                 moved = true;
9713             }
9714             // then make sure top/left isn't negative
9715             if(x < s.left){
9716                 x = s.left;
9717                 moved = true;
9718             }
9719             if(y < s.top){
9720                 y = s.top;
9721                 moved = true;
9722             }
9723             if(moved){
9724                 // cache xy
9725                 this.xy = [x, y];
9726                 if(this.isVisible()){
9727                     this.el.setLocation(x, y);
9728                     this.adjustAssets();
9729                 }
9730             }
9731         }
9732     },
9733
9734     // private
9735     onDrag : function(){
9736         if(!this.proxyDrag){
9737             this.xy = this.el.getXY();
9738             this.adjustAssets();
9739         }
9740     },
9741
9742     // private
9743     adjustAssets : function(doShow){
9744         var x = this.xy[0], y = this.xy[1];
9745         var w = this.size.width, h = this.size.height;
9746         if(doShow === true){
9747             if(this.shadow){
9748                 this.shadow.show(this.el);
9749             }
9750             if(this.shim){
9751                 this.shim.show();
9752             }
9753         }
9754         if(this.shadow && this.shadow.isVisible()){
9755             this.shadow.show(this.el);
9756         }
9757         if(this.shim && this.shim.isVisible()){
9758             this.shim.setBounds(x, y, w, h);
9759         }
9760     },
9761
9762     // private
9763     adjustViewport : function(w, h){
9764         if(!w || !h){
9765             w = Roo.lib.Dom.getViewWidth();
9766             h = Roo.lib.Dom.getViewHeight();
9767         }
9768         // cache the size
9769         this.viewSize = [w, h];
9770         if(this.modal && this.mask.isVisible()){
9771             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9772             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9773         }
9774         if(this.isVisible()){
9775             this.constrainXY();
9776         }
9777     },
9778
9779     /**
9780      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9781      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9782      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9783      */
9784     destroy : function(removeEl){
9785         if(this.isVisible()){
9786             this.animateTarget = null;
9787             this.hide();
9788         }
9789         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9790         if(this.tabs){
9791             this.tabs.destroy(removeEl);
9792         }
9793         Roo.destroy(
9794              this.shim,
9795              this.proxy,
9796              this.resizer,
9797              this.close,
9798              this.mask
9799         );
9800         if(this.dd){
9801             this.dd.unreg();
9802         }
9803         if(this.buttons){
9804            for(var i = 0, len = this.buttons.length; i < len; i++){
9805                this.buttons[i].destroy();
9806            }
9807         }
9808         this.el.removeAllListeners();
9809         if(removeEl === true){
9810             this.el.update("");
9811             this.el.remove();
9812         }
9813         Roo.DialogManager.unregister(this);
9814     },
9815
9816     // private
9817     startMove : function(){
9818         if(this.proxyDrag){
9819             this.proxy.show();
9820         }
9821         if(this.constraintoviewport !== false){
9822             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9823         }
9824     },
9825
9826     // private
9827     endMove : function(){
9828         if(!this.proxyDrag){
9829             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9830         }else{
9831             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9832             this.proxy.hide();
9833         }
9834         this.refreshSize();
9835         this.adjustAssets();
9836         this.focus();
9837         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9838     },
9839
9840     /**
9841      * Brings this dialog to the front of any other visible dialogs
9842      * @return {Roo.BasicDialog} this
9843      */
9844     toFront : function(){
9845         Roo.DialogManager.bringToFront(this);
9846         return this;
9847     },
9848
9849     /**
9850      * Sends this dialog to the back (under) of any other visible dialogs
9851      * @return {Roo.BasicDialog} this
9852      */
9853     toBack : function(){
9854         Roo.DialogManager.sendToBack(this);
9855         return this;
9856     },
9857
9858     /**
9859      * Centers this dialog in the viewport
9860      * @return {Roo.BasicDialog} this
9861      */
9862     center : function(){
9863         var xy = this.el.getCenterXY(true);
9864         this.moveTo(xy[0], xy[1]);
9865         return this;
9866     },
9867
9868     /**
9869      * Moves the dialog's top-left corner to the specified point
9870      * @param {Number} x
9871      * @param {Number} y
9872      * @return {Roo.BasicDialog} this
9873      */
9874     moveTo : function(x, y){
9875         this.xy = [x,y];
9876         if(this.isVisible()){
9877             this.el.setXY(this.xy);
9878             this.adjustAssets();
9879         }
9880         return this;
9881     },
9882
9883     /**
9884      * Aligns the dialog to the specified element
9885      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9886      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9887      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9888      * @return {Roo.BasicDialog} this
9889      */
9890     alignTo : function(element, position, offsets){
9891         this.xy = this.el.getAlignToXY(element, position, offsets);
9892         if(this.isVisible()){
9893             this.el.setXY(this.xy);
9894             this.adjustAssets();
9895         }
9896         return this;
9897     },
9898
9899     /**
9900      * Anchors an element to another element and realigns it when the window is resized.
9901      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9902      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9903      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9904      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9905      * is a number, it is used as the buffer delay (defaults to 50ms).
9906      * @return {Roo.BasicDialog} this
9907      */
9908     anchorTo : function(el, alignment, offsets, monitorScroll){
9909         var action = function(){
9910             this.alignTo(el, alignment, offsets);
9911         };
9912         Roo.EventManager.onWindowResize(action, this);
9913         var tm = typeof monitorScroll;
9914         if(tm != 'undefined'){
9915             Roo.EventManager.on(window, 'scroll', action, this,
9916                 {buffer: tm == 'number' ? monitorScroll : 50});
9917         }
9918         action.call(this);
9919         return this;
9920     },
9921
9922     /**
9923      * Returns true if the dialog is visible
9924      * @return {Boolean}
9925      */
9926     isVisible : function(){
9927         return this.el.isVisible();
9928     },
9929
9930     // private
9931     animHide : function(callback){
9932         var b = Roo.get(this.animateTarget).getBox();
9933         this.proxy.show();
9934         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9935         this.el.hide();
9936         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9937                     this.hideEl.createDelegate(this, [callback]));
9938     },
9939
9940     /**
9941      * Hides the dialog.
9942      * @param {Function} callback (optional) Function to call when the dialog is hidden
9943      * @return {Roo.BasicDialog} this
9944      */
9945     hide : function(callback){
9946         if (this.fireEvent("beforehide", this) === false){
9947             return;
9948         }
9949         if(this.shadow){
9950             this.shadow.hide();
9951         }
9952         if(this.shim) {
9953           this.shim.hide();
9954         }
9955         // sometimes animateTarget seems to get set.. causing problems...
9956         // this just double checks..
9957         if(this.animateTarget && Roo.get(this.animateTarget)) {
9958            this.animHide(callback);
9959         }else{
9960             this.el.hide();
9961             this.hideEl(callback);
9962         }
9963         return this;
9964     },
9965
9966     // private
9967     hideEl : function(callback){
9968         this.proxy.hide();
9969         if(this.modal){
9970             this.mask.hide();
9971             Roo.get(document.body).removeClass("x-body-masked");
9972         }
9973         this.fireEvent("hide", this);
9974         if(typeof callback == "function"){
9975             callback();
9976         }
9977     },
9978
9979     // private
9980     hideAction : function(){
9981         this.setLeft("-10000px");
9982         this.setTop("-10000px");
9983         this.setStyle("visibility", "hidden");
9984     },
9985
9986     // private
9987     refreshSize : function(){
9988         this.size = this.el.getSize();
9989         this.xy = this.el.getXY();
9990         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9991     },
9992
9993     // private
9994     // z-index is managed by the DialogManager and may be overwritten at any time
9995     setZIndex : function(index){
9996         if(this.modal){
9997             this.mask.setStyle("z-index", index);
9998         }
9999         if(this.shim){
10000             this.shim.setStyle("z-index", ++index);
10001         }
10002         if(this.shadow){
10003             this.shadow.setZIndex(++index);
10004         }
10005         this.el.setStyle("z-index", ++index);
10006         if(this.proxy){
10007             this.proxy.setStyle("z-index", ++index);
10008         }
10009         if(this.resizer){
10010             this.resizer.proxy.setStyle("z-index", ++index);
10011         }
10012
10013         this.lastZIndex = index;
10014     },
10015
10016     /**
10017      * Returns the element for this dialog
10018      * @return {Roo.Element} The underlying dialog Element
10019      */
10020     getEl : function(){
10021         return this.el;
10022     }
10023 });
10024
10025 /**
10026  * @class Roo.DialogManager
10027  * Provides global access to BasicDialogs that have been created and
10028  * support for z-indexing (layering) multiple open dialogs.
10029  */
10030 Roo.DialogManager = function(){
10031     var list = {};
10032     var accessList = [];
10033     var front = null;
10034
10035     // private
10036     var sortDialogs = function(d1, d2){
10037         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10038     };
10039
10040     // private
10041     var orderDialogs = function(){
10042         accessList.sort(sortDialogs);
10043         var seed = Roo.DialogManager.zseed;
10044         for(var i = 0, len = accessList.length; i < len; i++){
10045             var dlg = accessList[i];
10046             if(dlg){
10047                 dlg.setZIndex(seed + (i*10));
10048             }
10049         }
10050     };
10051
10052     return {
10053         /**
10054          * The starting z-index for BasicDialogs (defaults to 9000)
10055          * @type Number The z-index value
10056          */
10057         zseed : 9000,
10058
10059         // private
10060         register : function(dlg){
10061             list[dlg.id] = dlg;
10062             accessList.push(dlg);
10063         },
10064
10065         // private
10066         unregister : function(dlg){
10067             delete list[dlg.id];
10068             var i=0;
10069             var len=0;
10070             if(!accessList.indexOf){
10071                 for(  i = 0, len = accessList.length; i < len; i++){
10072                     if(accessList[i] == dlg){
10073                         accessList.splice(i, 1);
10074                         return;
10075                     }
10076                 }
10077             }else{
10078                  i = accessList.indexOf(dlg);
10079                 if(i != -1){
10080                     accessList.splice(i, 1);
10081                 }
10082             }
10083         },
10084
10085         /**
10086          * Gets a registered dialog by id
10087          * @param {String/Object} id The id of the dialog or a dialog
10088          * @return {Roo.BasicDialog} this
10089          */
10090         get : function(id){
10091             return typeof id == "object" ? id : list[id];
10092         },
10093
10094         /**
10095          * Brings the specified dialog to the front
10096          * @param {String/Object} dlg The id of the dialog or a dialog
10097          * @return {Roo.BasicDialog} this
10098          */
10099         bringToFront : function(dlg){
10100             dlg = this.get(dlg);
10101             if(dlg != front){
10102                 front = dlg;
10103                 dlg._lastAccess = new Date().getTime();
10104                 orderDialogs();
10105             }
10106             return dlg;
10107         },
10108
10109         /**
10110          * Sends the specified dialog to the back
10111          * @param {String/Object} dlg The id of the dialog or a dialog
10112          * @return {Roo.BasicDialog} this
10113          */
10114         sendToBack : function(dlg){
10115             dlg = this.get(dlg);
10116             dlg._lastAccess = -(new Date().getTime());
10117             orderDialogs();
10118             return dlg;
10119         },
10120
10121         /**
10122          * Hides all dialogs
10123          */
10124         hideAll : function(){
10125             for(var id in list){
10126                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10127                     list[id].hide();
10128                 }
10129             }
10130         }
10131     };
10132 }();
10133
10134 /**
10135  * @class Roo.LayoutDialog
10136  * @extends Roo.BasicDialog
10137  * @children Roo.ContentPanel
10138  * @parent builder none
10139  * Dialog which provides adjustments for working with a layout in a Dialog.
10140  * Add your necessary layout config options to the dialog's config.<br>
10141  * Example usage (including a nested layout):
10142  * <pre><code>
10143 if(!dialog){
10144     dialog = new Roo.LayoutDialog("download-dlg", {
10145         modal: true,
10146         width:600,
10147         height:450,
10148         shadow:true,
10149         minWidth:500,
10150         minHeight:350,
10151         autoTabs:true,
10152         proxyDrag:true,
10153         // layout config merges with the dialog config
10154         center:{
10155             tabPosition: "top",
10156             alwaysShowTabs: true
10157         }
10158     });
10159     dialog.addKeyListener(27, dialog.hide, dialog);
10160     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10161     dialog.addButton("Build It!", this.getDownload, this);
10162
10163     // we can even add nested layouts
10164     var innerLayout = new Roo.BorderLayout("dl-inner", {
10165         east: {
10166             initialSize: 200,
10167             autoScroll:true,
10168             split:true
10169         },
10170         center: {
10171             autoScroll:true
10172         }
10173     });
10174     innerLayout.beginUpdate();
10175     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10176     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10177     innerLayout.endUpdate(true);
10178
10179     var layout = dialog.getLayout();
10180     layout.beginUpdate();
10181     layout.add("center", new Roo.ContentPanel("standard-panel",
10182                         {title: "Download the Source", fitToFrame:true}));
10183     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10184                {title: "Build your own roo.js"}));
10185     layout.getRegion("center").showPanel(sp);
10186     layout.endUpdate();
10187 }
10188 </code></pre>
10189     * @constructor
10190     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10191     * @param {Object} config configuration options
10192   */
10193 Roo.LayoutDialog = function(el, cfg){
10194     
10195     var config=  cfg;
10196     if (typeof(cfg) == 'undefined') {
10197         config = Roo.apply({}, el);
10198         // not sure why we use documentElement here.. - it should always be body.
10199         // IE7 borks horribly if we use documentElement.
10200         // webkit also does not like documentElement - it creates a body element...
10201         el = Roo.get( document.body || document.documentElement ).createChild();
10202         //config.autoCreate = true;
10203     }
10204     
10205     
10206     config.autoTabs = false;
10207     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10208     this.body.setStyle({overflow:"hidden", position:"relative"});
10209     this.layout = new Roo.BorderLayout(this.body.dom, config);
10210     this.layout.monitorWindowResize = false;
10211     this.el.addClass("x-dlg-auto-layout");
10212     // fix case when center region overwrites center function
10213     this.center = Roo.BasicDialog.prototype.center;
10214     this.on("show", this.layout.layout, this.layout, true);
10215     if (config.items) {
10216         var xitems = config.items;
10217         delete config.items;
10218         Roo.each(xitems, this.addxtype, this);
10219     }
10220     
10221     
10222 };
10223 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10224     
10225     
10226     /**
10227      * @cfg {Roo.LayoutRegion} east  
10228      */
10229     /**
10230      * @cfg {Roo.LayoutRegion} west
10231      */
10232     /**
10233      * @cfg {Roo.LayoutRegion} south
10234      */
10235     /**
10236      * @cfg {Roo.LayoutRegion} north
10237      */
10238     /**
10239      * @cfg {Roo.LayoutRegion} center
10240      */
10241     /**
10242      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10243      */
10244     
10245     
10246     /**
10247      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10248      * @deprecated
10249      */
10250     endUpdate : function(){
10251         this.layout.endUpdate();
10252     },
10253
10254     /**
10255      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10256      *  @deprecated
10257      */
10258     beginUpdate : function(){
10259         this.layout.beginUpdate();
10260     },
10261
10262     /**
10263      * Get the BorderLayout for this dialog
10264      * @return {Roo.BorderLayout}
10265      */
10266     getLayout : function(){
10267         return this.layout;
10268     },
10269
10270     showEl : function(){
10271         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10272         if(Roo.isIE7){
10273             this.layout.layout();
10274         }
10275     },
10276
10277     // private
10278     // Use the syncHeightBeforeShow config option to control this automatically
10279     syncBodyHeight : function(){
10280         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10281         if(this.layout){this.layout.layout();}
10282     },
10283     
10284       /**
10285      * Add an xtype element (actually adds to the layout.)
10286      * @return {Object} xdata xtype object data.
10287      */
10288     
10289     addxtype : function(c) {
10290         return this.layout.addxtype(c);
10291     }
10292 });/*
10293  * Based on:
10294  * Ext JS Library 1.1.1
10295  * Copyright(c) 2006-2007, Ext JS, LLC.
10296  *
10297  * Originally Released Under LGPL - original licence link has changed is not relivant.
10298  *
10299  * Fork - LGPL
10300  * <script type="text/javascript">
10301  */
10302  
10303 /**
10304  * @class Roo.MessageBox
10305  * @static
10306  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10307  * Example usage:
10308  *<pre><code>
10309 // Basic alert:
10310 Roo.Msg.alert('Status', 'Changes saved successfully.');
10311
10312 // Prompt for user data:
10313 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10314     if (btn == 'ok'){
10315         // process text value...
10316     }
10317 });
10318
10319 // Show a dialog using config options:
10320 Roo.Msg.show({
10321    title:'Save Changes?',
10322    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10323    buttons: Roo.Msg.YESNOCANCEL,
10324    fn: processResult,
10325    animEl: 'elId'
10326 });
10327 </code></pre>
10328  * @static
10329  */
10330 Roo.MessageBox = function(){
10331     var dlg, opt, mask, waitTimer;
10332     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10333     var buttons, activeTextEl, bwidth;
10334
10335     // private
10336     var handleButton = function(button){
10337         dlg.hide();
10338         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10339     };
10340
10341     // private
10342     var handleHide = function(){
10343         if(opt && opt.cls){
10344             dlg.el.removeClass(opt.cls);
10345         }
10346         if(waitTimer){
10347             Roo.TaskMgr.stop(waitTimer);
10348             waitTimer = null;
10349         }
10350     };
10351
10352     // private
10353     var updateButtons = function(b){
10354         var width = 0;
10355         if(!b){
10356             buttons["ok"].hide();
10357             buttons["cancel"].hide();
10358             buttons["yes"].hide();
10359             buttons["no"].hide();
10360             dlg.footer.dom.style.display = 'none';
10361             return width;
10362         }
10363         dlg.footer.dom.style.display = '';
10364         for(var k in buttons){
10365             if(typeof buttons[k] != "function"){
10366                 if(b[k]){
10367                     buttons[k].show();
10368                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10369                     width += buttons[k].el.getWidth()+15;
10370                 }else{
10371                     buttons[k].hide();
10372                 }
10373             }
10374         }
10375         return width;
10376     };
10377
10378     // private
10379     var handleEsc = function(d, k, e){
10380         if(opt && opt.closable !== false){
10381             dlg.hide();
10382         }
10383         if(e){
10384             e.stopEvent();
10385         }
10386     };
10387
10388     return {
10389         /**
10390          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10391          * @return {Roo.BasicDialog} The BasicDialog element
10392          */
10393         getDialog : function(){
10394            if(!dlg){
10395                 dlg = new Roo.BasicDialog("x-msg-box", {
10396                     autoCreate : true,
10397                     shadow: true,
10398                     draggable: true,
10399                     resizable:false,
10400                     constraintoviewport:false,
10401                     fixedcenter:true,
10402                     collapsible : false,
10403                     shim:true,
10404                     modal: true,
10405                     width:400, height:100,
10406                     buttonAlign:"center",
10407                     closeClick : function(){
10408                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10409                             handleButton("no");
10410                         }else{
10411                             handleButton("cancel");
10412                         }
10413                     }
10414                 });
10415               
10416                 dlg.on("hide", handleHide);
10417                 mask = dlg.mask;
10418                 dlg.addKeyListener(27, handleEsc);
10419                 buttons = {};
10420                 var bt = this.buttonText;
10421                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10422                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10423                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10424                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10425                 bodyEl = dlg.body.createChild({
10426
10427                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10428                 });
10429                 msgEl = bodyEl.dom.firstChild;
10430                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10431                 textboxEl.enableDisplayMode();
10432                 textboxEl.addKeyListener([10,13], function(){
10433                     if(dlg.isVisible() && opt && opt.buttons){
10434                         if(opt.buttons.ok){
10435                             handleButton("ok");
10436                         }else if(opt.buttons.yes){
10437                             handleButton("yes");
10438                         }
10439                     }
10440                 });
10441                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10442                 textareaEl.enableDisplayMode();
10443                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10444                 progressEl.enableDisplayMode();
10445                 var pf = progressEl.dom.firstChild;
10446                 if (pf) {
10447                     pp = Roo.get(pf.firstChild);
10448                     pp.setHeight(pf.offsetHeight);
10449                 }
10450                 
10451             }
10452             return dlg;
10453         },
10454
10455         /**
10456          * Updates the message box body text
10457          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10458          * the XHTML-compliant non-breaking space character '&amp;#160;')
10459          * @return {Roo.MessageBox} This message box
10460          */
10461         updateText : function(text){
10462             if(!dlg.isVisible() && !opt.width){
10463                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10464             }
10465             msgEl.innerHTML = text || '&#160;';
10466       
10467             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10468             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10469             var w = Math.max(
10470                     Math.min(opt.width || cw , this.maxWidth), 
10471                     Math.max(opt.minWidth || this.minWidth, bwidth)
10472             );
10473             if(opt.prompt){
10474                 activeTextEl.setWidth(w);
10475             }
10476             if(dlg.isVisible()){
10477                 dlg.fixedcenter = false;
10478             }
10479             // to big, make it scroll. = But as usual stupid IE does not support
10480             // !important..
10481             
10482             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10483                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10484                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10485             } else {
10486                 bodyEl.dom.style.height = '';
10487                 bodyEl.dom.style.overflowY = '';
10488             }
10489             if (cw > w) {
10490                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10491             } else {
10492                 bodyEl.dom.style.overflowX = '';
10493             }
10494             
10495             dlg.setContentSize(w, bodyEl.getHeight());
10496             if(dlg.isVisible()){
10497                 dlg.fixedcenter = true;
10498             }
10499             return this;
10500         },
10501
10502         /**
10503          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10504          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10505          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10506          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10507          * @return {Roo.MessageBox} This message box
10508          */
10509         updateProgress : function(value, text){
10510             if(text){
10511                 this.updateText(text);
10512             }
10513             if (pp) { // weird bug on my firefox - for some reason this is not defined
10514                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10515             }
10516             return this;
10517         },        
10518
10519         /**
10520          * Returns true if the message box is currently displayed
10521          * @return {Boolean} True if the message box is visible, else false
10522          */
10523         isVisible : function(){
10524             return dlg && dlg.isVisible();  
10525         },
10526
10527         /**
10528          * Hides the message box if it is displayed
10529          */
10530         hide : function(){
10531             if(this.isVisible()){
10532                 dlg.hide();
10533             }  
10534         },
10535
10536         /**
10537          * Displays a new message box, or reinitializes an existing message box, based on the config options
10538          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10539          * The following config object properties are supported:
10540          * <pre>
10541 Property    Type             Description
10542 ----------  ---------------  ------------------------------------------------------------------------------------
10543 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10544                                    closes (defaults to undefined)
10545 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10546                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10547 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10548                                    progress and wait dialogs will ignore this property and always hide the
10549                                    close button as they can only be closed programmatically.
10550 cls               String           A custom CSS class to apply to the message box element
10551 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10552                                    displayed (defaults to 75)
10553 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10554                                    function will be btn (the name of the button that was clicked, if applicable,
10555                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10556                                    Progress and wait dialogs will ignore this option since they do not respond to
10557                                    user actions and can only be closed programmatically, so any required function
10558                                    should be called by the same code after it closes the dialog.
10559 icon              String           A CSS class that provides a background image to be used as an icon for
10560                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10561 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10562 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10563 modal             Boolean          False to allow user interaction with the page while the message box is
10564                                    displayed (defaults to true)
10565 msg               String           A string that will replace the existing message box body text (defaults
10566                                    to the XHTML-compliant non-breaking space character '&#160;')
10567 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10568 progress          Boolean          True to display a progress bar (defaults to false)
10569 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10570 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10571 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10572 title             String           The title text
10573 value             String           The string value to set into the active textbox element if displayed
10574 wait              Boolean          True to display a progress bar (defaults to false)
10575 width             Number           The width of the dialog in pixels
10576 </pre>
10577          *
10578          * Example usage:
10579          * <pre><code>
10580 Roo.Msg.show({
10581    title: 'Address',
10582    msg: 'Please enter your address:',
10583    width: 300,
10584    buttons: Roo.MessageBox.OKCANCEL,
10585    multiline: true,
10586    fn: saveAddress,
10587    animEl: 'addAddressBtn'
10588 });
10589 </code></pre>
10590          * @param {Object} config Configuration options
10591          * @return {Roo.MessageBox} This message box
10592          */
10593         show : function(options)
10594         {
10595             
10596             // this causes nightmares if you show one dialog after another
10597             // especially on callbacks..
10598              
10599             if(this.isVisible()){
10600                 
10601                 this.hide();
10602                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10603                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10604                 Roo.log("New Dialog Message:" +  options.msg )
10605                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10606                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10607                 
10608             }
10609             var d = this.getDialog();
10610             opt = options;
10611             d.setTitle(opt.title || "&#160;");
10612             d.close.setDisplayed(opt.closable !== false);
10613             activeTextEl = textboxEl;
10614             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10615             if(opt.prompt){
10616                 if(opt.multiline){
10617                     textboxEl.hide();
10618                     textareaEl.show();
10619                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10620                         opt.multiline : this.defaultTextHeight);
10621                     activeTextEl = textareaEl;
10622                 }else{
10623                     textboxEl.show();
10624                     textareaEl.hide();
10625                 }
10626             }else{
10627                 textboxEl.hide();
10628                 textareaEl.hide();
10629             }
10630             progressEl.setDisplayed(opt.progress === true);
10631             this.updateProgress(0);
10632             activeTextEl.dom.value = opt.value || "";
10633             if(opt.prompt){
10634                 dlg.setDefaultButton(activeTextEl);
10635             }else{
10636                 var bs = opt.buttons;
10637                 var db = null;
10638                 if(bs && bs.ok){
10639                     db = buttons["ok"];
10640                 }else if(bs && bs.yes){
10641                     db = buttons["yes"];
10642                 }
10643                 dlg.setDefaultButton(db);
10644             }
10645             bwidth = updateButtons(opt.buttons);
10646             this.updateText(opt.msg);
10647             if(opt.cls){
10648                 d.el.addClass(opt.cls);
10649             }
10650             d.proxyDrag = opt.proxyDrag === true;
10651             d.modal = opt.modal !== false;
10652             d.mask = opt.modal !== false ? mask : false;
10653             if(!d.isVisible()){
10654                 // force it to the end of the z-index stack so it gets a cursor in FF
10655                 document.body.appendChild(dlg.el.dom);
10656                 d.animateTarget = null;
10657                 d.show(options.animEl);
10658             }
10659             dlg.toFront();
10660             return this;
10661         },
10662
10663         /**
10664          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10665          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10666          * and closing the message box when the process is complete.
10667          * @param {String} title The title bar text
10668          * @param {String} msg The message box body text
10669          * @return {Roo.MessageBox} This message box
10670          */
10671         progress : function(title, msg){
10672             this.show({
10673                 title : title,
10674                 msg : msg,
10675                 buttons: false,
10676                 progress:true,
10677                 closable:false,
10678                 minWidth: this.minProgressWidth,
10679                 modal : true
10680             });
10681             return this;
10682         },
10683
10684         /**
10685          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10686          * If a callback function is passed it will be called after the user clicks the button, and the
10687          * id of the button that was clicked will be passed as the only parameter to the callback
10688          * (could also be the top-right close button).
10689          * @param {String} title The title bar text
10690          * @param {String} msg The message box body text
10691          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10692          * @param {Object} scope (optional) The scope of the callback function
10693          * @return {Roo.MessageBox} This message box
10694          */
10695         alert : function(title, msg, fn, scope){
10696             this.show({
10697                 title : title,
10698                 msg : msg,
10699                 buttons: this.OK,
10700                 fn: fn,
10701                 scope : scope,
10702                 modal : true
10703             });
10704             return this;
10705         },
10706
10707         /**
10708          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10709          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10710          * You are responsible for closing the message box when the process is complete.
10711          * @param {String} msg The message box body text
10712          * @param {String} title (optional) The title bar text
10713          * @return {Roo.MessageBox} This message box
10714          */
10715         wait : function(msg, title){
10716             this.show({
10717                 title : title,
10718                 msg : msg,
10719                 buttons: false,
10720                 closable:false,
10721                 progress:true,
10722                 modal:true,
10723                 width:300,
10724                 wait:true
10725             });
10726             waitTimer = Roo.TaskMgr.start({
10727                 run: function(i){
10728                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10729                 },
10730                 interval: 1000
10731             });
10732             return this;
10733         },
10734
10735         /**
10736          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10737          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10738          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10739          * @param {String} title The title bar text
10740          * @param {String} msg The message box body text
10741          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10742          * @param {Object} scope (optional) The scope of the callback function
10743          * @return {Roo.MessageBox} This message box
10744          */
10745         confirm : function(title, msg, fn, scope){
10746             this.show({
10747                 title : title,
10748                 msg : msg,
10749                 buttons: this.YESNO,
10750                 fn: fn,
10751                 scope : scope,
10752                 modal : true
10753             });
10754             return this;
10755         },
10756
10757         /**
10758          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10759          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10760          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10761          * (could also be the top-right close button) and the text that was entered will be passed as the two
10762          * parameters to the callback.
10763          * @param {String} title The title bar text
10764          * @param {String} msg The message box body text
10765          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10766          * @param {Object} scope (optional) The scope of the callback function
10767          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10768          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10769          * @return {Roo.MessageBox} This message box
10770          */
10771         prompt : function(title, msg, fn, scope, multiline){
10772             this.show({
10773                 title : title,
10774                 msg : msg,
10775                 buttons: this.OKCANCEL,
10776                 fn: fn,
10777                 minWidth:250,
10778                 scope : scope,
10779                 prompt:true,
10780                 multiline: multiline,
10781                 modal : true
10782             });
10783             return this;
10784         },
10785
10786         /**
10787          * Button config that displays a single OK button
10788          * @type Object
10789          */
10790         OK : {ok:true},
10791         /**
10792          * Button config that displays Yes and No buttons
10793          * @type Object
10794          */
10795         YESNO : {yes:true, no:true},
10796         /**
10797          * Button config that displays OK and Cancel buttons
10798          * @type Object
10799          */
10800         OKCANCEL : {ok:true, cancel:true},
10801         /**
10802          * Button config that displays Yes, No and Cancel buttons
10803          * @type Object
10804          */
10805         YESNOCANCEL : {yes:true, no:true, cancel:true},
10806
10807         /**
10808          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10809          * @type Number
10810          */
10811         defaultTextHeight : 75,
10812         /**
10813          * The maximum width in pixels of the message box (defaults to 600)
10814          * @type Number
10815          */
10816         maxWidth : 600,
10817         /**
10818          * The minimum width in pixels of the message box (defaults to 100)
10819          * @type Number
10820          */
10821         minWidth : 100,
10822         /**
10823          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10824          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10825          * @type Number
10826          */
10827         minProgressWidth : 250,
10828         /**
10829          * An object containing the default button text strings that can be overriden for localized language support.
10830          * Supported properties are: ok, cancel, yes and no.
10831          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10832          * @type Object
10833          */
10834         buttonText : {
10835             ok : "OK",
10836             cancel : "Cancel",
10837             yes : "Yes",
10838             no : "No"
10839         }
10840     };
10841 }();
10842
10843 /**
10844  * Shorthand for {@link Roo.MessageBox}
10845  */
10846 Roo.Msg = Roo.MessageBox;/*
10847  * Based on:
10848  * Ext JS Library 1.1.1
10849  * Copyright(c) 2006-2007, Ext JS, LLC.
10850  *
10851  * Originally Released Under LGPL - original licence link has changed is not relivant.
10852  *
10853  * Fork - LGPL
10854  * <script type="text/javascript">
10855  */
10856 /**
10857  * @class Roo.QuickTips
10858  * Provides attractive and customizable tooltips for any element.
10859  * @static
10860  */
10861 Roo.QuickTips = function(){
10862     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10863     var ce, bd, xy, dd;
10864     var visible = false, disabled = true, inited = false;
10865     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10866     
10867     var onOver = function(e){
10868         if(disabled){
10869             return;
10870         }
10871         var t = e.getTarget();
10872         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10873             return;
10874         }
10875         if(ce && t == ce.el){
10876             clearTimeout(hideProc);
10877             return;
10878         }
10879         if(t && tagEls[t.id]){
10880             tagEls[t.id].el = t;
10881             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10882             return;
10883         }
10884         var ttp, et = Roo.fly(t);
10885         var ns = cfg.namespace;
10886         if(tm.interceptTitles && t.title){
10887             ttp = t.title;
10888             t.qtip = ttp;
10889             t.removeAttribute("title");
10890             e.preventDefault();
10891         }else{
10892             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10893         }
10894         if(ttp){
10895             showProc = show.defer(tm.showDelay, tm, [{
10896                 el: t, 
10897                 text: ttp.replace(/\\n/g,'<br/>'),
10898                 width: et.getAttributeNS(ns, cfg.width),
10899                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10900                 title: et.getAttributeNS(ns, cfg.title),
10901                     cls: et.getAttributeNS(ns, cfg.cls)
10902             }]);
10903         }
10904     };
10905     
10906     var onOut = function(e){
10907         clearTimeout(showProc);
10908         var t = e.getTarget();
10909         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10910             hideProc = setTimeout(hide, tm.hideDelay);
10911         }
10912     };
10913     
10914     var onMove = function(e){
10915         if(disabled){
10916             return;
10917         }
10918         xy = e.getXY();
10919         xy[1] += 18;
10920         if(tm.trackMouse && ce){
10921             el.setXY(xy);
10922         }
10923     };
10924     
10925     var onDown = function(e){
10926         clearTimeout(showProc);
10927         clearTimeout(hideProc);
10928         if(!e.within(el)){
10929             if(tm.hideOnClick){
10930                 hide();
10931                 tm.disable();
10932                 tm.enable.defer(100, tm);
10933             }
10934         }
10935     };
10936     
10937     var getPad = function(){
10938         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10939     };
10940
10941     var show = function(o){
10942         if(disabled){
10943             return;
10944         }
10945         clearTimeout(dismissProc);
10946         ce = o;
10947         if(removeCls){ // in case manually hidden
10948             el.removeClass(removeCls);
10949             removeCls = null;
10950         }
10951         if(ce.cls){
10952             el.addClass(ce.cls);
10953             removeCls = ce.cls;
10954         }
10955         if(ce.title){
10956             tipTitle.update(ce.title);
10957             tipTitle.show();
10958         }else{
10959             tipTitle.update('');
10960             tipTitle.hide();
10961         }
10962         el.dom.style.width  = tm.maxWidth+'px';
10963         //tipBody.dom.style.width = '';
10964         tipBodyText.update(o.text);
10965         var p = getPad(), w = ce.width;
10966         if(!w){
10967             var td = tipBodyText.dom;
10968             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10969             if(aw > tm.maxWidth){
10970                 w = tm.maxWidth;
10971             }else if(aw < tm.minWidth){
10972                 w = tm.minWidth;
10973             }else{
10974                 w = aw;
10975             }
10976         }
10977         //tipBody.setWidth(w);
10978         el.setWidth(parseInt(w, 10) + p);
10979         if(ce.autoHide === false){
10980             close.setDisplayed(true);
10981             if(dd){
10982                 dd.unlock();
10983             }
10984         }else{
10985             close.setDisplayed(false);
10986             if(dd){
10987                 dd.lock();
10988             }
10989         }
10990         if(xy){
10991             el.avoidY = xy[1]-18;
10992             el.setXY(xy);
10993         }
10994         if(tm.animate){
10995             el.setOpacity(.1);
10996             el.setStyle("visibility", "visible");
10997             el.fadeIn({callback: afterShow});
10998         }else{
10999             afterShow();
11000         }
11001     };
11002     
11003     var afterShow = function(){
11004         if(ce){
11005             el.show();
11006             esc.enable();
11007             if(tm.autoDismiss && ce.autoHide !== false){
11008                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11009             }
11010         }
11011     };
11012     
11013     var hide = function(noanim){
11014         clearTimeout(dismissProc);
11015         clearTimeout(hideProc);
11016         ce = null;
11017         if(el.isVisible()){
11018             esc.disable();
11019             if(noanim !== true && tm.animate){
11020                 el.fadeOut({callback: afterHide});
11021             }else{
11022                 afterHide();
11023             } 
11024         }
11025     };
11026     
11027     var afterHide = function(){
11028         el.hide();
11029         if(removeCls){
11030             el.removeClass(removeCls);
11031             removeCls = null;
11032         }
11033     };
11034     
11035     return {
11036         /**
11037         * @cfg {Number} minWidth
11038         * The minimum width of the quick tip (defaults to 40)
11039         */
11040        minWidth : 40,
11041         /**
11042         * @cfg {Number} maxWidth
11043         * The maximum width of the quick tip (defaults to 300)
11044         */
11045        maxWidth : 300,
11046         /**
11047         * @cfg {Boolean} interceptTitles
11048         * True to automatically use the element's DOM title value if available (defaults to false)
11049         */
11050        interceptTitles : false,
11051         /**
11052         * @cfg {Boolean} trackMouse
11053         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11054         */
11055        trackMouse : false,
11056         /**
11057         * @cfg {Boolean} hideOnClick
11058         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11059         */
11060        hideOnClick : true,
11061         /**
11062         * @cfg {Number} showDelay
11063         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11064         */
11065        showDelay : 500,
11066         /**
11067         * @cfg {Number} hideDelay
11068         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11069         */
11070        hideDelay : 200,
11071         /**
11072         * @cfg {Boolean} autoHide
11073         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11074         * Used in conjunction with hideDelay.
11075         */
11076        autoHide : true,
11077         /**
11078         * @cfg {Boolean}
11079         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11080         * (defaults to true).  Used in conjunction with autoDismissDelay.
11081         */
11082        autoDismiss : true,
11083         /**
11084         * @cfg {Number}
11085         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11086         */
11087        autoDismissDelay : 5000,
11088        /**
11089         * @cfg {Boolean} animate
11090         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11091         */
11092        animate : false,
11093
11094        /**
11095         * @cfg {String} title
11096         * Title text to display (defaults to '').  This can be any valid HTML markup.
11097         */
11098         title: '',
11099        /**
11100         * @cfg {String} text
11101         * Body text to display (defaults to '').  This can be any valid HTML markup.
11102         */
11103         text : '',
11104        /**
11105         * @cfg {String} cls
11106         * A CSS class to apply to the base quick tip element (defaults to '').
11107         */
11108         cls : '',
11109        /**
11110         * @cfg {Number} width
11111         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11112         * minWidth or maxWidth.
11113         */
11114         width : null,
11115
11116     /**
11117      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11118      * or display QuickTips in a page.
11119      */
11120        init : function(){
11121           tm = Roo.QuickTips;
11122           cfg = tm.tagConfig;
11123           if(!inited){
11124               if(!Roo.isReady){ // allow calling of init() before onReady
11125                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11126                   return;
11127               }
11128               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11129               el.fxDefaults = {stopFx: true};
11130               // maximum custom styling
11131               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11132               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11133               tipTitle = el.child('h3');
11134               tipTitle.enableDisplayMode("block");
11135               tipBody = el.child('div.x-tip-bd');
11136               tipBodyText = el.child('div.x-tip-bd-inner');
11137               //bdLeft = el.child('div.x-tip-bd-left');
11138               //bdRight = el.child('div.x-tip-bd-right');
11139               close = el.child('div.x-tip-close');
11140               close.enableDisplayMode("block");
11141               close.on("click", hide);
11142               var d = Roo.get(document);
11143               d.on("mousedown", onDown);
11144               d.on("mouseover", onOver);
11145               d.on("mouseout", onOut);
11146               d.on("mousemove", onMove);
11147               esc = d.addKeyListener(27, hide);
11148               esc.disable();
11149               if(Roo.dd.DD){
11150                   dd = el.initDD("default", null, {
11151                       onDrag : function(){
11152                           el.sync();  
11153                       }
11154                   });
11155                   dd.setHandleElId(tipTitle.id);
11156                   dd.lock();
11157               }
11158               inited = true;
11159           }
11160           this.enable(); 
11161        },
11162
11163     /**
11164      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11165      * are supported:
11166      * <pre>
11167 Property    Type                   Description
11168 ----------  ---------------------  ------------------------------------------------------------------------
11169 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11170      * </ul>
11171      * @param {Object} config The config object
11172      */
11173        register : function(config){
11174            var cs = config instanceof Array ? config : arguments;
11175            for(var i = 0, len = cs.length; i < len; i++) {
11176                var c = cs[i];
11177                var target = c.target;
11178                if(target){
11179                    if(target instanceof Array){
11180                        for(var j = 0, jlen = target.length; j < jlen; j++){
11181                            tagEls[target[j]] = c;
11182                        }
11183                    }else{
11184                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11185                    }
11186                }
11187            }
11188        },
11189
11190     /**
11191      * Removes this quick tip from its element and destroys it.
11192      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11193      */
11194        unregister : function(el){
11195            delete tagEls[Roo.id(el)];
11196        },
11197
11198     /**
11199      * Enable this quick tip.
11200      */
11201        enable : function(){
11202            if(inited && disabled){
11203                locks.pop();
11204                if(locks.length < 1){
11205                    disabled = false;
11206                }
11207            }
11208        },
11209
11210     /**
11211      * Disable this quick tip.
11212      */
11213        disable : function(){
11214           disabled = true;
11215           clearTimeout(showProc);
11216           clearTimeout(hideProc);
11217           clearTimeout(dismissProc);
11218           if(ce){
11219               hide(true);
11220           }
11221           locks.push(1);
11222        },
11223
11224     /**
11225      * Returns true if the quick tip is enabled, else false.
11226      */
11227        isEnabled : function(){
11228             return !disabled;
11229        },
11230
11231         // private
11232        tagConfig : {
11233            namespace : "roo", // was ext?? this may break..
11234            alt_namespace : "ext",
11235            attribute : "qtip",
11236            width : "width",
11237            target : "target",
11238            title : "qtitle",
11239            hide : "hide",
11240            cls : "qclass"
11241        }
11242    };
11243 }();
11244
11245 // backwards compat
11246 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11247  * Based on:
11248  * Ext JS Library 1.1.1
11249  * Copyright(c) 2006-2007, Ext JS, LLC.
11250  *
11251  * Originally Released Under LGPL - original licence link has changed is not relivant.
11252  *
11253  * Fork - LGPL
11254  * <script type="text/javascript">
11255  */
11256  
11257
11258 /**
11259  * @class Roo.tree.TreePanel
11260  * @extends Roo.data.Tree
11261  * @cfg {Roo.tree.TreeNode} root The root node
11262  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11263  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11264  * @cfg {Boolean} enableDD true to enable drag and drop
11265  * @cfg {Boolean} enableDrag true to enable just drag
11266  * @cfg {Boolean} enableDrop true to enable just drop
11267  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11268  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11269  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11270  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11271  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11272  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11273  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11274  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11275  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11276  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11277  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11278  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11279  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11280  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11281  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11282  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11283  * 
11284  * @constructor
11285  * @param {String/HTMLElement/Element} el The container element
11286  * @param {Object} config
11287  */
11288 Roo.tree.TreePanel = function(el, config){
11289     var root = false;
11290     var loader = false;
11291     if (config.root) {
11292         root = config.root;
11293         delete config.root;
11294     }
11295     if (config.loader) {
11296         loader = config.loader;
11297         delete config.loader;
11298     }
11299     
11300     Roo.apply(this, config);
11301     Roo.tree.TreePanel.superclass.constructor.call(this);
11302     this.el = Roo.get(el);
11303     this.el.addClass('x-tree');
11304     //console.log(root);
11305     if (root) {
11306         this.setRootNode( Roo.factory(root, Roo.tree));
11307     }
11308     if (loader) {
11309         this.loader = Roo.factory(loader, Roo.tree);
11310     }
11311    /**
11312     * Read-only. The id of the container element becomes this TreePanel's id.
11313     */
11314     this.id = this.el.id;
11315     this.addEvents({
11316         /**
11317         * @event beforeload
11318         * Fires before a node is loaded, return false to cancel
11319         * @param {Node} node The node being loaded
11320         */
11321         "beforeload" : true,
11322         /**
11323         * @event load
11324         * Fires when a node is loaded
11325         * @param {Node} node The node that was loaded
11326         */
11327         "load" : true,
11328         /**
11329         * @event textchange
11330         * Fires when the text for a node is changed
11331         * @param {Node} node The node
11332         * @param {String} text The new text
11333         * @param {String} oldText The old text
11334         */
11335         "textchange" : true,
11336         /**
11337         * @event beforeexpand
11338         * Fires before a node is expanded, return false to cancel.
11339         * @param {Node} node The node
11340         * @param {Boolean} deep
11341         * @param {Boolean} anim
11342         */
11343         "beforeexpand" : true,
11344         /**
11345         * @event beforecollapse
11346         * Fires before a node is collapsed, return false to cancel.
11347         * @param {Node} node The node
11348         * @param {Boolean} deep
11349         * @param {Boolean} anim
11350         */
11351         "beforecollapse" : true,
11352         /**
11353         * @event expand
11354         * Fires when a node is expanded
11355         * @param {Node} node The node
11356         */
11357         "expand" : true,
11358         /**
11359         * @event disabledchange
11360         * Fires when the disabled status of a node changes
11361         * @param {Node} node The node
11362         * @param {Boolean} disabled
11363         */
11364         "disabledchange" : true,
11365         /**
11366         * @event collapse
11367         * Fires when a node is collapsed
11368         * @param {Node} node The node
11369         */
11370         "collapse" : true,
11371         /**
11372         * @event beforeclick
11373         * Fires before click processing on a node. Return false to cancel the default action.
11374         * @param {Node} node The node
11375         * @param {Roo.EventObject} e The event object
11376         */
11377         "beforeclick":true,
11378         /**
11379         * @event checkchange
11380         * Fires when a node with a checkbox's checked property changes
11381         * @param {Node} this This node
11382         * @param {Boolean} checked
11383         */
11384         "checkchange":true,
11385         /**
11386         * @event click
11387         * Fires when a node is clicked
11388         * @param {Node} node The node
11389         * @param {Roo.EventObject} e The event object
11390         */
11391         "click":true,
11392         /**
11393         * @event dblclick
11394         * Fires when a node is double clicked
11395         * @param {Node} node The node
11396         * @param {Roo.EventObject} e The event object
11397         */
11398         "dblclick":true,
11399         /**
11400         * @event contextmenu
11401         * Fires when a node is right clicked
11402         * @param {Node} node The node
11403         * @param {Roo.EventObject} e The event object
11404         */
11405         "contextmenu":true,
11406         /**
11407         * @event beforechildrenrendered
11408         * Fires right before the child nodes for a node are rendered
11409         * @param {Node} node The node
11410         */
11411         "beforechildrenrendered":true,
11412         /**
11413         * @event startdrag
11414         * Fires when a node starts being dragged
11415         * @param {Roo.tree.TreePanel} this
11416         * @param {Roo.tree.TreeNode} node
11417         * @param {event} e The raw browser event
11418         */ 
11419        "startdrag" : true,
11420        /**
11421         * @event enddrag
11422         * Fires when a drag operation is complete
11423         * @param {Roo.tree.TreePanel} this
11424         * @param {Roo.tree.TreeNode} node
11425         * @param {event} e The raw browser event
11426         */
11427        "enddrag" : true,
11428        /**
11429         * @event dragdrop
11430         * Fires when a dragged node is dropped on a valid DD target
11431         * @param {Roo.tree.TreePanel} this
11432         * @param {Roo.tree.TreeNode} node
11433         * @param {DD} dd The dd it was dropped on
11434         * @param {event} e The raw browser event
11435         */
11436        "dragdrop" : true,
11437        /**
11438         * @event beforenodedrop
11439         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11440         * passed to handlers has the following properties:<br />
11441         * <ul style="padding:5px;padding-left:16px;">
11442         * <li>tree - The TreePanel</li>
11443         * <li>target - The node being targeted for the drop</li>
11444         * <li>data - The drag data from the drag source</li>
11445         * <li>point - The point of the drop - append, above or below</li>
11446         * <li>source - The drag source</li>
11447         * <li>rawEvent - Raw mouse event</li>
11448         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11449         * to be inserted by setting them on this object.</li>
11450         * <li>cancel - Set this to true to cancel the drop.</li>
11451         * </ul>
11452         * @param {Object} dropEvent
11453         */
11454        "beforenodedrop" : true,
11455        /**
11456         * @event nodedrop
11457         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11458         * passed to handlers has the following properties:<br />
11459         * <ul style="padding:5px;padding-left:16px;">
11460         * <li>tree - The TreePanel</li>
11461         * <li>target - The node being targeted for the drop</li>
11462         * <li>data - The drag data from the drag source</li>
11463         * <li>point - The point of the drop - append, above or below</li>
11464         * <li>source - The drag source</li>
11465         * <li>rawEvent - Raw mouse event</li>
11466         * <li>dropNode - Dropped node(s).</li>
11467         * </ul>
11468         * @param {Object} dropEvent
11469         */
11470        "nodedrop" : true,
11471         /**
11472         * @event nodedragover
11473         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11474         * passed to handlers has the following properties:<br />
11475         * <ul style="padding:5px;padding-left:16px;">
11476         * <li>tree - The TreePanel</li>
11477         * <li>target - The node being targeted for the drop</li>
11478         * <li>data - The drag data from the drag source</li>
11479         * <li>point - The point of the drop - append, above or below</li>
11480         * <li>source - The drag source</li>
11481         * <li>rawEvent - Raw mouse event</li>
11482         * <li>dropNode - Drop node(s) provided by the source.</li>
11483         * <li>cancel - Set this to true to signal drop not allowed.</li>
11484         * </ul>
11485         * @param {Object} dragOverEvent
11486         */
11487        "nodedragover" : true,
11488        /**
11489         * @event appendnode
11490         * Fires when append node to the tree
11491         * @param {Roo.tree.TreePanel} this
11492         * @param {Roo.tree.TreeNode} node
11493         * @param {Number} index The index of the newly appended node
11494         */
11495        "appendnode" : true
11496         
11497     });
11498     if(this.singleExpand){
11499        this.on("beforeexpand", this.restrictExpand, this);
11500     }
11501     if (this.editor) {
11502         this.editor.tree = this;
11503         this.editor = Roo.factory(this.editor, Roo.tree);
11504     }
11505     
11506     if (this.selModel) {
11507         this.selModel = Roo.factory(this.selModel, Roo.tree);
11508     }
11509    
11510 };
11511 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11512     rootVisible : true,
11513     animate: Roo.enableFx,
11514     lines : true,
11515     enableDD : false,
11516     hlDrop : Roo.enableFx,
11517   
11518     renderer: false,
11519     
11520     rendererTip: false,
11521     // private
11522     restrictExpand : function(node){
11523         var p = node.parentNode;
11524         if(p){
11525             if(p.expandedChild && p.expandedChild.parentNode == p){
11526                 p.expandedChild.collapse();
11527             }
11528             p.expandedChild = node;
11529         }
11530     },
11531
11532     // private override
11533     setRootNode : function(node){
11534         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11535         if(!this.rootVisible){
11536             node.ui = new Roo.tree.RootTreeNodeUI(node);
11537         }
11538         return node;
11539     },
11540
11541     /**
11542      * Returns the container element for this TreePanel
11543      */
11544     getEl : function(){
11545         return this.el;
11546     },
11547
11548     /**
11549      * Returns the default TreeLoader for this TreePanel
11550      */
11551     getLoader : function(){
11552         return this.loader;
11553     },
11554
11555     /**
11556      * Expand all nodes
11557      */
11558     expandAll : function(){
11559         this.root.expand(true);
11560     },
11561
11562     /**
11563      * Collapse all nodes
11564      */
11565     collapseAll : function(){
11566         this.root.collapse(true);
11567     },
11568
11569     /**
11570      * Returns the selection model used by this TreePanel
11571      */
11572     getSelectionModel : function(){
11573         if(!this.selModel){
11574             this.selModel = new Roo.tree.DefaultSelectionModel();
11575         }
11576         return this.selModel;
11577     },
11578
11579     /**
11580      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11581      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11582      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11583      * @return {Array}
11584      */
11585     getChecked : function(a, startNode){
11586         startNode = startNode || this.root;
11587         var r = [];
11588         var f = function(){
11589             if(this.attributes.checked){
11590                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11591             }
11592         }
11593         startNode.cascade(f);
11594         return r;
11595     },
11596
11597     /**
11598      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11599      * @param {String} path
11600      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11601      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11602      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11603      */
11604     expandPath : function(path, attr, callback){
11605         attr = attr || "id";
11606         var keys = path.split(this.pathSeparator);
11607         var curNode = this.root;
11608         if(curNode.attributes[attr] != keys[1]){ // invalid root
11609             if(callback){
11610                 callback(false, null);
11611             }
11612             return;
11613         }
11614         var index = 1;
11615         var f = function(){
11616             if(++index == keys.length){
11617                 if(callback){
11618                     callback(true, curNode);
11619                 }
11620                 return;
11621             }
11622             var c = curNode.findChild(attr, keys[index]);
11623             if(!c){
11624                 if(callback){
11625                     callback(false, curNode);
11626                 }
11627                 return;
11628             }
11629             curNode = c;
11630             c.expand(false, false, f);
11631         };
11632         curNode.expand(false, false, f);
11633     },
11634
11635     /**
11636      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11637      * @param {String} path
11638      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11639      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11640      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11641      */
11642     selectPath : function(path, attr, callback){
11643         attr = attr || "id";
11644         var keys = path.split(this.pathSeparator);
11645         var v = keys.pop();
11646         if(keys.length > 0){
11647             var f = function(success, node){
11648                 if(success && node){
11649                     var n = node.findChild(attr, v);
11650                     if(n){
11651                         n.select();
11652                         if(callback){
11653                             callback(true, n);
11654                         }
11655                     }else if(callback){
11656                         callback(false, n);
11657                     }
11658                 }else{
11659                     if(callback){
11660                         callback(false, n);
11661                     }
11662                 }
11663             };
11664             this.expandPath(keys.join(this.pathSeparator), attr, f);
11665         }else{
11666             this.root.select();
11667             if(callback){
11668                 callback(true, this.root);
11669             }
11670         }
11671     },
11672
11673     getTreeEl : function(){
11674         return this.el;
11675     },
11676
11677     /**
11678      * Trigger rendering of this TreePanel
11679      */
11680     render : function(){
11681         if (this.innerCt) {
11682             return this; // stop it rendering more than once!!
11683         }
11684         
11685         this.innerCt = this.el.createChild({tag:"ul",
11686                cls:"x-tree-root-ct " +
11687                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11688
11689         if(this.containerScroll){
11690             Roo.dd.ScrollManager.register(this.el);
11691         }
11692         if((this.enableDD || this.enableDrop) && !this.dropZone){
11693            /**
11694             * The dropZone used by this tree if drop is enabled
11695             * @type Roo.tree.TreeDropZone
11696             */
11697              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11698                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11699            });
11700         }
11701         if((this.enableDD || this.enableDrag) && !this.dragZone){
11702            /**
11703             * The dragZone used by this tree if drag is enabled
11704             * @type Roo.tree.TreeDragZone
11705             */
11706             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11707                ddGroup: this.ddGroup || "TreeDD",
11708                scroll: this.ddScroll
11709            });
11710         }
11711         this.getSelectionModel().init(this);
11712         if (!this.root) {
11713             Roo.log("ROOT not set in tree");
11714             return this;
11715         }
11716         this.root.render();
11717         if(!this.rootVisible){
11718             this.root.renderChildren();
11719         }
11720         return this;
11721     }
11722 });/*
11723  * Based on:
11724  * Ext JS Library 1.1.1
11725  * Copyright(c) 2006-2007, Ext JS, LLC.
11726  *
11727  * Originally Released Under LGPL - original licence link has changed is not relivant.
11728  *
11729  * Fork - LGPL
11730  * <script type="text/javascript">
11731  */
11732  
11733
11734 /**
11735  * @class Roo.tree.DefaultSelectionModel
11736  * @extends Roo.util.Observable
11737  * The default single selection for a TreePanel.
11738  * @param {Object} cfg Configuration
11739  */
11740 Roo.tree.DefaultSelectionModel = function(cfg){
11741    this.selNode = null;
11742    
11743    
11744    
11745    this.addEvents({
11746        /**
11747         * @event selectionchange
11748         * Fires when the selected node changes
11749         * @param {DefaultSelectionModel} this
11750         * @param {TreeNode} node the new selection
11751         */
11752        "selectionchange" : true,
11753
11754        /**
11755         * @event beforeselect
11756         * Fires before the selected node changes, return false to cancel the change
11757         * @param {DefaultSelectionModel} this
11758         * @param {TreeNode} node the new selection
11759         * @param {TreeNode} node the old selection
11760         */
11761        "beforeselect" : true
11762    });
11763    
11764     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11765 };
11766
11767 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11768     init : function(tree){
11769         this.tree = tree;
11770         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11771         tree.on("click", this.onNodeClick, this);
11772     },
11773     
11774     onNodeClick : function(node, e){
11775         if (e.ctrlKey && this.selNode == node)  {
11776             this.unselect(node);
11777             return;
11778         }
11779         this.select(node);
11780     },
11781     
11782     /**
11783      * Select a node.
11784      * @param {TreeNode} node The node to select
11785      * @return {TreeNode} The selected node
11786      */
11787     select : function(node){
11788         var last = this.selNode;
11789         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11790             if(last){
11791                 last.ui.onSelectedChange(false);
11792             }
11793             this.selNode = node;
11794             node.ui.onSelectedChange(true);
11795             this.fireEvent("selectionchange", this, node, last);
11796         }
11797         return node;
11798     },
11799     
11800     /**
11801      * Deselect a node.
11802      * @param {TreeNode} node The node to unselect
11803      */
11804     unselect : function(node){
11805         if(this.selNode == node){
11806             this.clearSelections();
11807         }    
11808     },
11809     
11810     /**
11811      * Clear all selections
11812      */
11813     clearSelections : function(){
11814         var n = this.selNode;
11815         if(n){
11816             n.ui.onSelectedChange(false);
11817             this.selNode = null;
11818             this.fireEvent("selectionchange", this, null);
11819         }
11820         return n;
11821     },
11822     
11823     /**
11824      * Get the selected node
11825      * @return {TreeNode} The selected node
11826      */
11827     getSelectedNode : function(){
11828         return this.selNode;    
11829     },
11830     
11831     /**
11832      * Returns true if the node is selected
11833      * @param {TreeNode} node The node to check
11834      * @return {Boolean}
11835      */
11836     isSelected : function(node){
11837         return this.selNode == node;  
11838     },
11839
11840     /**
11841      * Selects the node above the selected node in the tree, intelligently walking the nodes
11842      * @return TreeNode The new selection
11843      */
11844     selectPrevious : function(){
11845         var s = this.selNode || this.lastSelNode;
11846         if(!s){
11847             return null;
11848         }
11849         var ps = s.previousSibling;
11850         if(ps){
11851             if(!ps.isExpanded() || ps.childNodes.length < 1){
11852                 return this.select(ps);
11853             } else{
11854                 var lc = ps.lastChild;
11855                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11856                     lc = lc.lastChild;
11857                 }
11858                 return this.select(lc);
11859             }
11860         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11861             return this.select(s.parentNode);
11862         }
11863         return null;
11864     },
11865
11866     /**
11867      * Selects the node above the selected node in the tree, intelligently walking the nodes
11868      * @return TreeNode The new selection
11869      */
11870     selectNext : function(){
11871         var s = this.selNode || this.lastSelNode;
11872         if(!s){
11873             return null;
11874         }
11875         if(s.firstChild && s.isExpanded()){
11876              return this.select(s.firstChild);
11877          }else if(s.nextSibling){
11878              return this.select(s.nextSibling);
11879          }else if(s.parentNode){
11880             var newS = null;
11881             s.parentNode.bubble(function(){
11882                 if(this.nextSibling){
11883                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11884                     return false;
11885                 }
11886             });
11887             return newS;
11888          }
11889         return null;
11890     },
11891
11892     onKeyDown : function(e){
11893         var s = this.selNode || this.lastSelNode;
11894         // undesirable, but required
11895         var sm = this;
11896         if(!s){
11897             return;
11898         }
11899         var k = e.getKey();
11900         switch(k){
11901              case e.DOWN:
11902                  e.stopEvent();
11903                  this.selectNext();
11904              break;
11905              case e.UP:
11906                  e.stopEvent();
11907                  this.selectPrevious();
11908              break;
11909              case e.RIGHT:
11910                  e.preventDefault();
11911                  if(s.hasChildNodes()){
11912                      if(!s.isExpanded()){
11913                          s.expand();
11914                      }else if(s.firstChild){
11915                          this.select(s.firstChild, e);
11916                      }
11917                  }
11918              break;
11919              case e.LEFT:
11920                  e.preventDefault();
11921                  if(s.hasChildNodes() && s.isExpanded()){
11922                      s.collapse();
11923                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11924                      this.select(s.parentNode, e);
11925                  }
11926              break;
11927         };
11928     }
11929 });
11930
11931 /**
11932  * @class Roo.tree.MultiSelectionModel
11933  * @extends Roo.util.Observable
11934  * Multi selection for a TreePanel.
11935  * @param {Object} cfg Configuration
11936  */
11937 Roo.tree.MultiSelectionModel = function(){
11938    this.selNodes = [];
11939    this.selMap = {};
11940    this.addEvents({
11941        /**
11942         * @event selectionchange
11943         * Fires when the selected nodes change
11944         * @param {MultiSelectionModel} this
11945         * @param {Array} nodes Array of the selected nodes
11946         */
11947        "selectionchange" : true
11948    });
11949    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11950    
11951 };
11952
11953 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11954     init : function(tree){
11955         this.tree = tree;
11956         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11957         tree.on("click", this.onNodeClick, this);
11958     },
11959     
11960     onNodeClick : function(node, e){
11961         this.select(node, e, e.ctrlKey);
11962     },
11963     
11964     /**
11965      * Select a node.
11966      * @param {TreeNode} node The node to select
11967      * @param {EventObject} e (optional) An event associated with the selection
11968      * @param {Boolean} keepExisting True to retain existing selections
11969      * @return {TreeNode} The selected node
11970      */
11971     select : function(node, e, keepExisting){
11972         if(keepExisting !== true){
11973             this.clearSelections(true);
11974         }
11975         if(this.isSelected(node)){
11976             this.lastSelNode = node;
11977             return node;
11978         }
11979         this.selNodes.push(node);
11980         this.selMap[node.id] = node;
11981         this.lastSelNode = node;
11982         node.ui.onSelectedChange(true);
11983         this.fireEvent("selectionchange", this, this.selNodes);
11984         return node;
11985     },
11986     
11987     /**
11988      * Deselect a node.
11989      * @param {TreeNode} node The node to unselect
11990      */
11991     unselect : function(node){
11992         if(this.selMap[node.id]){
11993             node.ui.onSelectedChange(false);
11994             var sn = this.selNodes;
11995             var index = -1;
11996             if(sn.indexOf){
11997                 index = sn.indexOf(node);
11998             }else{
11999                 for(var i = 0, len = sn.length; i < len; i++){
12000                     if(sn[i] == node){
12001                         index = i;
12002                         break;
12003                     }
12004                 }
12005             }
12006             if(index != -1){
12007                 this.selNodes.splice(index, 1);
12008             }
12009             delete this.selMap[node.id];
12010             this.fireEvent("selectionchange", this, this.selNodes);
12011         }
12012     },
12013     
12014     /**
12015      * Clear all selections
12016      */
12017     clearSelections : function(suppressEvent){
12018         var sn = this.selNodes;
12019         if(sn.length > 0){
12020             for(var i = 0, len = sn.length; i < len; i++){
12021                 sn[i].ui.onSelectedChange(false);
12022             }
12023             this.selNodes = [];
12024             this.selMap = {};
12025             if(suppressEvent !== true){
12026                 this.fireEvent("selectionchange", this, this.selNodes);
12027             }
12028         }
12029     },
12030     
12031     /**
12032      * Returns true if the node is selected
12033      * @param {TreeNode} node The node to check
12034      * @return {Boolean}
12035      */
12036     isSelected : function(node){
12037         return this.selMap[node.id] ? true : false;  
12038     },
12039     
12040     /**
12041      * Returns an array of the selected nodes
12042      * @return {Array}
12043      */
12044     getSelectedNodes : function(){
12045         return this.selNodes;    
12046     },
12047
12048     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12049
12050     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12051
12052     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064 /**
12065  * @class Roo.tree.TreeNode
12066  * @extends Roo.data.Node
12067  * @cfg {String} text The text for this node
12068  * @cfg {Boolean} expanded true to start the node expanded
12069  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12070  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12071  * @cfg {Boolean} disabled true to start the node disabled
12072  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12073  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12074  * @cfg {String} cls A css class to be added to the node
12075  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12076  * @cfg {String} href URL of the link used for the node (defaults to #)
12077  * @cfg {String} hrefTarget target frame for the link
12078  * @cfg {String} qtip An Ext QuickTip for the node
12079  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12080  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12081  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12082  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12083  * (defaults to undefined with no checkbox rendered)
12084  * @constructor
12085  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12086  */
12087 Roo.tree.TreeNode = function(attributes){
12088     attributes = attributes || {};
12089     if(typeof attributes == "string"){
12090         attributes = {text: attributes};
12091     }
12092     this.childrenRendered = false;
12093     this.rendered = false;
12094     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12095     this.expanded = attributes.expanded === true;
12096     this.isTarget = attributes.isTarget !== false;
12097     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12098     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12099
12100     /**
12101      * Read-only. The text for this node. To change it use setText().
12102      * @type String
12103      */
12104     this.text = attributes.text;
12105     /**
12106      * True if this node is disabled.
12107      * @type Boolean
12108      */
12109     this.disabled = attributes.disabled === true;
12110
12111     this.addEvents({
12112         /**
12113         * @event textchange
12114         * Fires when the text for this node is changed
12115         * @param {Node} this This node
12116         * @param {String} text The new text
12117         * @param {String} oldText The old text
12118         */
12119         "textchange" : true,
12120         /**
12121         * @event beforeexpand
12122         * Fires before this node is expanded, return false to cancel.
12123         * @param {Node} this This node
12124         * @param {Boolean} deep
12125         * @param {Boolean} anim
12126         */
12127         "beforeexpand" : true,
12128         /**
12129         * @event beforecollapse
12130         * Fires before this node is collapsed, return false to cancel.
12131         * @param {Node} this This node
12132         * @param {Boolean} deep
12133         * @param {Boolean} anim
12134         */
12135         "beforecollapse" : true,
12136         /**
12137         * @event expand
12138         * Fires when this node is expanded
12139         * @param {Node} this This node
12140         */
12141         "expand" : true,
12142         /**
12143         * @event disabledchange
12144         * Fires when the disabled status of this node changes
12145         * @param {Node} this This node
12146         * @param {Boolean} disabled
12147         */
12148         "disabledchange" : true,
12149         /**
12150         * @event collapse
12151         * Fires when this node is collapsed
12152         * @param {Node} this This node
12153         */
12154         "collapse" : true,
12155         /**
12156         * @event beforeclick
12157         * Fires before click processing. Return false to cancel the default action.
12158         * @param {Node} this This node
12159         * @param {Roo.EventObject} e The event object
12160         */
12161         "beforeclick":true,
12162         /**
12163         * @event checkchange
12164         * Fires when a node with a checkbox's checked property changes
12165         * @param {Node} this This node
12166         * @param {Boolean} checked
12167         */
12168         "checkchange":true,
12169         /**
12170         * @event click
12171         * Fires when this node is clicked
12172         * @param {Node} this This node
12173         * @param {Roo.EventObject} e The event object
12174         */
12175         "click":true,
12176         /**
12177         * @event dblclick
12178         * Fires when this node is double clicked
12179         * @param {Node} this This node
12180         * @param {Roo.EventObject} e The event object
12181         */
12182         "dblclick":true,
12183         /**
12184         * @event contextmenu
12185         * Fires when this node is right clicked
12186         * @param {Node} this This node
12187         * @param {Roo.EventObject} e The event object
12188         */
12189         "contextmenu":true,
12190         /**
12191         * @event beforechildrenrendered
12192         * Fires right before the child nodes for this node are rendered
12193         * @param {Node} this This node
12194         */
12195         "beforechildrenrendered":true
12196     });
12197
12198     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12199
12200     /**
12201      * Read-only. The UI for this node
12202      * @type TreeNodeUI
12203      */
12204     this.ui = new uiClass(this);
12205     
12206     // finally support items[]
12207     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12208         return;
12209     }
12210     
12211     
12212     Roo.each(this.attributes.items, function(c) {
12213         this.appendChild(Roo.factory(c,Roo.Tree));
12214     }, this);
12215     delete this.attributes.items;
12216     
12217     
12218     
12219 };
12220 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12221     preventHScroll: true,
12222     /**
12223      * Returns true if this node is expanded
12224      * @return {Boolean}
12225      */
12226     isExpanded : function(){
12227         return this.expanded;
12228     },
12229
12230     /**
12231      * Returns the UI object for this node
12232      * @return {TreeNodeUI}
12233      */
12234     getUI : function(){
12235         return this.ui;
12236     },
12237
12238     // private override
12239     setFirstChild : function(node){
12240         var of = this.firstChild;
12241         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12242         if(this.childrenRendered && of && node != of){
12243             of.renderIndent(true, true);
12244         }
12245         if(this.rendered){
12246             this.renderIndent(true, true);
12247         }
12248     },
12249
12250     // private override
12251     setLastChild : function(node){
12252         var ol = this.lastChild;
12253         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12254         if(this.childrenRendered && ol && node != ol){
12255             ol.renderIndent(true, true);
12256         }
12257         if(this.rendered){
12258             this.renderIndent(true, true);
12259         }
12260     },
12261
12262     // these methods are overridden to provide lazy rendering support
12263     // private override
12264     appendChild : function()
12265     {
12266         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12267         if(node && this.childrenRendered){
12268             node.render();
12269         }
12270         this.ui.updateExpandIcon();
12271         return node;
12272     },
12273
12274     // private override
12275     removeChild : function(node){
12276         this.ownerTree.getSelectionModel().unselect(node);
12277         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12278         // if it's been rendered remove dom node
12279         if(this.childrenRendered){
12280             node.ui.remove();
12281         }
12282         if(this.childNodes.length < 1){
12283             this.collapse(false, false);
12284         }else{
12285             this.ui.updateExpandIcon();
12286         }
12287         if(!this.firstChild) {
12288             this.childrenRendered = false;
12289         }
12290         return node;
12291     },
12292
12293     // private override
12294     insertBefore : function(node, refNode){
12295         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12296         if(newNode && refNode && this.childrenRendered){
12297             node.render();
12298         }
12299         this.ui.updateExpandIcon();
12300         return newNode;
12301     },
12302
12303     /**
12304      * Sets the text for this node
12305      * @param {String} text
12306      */
12307     setText : function(text){
12308         var oldText = this.text;
12309         this.text = text;
12310         this.attributes.text = text;
12311         if(this.rendered){ // event without subscribing
12312             this.ui.onTextChange(this, text, oldText);
12313         }
12314         this.fireEvent("textchange", this, text, oldText);
12315     },
12316
12317     /**
12318      * Triggers selection of this node
12319      */
12320     select : function(){
12321         this.getOwnerTree().getSelectionModel().select(this);
12322     },
12323
12324     /**
12325      * Triggers deselection of this node
12326      */
12327     unselect : function(){
12328         this.getOwnerTree().getSelectionModel().unselect(this);
12329     },
12330
12331     /**
12332      * Returns true if this node is selected
12333      * @return {Boolean}
12334      */
12335     isSelected : function(){
12336         return this.getOwnerTree().getSelectionModel().isSelected(this);
12337     },
12338
12339     /**
12340      * Expand this node.
12341      * @param {Boolean} deep (optional) True to expand all children as well
12342      * @param {Boolean} anim (optional) false to cancel the default animation
12343      * @param {Function} callback (optional) A callback to be called when
12344      * expanding this node completes (does not wait for deep expand to complete).
12345      * Called with 1 parameter, this node.
12346      */
12347     expand : function(deep, anim, callback){
12348         if(!this.expanded){
12349             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12350                 return;
12351             }
12352             if(!this.childrenRendered){
12353                 this.renderChildren();
12354             }
12355             this.expanded = true;
12356             
12357             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12358                 this.ui.animExpand(function(){
12359                     this.fireEvent("expand", this);
12360                     if(typeof callback == "function"){
12361                         callback(this);
12362                     }
12363                     if(deep === true){
12364                         this.expandChildNodes(true);
12365                     }
12366                 }.createDelegate(this));
12367                 return;
12368             }else{
12369                 this.ui.expand();
12370                 this.fireEvent("expand", this);
12371                 if(typeof callback == "function"){
12372                     callback(this);
12373                 }
12374             }
12375         }else{
12376            if(typeof callback == "function"){
12377                callback(this);
12378            }
12379         }
12380         if(deep === true){
12381             this.expandChildNodes(true);
12382         }
12383     },
12384
12385     isHiddenRoot : function(){
12386         return this.isRoot && !this.getOwnerTree().rootVisible;
12387     },
12388
12389     /**
12390      * Collapse this node.
12391      * @param {Boolean} deep (optional) True to collapse all children as well
12392      * @param {Boolean} anim (optional) false to cancel the default animation
12393      */
12394     collapse : function(deep, anim){
12395         if(this.expanded && !this.isHiddenRoot()){
12396             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12397                 return;
12398             }
12399             this.expanded = false;
12400             if((this.getOwnerTree().animate && anim !== false) || anim){
12401                 this.ui.animCollapse(function(){
12402                     this.fireEvent("collapse", this);
12403                     if(deep === true){
12404                         this.collapseChildNodes(true);
12405                     }
12406                 }.createDelegate(this));
12407                 return;
12408             }else{
12409                 this.ui.collapse();
12410                 this.fireEvent("collapse", this);
12411             }
12412         }
12413         if(deep === true){
12414             var cs = this.childNodes;
12415             for(var i = 0, len = cs.length; i < len; i++) {
12416                 cs[i].collapse(true, false);
12417             }
12418         }
12419     },
12420
12421     // private
12422     delayedExpand : function(delay){
12423         if(!this.expandProcId){
12424             this.expandProcId = this.expand.defer(delay, this);
12425         }
12426     },
12427
12428     // private
12429     cancelExpand : function(){
12430         if(this.expandProcId){
12431             clearTimeout(this.expandProcId);
12432         }
12433         this.expandProcId = false;
12434     },
12435
12436     /**
12437      * Toggles expanded/collapsed state of the node
12438      */
12439     toggle : function(){
12440         if(this.expanded){
12441             this.collapse();
12442         }else{
12443             this.expand();
12444         }
12445     },
12446
12447     /**
12448      * Ensures all parent nodes are expanded
12449      */
12450     ensureVisible : function(callback){
12451         var tree = this.getOwnerTree();
12452         tree.expandPath(this.parentNode.getPath(), false, function(){
12453             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12454             Roo.callback(callback);
12455         }.createDelegate(this));
12456     },
12457
12458     /**
12459      * Expand all child nodes
12460      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12461      */
12462     expandChildNodes : function(deep){
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++) {
12465                 cs[i].expand(deep);
12466         }
12467     },
12468
12469     /**
12470      * Collapse all child nodes
12471      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12472      */
12473     collapseChildNodes : function(deep){
12474         var cs = this.childNodes;
12475         for(var i = 0, len = cs.length; i < len; i++) {
12476                 cs[i].collapse(deep);
12477         }
12478     },
12479
12480     /**
12481      * Disables this node
12482      */
12483     disable : function(){
12484         this.disabled = true;
12485         this.unselect();
12486         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12487             this.ui.onDisableChange(this, true);
12488         }
12489         this.fireEvent("disabledchange", this, true);
12490     },
12491
12492     /**
12493      * Enables this node
12494      */
12495     enable : function(){
12496         this.disabled = false;
12497         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12498             this.ui.onDisableChange(this, false);
12499         }
12500         this.fireEvent("disabledchange", this, false);
12501     },
12502
12503     // private
12504     renderChildren : function(suppressEvent){
12505         if(suppressEvent !== false){
12506             this.fireEvent("beforechildrenrendered", this);
12507         }
12508         var cs = this.childNodes;
12509         for(var i = 0, len = cs.length; i < len; i++){
12510             cs[i].render(true);
12511         }
12512         this.childrenRendered = true;
12513     },
12514
12515     // private
12516     sort : function(fn, scope){
12517         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12518         if(this.childrenRendered){
12519             var cs = this.childNodes;
12520             for(var i = 0, len = cs.length; i < len; i++){
12521                 cs[i].render(true);
12522             }
12523         }
12524     },
12525
12526     // private
12527     render : function(bulkRender){
12528         this.ui.render(bulkRender);
12529         if(!this.rendered){
12530             this.rendered = true;
12531             if(this.expanded){
12532                 this.expanded = false;
12533                 this.expand(false, false);
12534             }
12535         }
12536     },
12537
12538     // private
12539     renderIndent : function(deep, refresh){
12540         if(refresh){
12541             this.ui.childIndent = null;
12542         }
12543         this.ui.renderIndent();
12544         if(deep === true && this.childrenRendered){
12545             var cs = this.childNodes;
12546             for(var i = 0, len = cs.length; i < len; i++){
12547                 cs[i].renderIndent(true, refresh);
12548             }
12549         }
12550     }
12551 });/*
12552  * Based on:
12553  * Ext JS Library 1.1.1
12554  * Copyright(c) 2006-2007, Ext JS, LLC.
12555  *
12556  * Originally Released Under LGPL - original licence link has changed is not relivant.
12557  *
12558  * Fork - LGPL
12559  * <script type="text/javascript">
12560  */
12561  
12562 /**
12563  * @class Roo.tree.AsyncTreeNode
12564  * @extends Roo.tree.TreeNode
12565  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12566  * @constructor
12567  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12568  */
12569  Roo.tree.AsyncTreeNode = function(config){
12570     this.loaded = false;
12571     this.loading = false;
12572     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12573     /**
12574     * @event beforeload
12575     * Fires before this node is loaded, return false to cancel
12576     * @param {Node} this This node
12577     */
12578     this.addEvents({'beforeload':true, 'load': true});
12579     /**
12580     * @event load
12581     * Fires when this node is loaded
12582     * @param {Node} this This node
12583     */
12584     /**
12585      * The loader used by this node (defaults to using the tree's defined loader)
12586      * @type TreeLoader
12587      * @property loader
12588      */
12589 };
12590 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12591     expand : function(deep, anim, callback){
12592         if(this.loading){ // if an async load is already running, waiting til it's done
12593             var timer;
12594             var f = function(){
12595                 if(!this.loading){ // done loading
12596                     clearInterval(timer);
12597                     this.expand(deep, anim, callback);
12598                 }
12599             }.createDelegate(this);
12600             timer = setInterval(f, 200);
12601             return;
12602         }
12603         if(!this.loaded){
12604             if(this.fireEvent("beforeload", this) === false){
12605                 return;
12606             }
12607             this.loading = true;
12608             this.ui.beforeLoad(this);
12609             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12610             if(loader){
12611                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12612                 return;
12613             }
12614         }
12615         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12616     },
12617     
12618     /**
12619      * Returns true if this node is currently loading
12620      * @return {Boolean}
12621      */
12622     isLoading : function(){
12623         return this.loading;  
12624     },
12625     
12626     loadComplete : function(deep, anim, callback){
12627         this.loading = false;
12628         this.loaded = true;
12629         this.ui.afterLoad(this);
12630         this.fireEvent("load", this);
12631         this.expand(deep, anim, callback);
12632     },
12633     
12634     /**
12635      * Returns true if this node has been loaded
12636      * @return {Boolean}
12637      */
12638     isLoaded : function(){
12639         return this.loaded;
12640     },
12641     
12642     hasChildNodes : function(){
12643         if(!this.isLeaf() && !this.loaded){
12644             return true;
12645         }else{
12646             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12647         }
12648     },
12649
12650     /**
12651      * Trigger a reload for this node
12652      * @param {Function} callback
12653      */
12654     reload : function(callback){
12655         this.collapse(false, false);
12656         while(this.firstChild){
12657             this.removeChild(this.firstChild);
12658         }
12659         this.childrenRendered = false;
12660         this.loaded = false;
12661         if(this.isHiddenRoot()){
12662             this.expanded = false;
12663         }
12664         this.expand(false, false, callback);
12665     }
12666 });/*
12667  * Based on:
12668  * Ext JS Library 1.1.1
12669  * Copyright(c) 2006-2007, Ext JS, LLC.
12670  *
12671  * Originally Released Under LGPL - original licence link has changed is not relivant.
12672  *
12673  * Fork - LGPL
12674  * <script type="text/javascript">
12675  */
12676  
12677 /**
12678  * @class Roo.tree.TreeNodeUI
12679  * @constructor
12680  * @param {Object} node The node to render
12681  * The TreeNode UI implementation is separate from the
12682  * tree implementation. Unless you are customizing the tree UI,
12683  * you should never have to use this directly.
12684  */
12685 Roo.tree.TreeNodeUI = function(node){
12686     this.node = node;
12687     this.rendered = false;
12688     this.animating = false;
12689     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12690 };
12691
12692 Roo.tree.TreeNodeUI.prototype = {
12693     removeChild : function(node){
12694         if(this.rendered){
12695             this.ctNode.removeChild(node.ui.getEl());
12696         }
12697     },
12698
12699     beforeLoad : function(){
12700          this.addClass("x-tree-node-loading");
12701     },
12702
12703     afterLoad : function(){
12704          this.removeClass("x-tree-node-loading");
12705     },
12706
12707     onTextChange : function(node, text, oldText){
12708         if(this.rendered){
12709             this.textNode.innerHTML = text;
12710         }
12711     },
12712
12713     onDisableChange : function(node, state){
12714         this.disabled = state;
12715         if(state){
12716             this.addClass("x-tree-node-disabled");
12717         }else{
12718             this.removeClass("x-tree-node-disabled");
12719         }
12720     },
12721
12722     onSelectedChange : function(state){
12723         if(state){
12724             this.focus();
12725             this.addClass("x-tree-selected");
12726         }else{
12727             //this.blur();
12728             this.removeClass("x-tree-selected");
12729         }
12730     },
12731
12732     onMove : function(tree, node, oldParent, newParent, index, refNode){
12733         this.childIndent = null;
12734         if(this.rendered){
12735             var targetNode = newParent.ui.getContainer();
12736             if(!targetNode){//target not rendered
12737                 this.holder = document.createElement("div");
12738                 this.holder.appendChild(this.wrap);
12739                 return;
12740             }
12741             var insertBefore = refNode ? refNode.ui.getEl() : null;
12742             if(insertBefore){
12743                 targetNode.insertBefore(this.wrap, insertBefore);
12744             }else{
12745                 targetNode.appendChild(this.wrap);
12746             }
12747             this.node.renderIndent(true);
12748         }
12749     },
12750
12751     addClass : function(cls){
12752         if(this.elNode){
12753             Roo.fly(this.elNode).addClass(cls);
12754         }
12755     },
12756
12757     removeClass : function(cls){
12758         if(this.elNode){
12759             Roo.fly(this.elNode).removeClass(cls);
12760         }
12761     },
12762
12763     remove : function(){
12764         if(this.rendered){
12765             this.holder = document.createElement("div");
12766             this.holder.appendChild(this.wrap);
12767         }
12768     },
12769
12770     fireEvent : function(){
12771         return this.node.fireEvent.apply(this.node, arguments);
12772     },
12773
12774     initEvents : function(){
12775         this.node.on("move", this.onMove, this);
12776         var E = Roo.EventManager;
12777         var a = this.anchor;
12778
12779         var el = Roo.fly(a, '_treeui');
12780
12781         if(Roo.isOpera){ // opera render bug ignores the CSS
12782             el.setStyle("text-decoration", "none");
12783         }
12784
12785         el.on("click", this.onClick, this);
12786         el.on("dblclick", this.onDblClick, this);
12787
12788         if(this.checkbox){
12789             Roo.EventManager.on(this.checkbox,
12790                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12791         }
12792
12793         el.on("contextmenu", this.onContextMenu, this);
12794
12795         var icon = Roo.fly(this.iconNode);
12796         icon.on("click", this.onClick, this);
12797         icon.on("dblclick", this.onDblClick, this);
12798         icon.on("contextmenu", this.onContextMenu, this);
12799         E.on(this.ecNode, "click", this.ecClick, this, true);
12800
12801         if(this.node.disabled){
12802             this.addClass("x-tree-node-disabled");
12803         }
12804         if(this.node.hidden){
12805             this.addClass("x-tree-node-disabled");
12806         }
12807         var ot = this.node.getOwnerTree();
12808         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12809         if(dd && (!this.node.isRoot || ot.rootVisible)){
12810             Roo.dd.Registry.register(this.elNode, {
12811                 node: this.node,
12812                 handles: this.getDDHandles(),
12813                 isHandle: false
12814             });
12815         }
12816     },
12817
12818     getDDHandles : function(){
12819         return [this.iconNode, this.textNode];
12820     },
12821
12822     hide : function(){
12823         if(this.rendered){
12824             this.wrap.style.display = "none";
12825         }
12826     },
12827
12828     show : function(){
12829         if(this.rendered){
12830             this.wrap.style.display = "";
12831         }
12832     },
12833
12834     onContextMenu : function(e){
12835         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12836             e.preventDefault();
12837             this.focus();
12838             this.fireEvent("contextmenu", this.node, e);
12839         }
12840     },
12841
12842     onClick : function(e){
12843         if(this.dropping){
12844             e.stopEvent();
12845             return;
12846         }
12847         if(this.fireEvent("beforeclick", this.node, e) !== false){
12848             if(!this.disabled && this.node.attributes.href){
12849                 this.fireEvent("click", this.node, e);
12850                 return;
12851             }
12852             e.preventDefault();
12853             if(this.disabled){
12854                 return;
12855             }
12856
12857             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12858                 this.node.toggle();
12859             }
12860
12861             this.fireEvent("click", this.node, e);
12862         }else{
12863             e.stopEvent();
12864         }
12865     },
12866
12867     onDblClick : function(e){
12868         e.preventDefault();
12869         if(this.disabled){
12870             return;
12871         }
12872         if(this.checkbox){
12873             this.toggleCheck();
12874         }
12875         if(!this.animating && this.node.hasChildNodes()){
12876             this.node.toggle();
12877         }
12878         this.fireEvent("dblclick", this.node, e);
12879     },
12880
12881     onCheckChange : function(){
12882         var checked = this.checkbox.checked;
12883         this.node.attributes.checked = checked;
12884         this.fireEvent('checkchange', this.node, checked);
12885     },
12886
12887     ecClick : function(e){
12888         if(!this.animating && this.node.hasChildNodes()){
12889             this.node.toggle();
12890         }
12891     },
12892
12893     startDrop : function(){
12894         this.dropping = true;
12895     },
12896
12897     // delayed drop so the click event doesn't get fired on a drop
12898     endDrop : function(){
12899        setTimeout(function(){
12900            this.dropping = false;
12901        }.createDelegate(this), 50);
12902     },
12903
12904     expand : function(){
12905         this.updateExpandIcon();
12906         this.ctNode.style.display = "";
12907     },
12908
12909     focus : function(){
12910         if(!this.node.preventHScroll){
12911             try{this.anchor.focus();
12912             }catch(e){}
12913         }else if(!Roo.isIE){
12914             try{
12915                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12916                 var l = noscroll.scrollLeft;
12917                 this.anchor.focus();
12918                 noscroll.scrollLeft = l;
12919             }catch(e){}
12920         }
12921     },
12922
12923     toggleCheck : function(value){
12924         var cb = this.checkbox;
12925         if(cb){
12926             cb.checked = (value === undefined ? !cb.checked : value);
12927         }
12928     },
12929
12930     blur : function(){
12931         try{
12932             this.anchor.blur();
12933         }catch(e){}
12934     },
12935
12936     animExpand : function(callback){
12937         var ct = Roo.get(this.ctNode);
12938         ct.stopFx();
12939         if(!this.node.hasChildNodes()){
12940             this.updateExpandIcon();
12941             this.ctNode.style.display = "";
12942             Roo.callback(callback);
12943             return;
12944         }
12945         this.animating = true;
12946         this.updateExpandIcon();
12947
12948         ct.slideIn('t', {
12949            callback : function(){
12950                this.animating = false;
12951                Roo.callback(callback);
12952             },
12953             scope: this,
12954             duration: this.node.ownerTree.duration || .25
12955         });
12956     },
12957
12958     highlight : function(){
12959         var tree = this.node.getOwnerTree();
12960         Roo.fly(this.wrap).highlight(
12961             tree.hlColor || "C3DAF9",
12962             {endColor: tree.hlBaseColor}
12963         );
12964     },
12965
12966     collapse : function(){
12967         this.updateExpandIcon();
12968         this.ctNode.style.display = "none";
12969     },
12970
12971     animCollapse : function(callback){
12972         var ct = Roo.get(this.ctNode);
12973         ct.enableDisplayMode('block');
12974         ct.stopFx();
12975
12976         this.animating = true;
12977         this.updateExpandIcon();
12978
12979         ct.slideOut('t', {
12980             callback : function(){
12981                this.animating = false;
12982                Roo.callback(callback);
12983             },
12984             scope: this,
12985             duration: this.node.ownerTree.duration || .25
12986         });
12987     },
12988
12989     getContainer : function(){
12990         return this.ctNode;
12991     },
12992
12993     getEl : function(){
12994         return this.wrap;
12995     },
12996
12997     appendDDGhost : function(ghostNode){
12998         ghostNode.appendChild(this.elNode.cloneNode(true));
12999     },
13000
13001     getDDRepairXY : function(){
13002         return Roo.lib.Dom.getXY(this.iconNode);
13003     },
13004
13005     onRender : function(){
13006         this.render();
13007     },
13008
13009     render : function(bulkRender){
13010         var n = this.node, a = n.attributes;
13011         var targetNode = n.parentNode ?
13012               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13013
13014         if(!this.rendered){
13015             this.rendered = true;
13016
13017             this.renderElements(n, a, targetNode, bulkRender);
13018
13019             if(a.qtip){
13020                if(this.textNode.setAttributeNS){
13021                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13022                    if(a.qtipTitle){
13023                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13024                    }
13025                }else{
13026                    this.textNode.setAttribute("ext:qtip", a.qtip);
13027                    if(a.qtipTitle){
13028                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13029                    }
13030                }
13031             }else if(a.qtipCfg){
13032                 a.qtipCfg.target = Roo.id(this.textNode);
13033                 Roo.QuickTips.register(a.qtipCfg);
13034             }
13035             this.initEvents();
13036             if(!this.node.expanded){
13037                 this.updateExpandIcon();
13038             }
13039         }else{
13040             if(bulkRender === true) {
13041                 targetNode.appendChild(this.wrap);
13042             }
13043         }
13044     },
13045
13046     renderElements : function(n, a, targetNode, bulkRender)
13047     {
13048         // add some indent caching, this helps performance when rendering a large tree
13049         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13050         var t = n.getOwnerTree();
13051         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13052         if (typeof(n.attributes.html) != 'undefined') {
13053             txt = n.attributes.html;
13054         }
13055         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13056         var cb = typeof a.checked == 'boolean';
13057         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13058         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13059             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13060             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13061             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13062             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13063             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13064              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13065                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13066             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13067             "</li>"];
13068
13069         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13070             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13071                                 n.nextSibling.ui.getEl(), buf.join(""));
13072         }else{
13073             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13074         }
13075
13076         this.elNode = this.wrap.childNodes[0];
13077         this.ctNode = this.wrap.childNodes[1];
13078         var cs = this.elNode.childNodes;
13079         this.indentNode = cs[0];
13080         this.ecNode = cs[1];
13081         this.iconNode = cs[2];
13082         var index = 3;
13083         if(cb){
13084             this.checkbox = cs[3];
13085             index++;
13086         }
13087         this.anchor = cs[index];
13088         this.textNode = cs[index].firstChild;
13089     },
13090
13091     getAnchor : function(){
13092         return this.anchor;
13093     },
13094
13095     getTextEl : function(){
13096         return this.textNode;
13097     },
13098
13099     getIconEl : function(){
13100         return this.iconNode;
13101     },
13102
13103     isChecked : function(){
13104         return this.checkbox ? this.checkbox.checked : false;
13105     },
13106
13107     updateExpandIcon : function(){
13108         if(this.rendered){
13109             var n = this.node, c1, c2;
13110             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13111             var hasChild = n.hasChildNodes();
13112             if(hasChild){
13113                 if(n.expanded){
13114                     cls += "-minus";
13115                     c1 = "x-tree-node-collapsed";
13116                     c2 = "x-tree-node-expanded";
13117                 }else{
13118                     cls += "-plus";
13119                     c1 = "x-tree-node-expanded";
13120                     c2 = "x-tree-node-collapsed";
13121                 }
13122                 if(this.wasLeaf){
13123                     this.removeClass("x-tree-node-leaf");
13124                     this.wasLeaf = false;
13125                 }
13126                 if(this.c1 != c1 || this.c2 != c2){
13127                     Roo.fly(this.elNode).replaceClass(c1, c2);
13128                     this.c1 = c1; this.c2 = c2;
13129                 }
13130             }else{
13131                 // this changes non-leafs into leafs if they have no children.
13132                 // it's not very rational behaviour..
13133                 
13134                 if(!this.wasLeaf && this.node.leaf){
13135                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13136                     delete this.c1;
13137                     delete this.c2;
13138                     this.wasLeaf = true;
13139                 }
13140             }
13141             var ecc = "x-tree-ec-icon "+cls;
13142             if(this.ecc != ecc){
13143                 this.ecNode.className = ecc;
13144                 this.ecc = ecc;
13145             }
13146         }
13147     },
13148
13149     getChildIndent : function(){
13150         if(!this.childIndent){
13151             var buf = [];
13152             var p = this.node;
13153             while(p){
13154                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13155                     if(!p.isLast()) {
13156                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13157                     } else {
13158                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13159                     }
13160                 }
13161                 p = p.parentNode;
13162             }
13163             this.childIndent = buf.join("");
13164         }
13165         return this.childIndent;
13166     },
13167
13168     renderIndent : function(){
13169         if(this.rendered){
13170             var indent = "";
13171             var p = this.node.parentNode;
13172             if(p){
13173                 indent = p.ui.getChildIndent();
13174             }
13175             if(this.indentMarkup != indent){ // don't rerender if not required
13176                 this.indentNode.innerHTML = indent;
13177                 this.indentMarkup = indent;
13178             }
13179             this.updateExpandIcon();
13180         }
13181     }
13182 };
13183
13184 Roo.tree.RootTreeNodeUI = function(){
13185     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13186 };
13187 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13188     render : function(){
13189         if(!this.rendered){
13190             var targetNode = this.node.ownerTree.innerCt.dom;
13191             this.node.expanded = true;
13192             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13193             this.wrap = this.ctNode = targetNode.firstChild;
13194         }
13195     },
13196     collapse : function(){
13197     },
13198     expand : function(){
13199     }
13200 });/*
13201  * Based on:
13202  * Ext JS Library 1.1.1
13203  * Copyright(c) 2006-2007, Ext JS, LLC.
13204  *
13205  * Originally Released Under LGPL - original licence link has changed is not relivant.
13206  *
13207  * Fork - LGPL
13208  * <script type="text/javascript">
13209  */
13210 /**
13211  * @class Roo.tree.TreeLoader
13212  * @extends Roo.util.Observable
13213  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13214  * nodes from a specified URL. The response must be a javascript Array definition
13215  * who's elements are node definition objects. eg:
13216  * <pre><code>
13217 {  success : true,
13218    data :      [
13219    
13220     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13221     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13222     ]
13223 }
13224
13225
13226 </code></pre>
13227  * <br><br>
13228  * The old style respose with just an array is still supported, but not recommended.
13229  * <br><br>
13230  *
13231  * A server request is sent, and child nodes are loaded only when a node is expanded.
13232  * The loading node's id is passed to the server under the parameter name "node" to
13233  * enable the server to produce the correct child nodes.
13234  * <br><br>
13235  * To pass extra parameters, an event handler may be attached to the "beforeload"
13236  * event, and the parameters specified in the TreeLoader's baseParams property:
13237  * <pre><code>
13238     myTreeLoader.on("beforeload", function(treeLoader, node) {
13239         this.baseParams.category = node.attributes.category;
13240     }, this);
13241     
13242 </code></pre>
13243  *
13244  * This would pass an HTTP parameter called "category" to the server containing
13245  * the value of the Node's "category" attribute.
13246  * @constructor
13247  * Creates a new Treeloader.
13248  * @param {Object} config A config object containing config properties.
13249  */
13250 Roo.tree.TreeLoader = function(config){
13251     this.baseParams = {};
13252     this.requestMethod = "POST";
13253     Roo.apply(this, config);
13254
13255     this.addEvents({
13256     
13257         /**
13258          * @event beforeload
13259          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13260          * @param {Object} This TreeLoader object.
13261          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13262          * @param {Object} callback The callback function specified in the {@link #load} call.
13263          */
13264         beforeload : true,
13265         /**
13266          * @event load
13267          * Fires when the node has been successfuly loaded.
13268          * @param {Object} This TreeLoader object.
13269          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13270          * @param {Object} response The response object containing the data from the server.
13271          */
13272         load : true,
13273         /**
13274          * @event loadexception
13275          * Fires if the network request failed.
13276          * @param {Object} This TreeLoader object.
13277          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13278          * @param {Object} response The response object containing the data from the server.
13279          */
13280         loadexception : true,
13281         /**
13282          * @event create
13283          * Fires before a node is created, enabling you to return custom Node types 
13284          * @param {Object} This TreeLoader object.
13285          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13286          */
13287         create : true
13288     });
13289
13290     Roo.tree.TreeLoader.superclass.constructor.call(this);
13291 };
13292
13293 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13294     /**
13295     * @cfg {String} dataUrl The URL from which to request a Json string which
13296     * specifies an array of node definition object representing the child nodes
13297     * to be loaded.
13298     */
13299     /**
13300     * @cfg {String} requestMethod either GET or POST
13301     * defaults to POST (due to BC)
13302     * to be loaded.
13303     */
13304     /**
13305     * @cfg {Object} baseParams (optional) An object containing properties which
13306     * specify HTTP parameters to be passed to each request for child nodes.
13307     */
13308     /**
13309     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13310     * created by this loader. If the attributes sent by the server have an attribute in this object,
13311     * they take priority.
13312     */
13313     /**
13314     * @cfg {Object} uiProviders (optional) An object containing properties which
13315     * 
13316     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13317     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13318     * <i>uiProvider</i> attribute of a returned child node is a string rather
13319     * than a reference to a TreeNodeUI implementation, this that string value
13320     * is used as a property name in the uiProviders object. You can define the provider named
13321     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13322     */
13323     uiProviders : {},
13324
13325     /**
13326     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13327     * child nodes before loading.
13328     */
13329     clearOnLoad : true,
13330
13331     /**
13332     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13333     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13334     * Grid query { data : [ .....] }
13335     */
13336     
13337     root : false,
13338      /**
13339     * @cfg {String} queryParam (optional) 
13340     * Name of the query as it will be passed on the querystring (defaults to 'node')
13341     * eg. the request will be ?node=[id]
13342     */
13343     
13344     
13345     queryParam: false,
13346     
13347     /**
13348      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13349      * This is called automatically when a node is expanded, but may be used to reload
13350      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13351      * @param {Roo.tree.TreeNode} node
13352      * @param {Function} callback
13353      */
13354     load : function(node, callback){
13355         if(this.clearOnLoad){
13356             while(node.firstChild){
13357                 node.removeChild(node.firstChild);
13358             }
13359         }
13360         if(node.attributes.children){ // preloaded json children
13361             var cs = node.attributes.children;
13362             for(var i = 0, len = cs.length; i < len; i++){
13363                 node.appendChild(this.createNode(cs[i]));
13364             }
13365             if(typeof callback == "function"){
13366                 callback();
13367             }
13368         }else if(this.dataUrl){
13369             this.requestData(node, callback);
13370         }
13371     },
13372
13373     getParams: function(node){
13374         var buf = [], bp = this.baseParams;
13375         for(var key in bp){
13376             if(typeof bp[key] != "function"){
13377                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13378             }
13379         }
13380         var n = this.queryParam === false ? 'node' : this.queryParam;
13381         buf.push(n + "=", encodeURIComponent(node.id));
13382         return buf.join("");
13383     },
13384
13385     requestData : function(node, callback){
13386         if(this.fireEvent("beforeload", this, node, callback) !== false){
13387             this.transId = Roo.Ajax.request({
13388                 method:this.requestMethod,
13389                 url: this.dataUrl||this.url,
13390                 success: this.handleResponse,
13391                 failure: this.handleFailure,
13392                 scope: this,
13393                 argument: {callback: callback, node: node},
13394                 params: this.getParams(node)
13395             });
13396         }else{
13397             // if the load is cancelled, make sure we notify
13398             // the node that we are done
13399             if(typeof callback == "function"){
13400                 callback();
13401             }
13402         }
13403     },
13404
13405     isLoading : function(){
13406         return this.transId ? true : false;
13407     },
13408
13409     abort : function(){
13410         if(this.isLoading()){
13411             Roo.Ajax.abort(this.transId);
13412         }
13413     },
13414
13415     // private
13416     createNode : function(attr)
13417     {
13418         // apply baseAttrs, nice idea Corey!
13419         if(this.baseAttrs){
13420             Roo.applyIf(attr, this.baseAttrs);
13421         }
13422         if(this.applyLoader !== false){
13423             attr.loader = this;
13424         }
13425         // uiProvider = depreciated..
13426         
13427         if(typeof(attr.uiProvider) == 'string'){
13428            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13429                 /**  eval:var:attr */ eval(attr.uiProvider);
13430         }
13431         if(typeof(this.uiProviders['default']) != 'undefined') {
13432             attr.uiProvider = this.uiProviders['default'];
13433         }
13434         
13435         this.fireEvent('create', this, attr);
13436         
13437         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13438         return(attr.leaf ?
13439                         new Roo.tree.TreeNode(attr) :
13440                         new Roo.tree.AsyncTreeNode(attr));
13441     },
13442
13443     processResponse : function(response, node, callback)
13444     {
13445         var json = response.responseText;
13446         try {
13447             
13448             var o = Roo.decode(json);
13449             
13450             if (this.root === false && typeof(o.success) != undefined) {
13451                 this.root = 'data'; // the default behaviour for list like data..
13452                 }
13453                 
13454             if (this.root !== false &&  !o.success) {
13455                 // it's a failure condition.
13456                 var a = response.argument;
13457                 this.fireEvent("loadexception", this, a.node, response);
13458                 Roo.log("Load failed - should have a handler really");
13459                 return;
13460             }
13461             
13462             
13463             
13464             if (this.root !== false) {
13465                  o = o[this.root];
13466             }
13467             
13468             for(var i = 0, len = o.length; i < len; i++){
13469                 var n = this.createNode(o[i]);
13470                 if(n){
13471                     node.appendChild(n);
13472                 }
13473             }
13474             if(typeof callback == "function"){
13475                 callback(this, node);
13476             }
13477         }catch(e){
13478             this.handleFailure(response);
13479         }
13480     },
13481
13482     handleResponse : function(response){
13483         this.transId = false;
13484         var a = response.argument;
13485         this.processResponse(response, a.node, a.callback);
13486         this.fireEvent("load", this, a.node, response);
13487     },
13488
13489     handleFailure : function(response)
13490     {
13491         // should handle failure better..
13492         this.transId = false;
13493         var a = response.argument;
13494         this.fireEvent("loadexception", this, a.node, response);
13495         if(typeof a.callback == "function"){
13496             a.callback(this, a.node);
13497         }
13498     }
13499 });/*
13500  * Based on:
13501  * Ext JS Library 1.1.1
13502  * Copyright(c) 2006-2007, Ext JS, LLC.
13503  *
13504  * Originally Released Under LGPL - original licence link has changed is not relivant.
13505  *
13506  * Fork - LGPL
13507  * <script type="text/javascript">
13508  */
13509
13510 /**
13511 * @class Roo.tree.TreeFilter
13512 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13513 * @param {TreePanel} tree
13514 * @param {Object} config (optional)
13515  */
13516 Roo.tree.TreeFilter = function(tree, config){
13517     this.tree = tree;
13518     this.filtered = {};
13519     Roo.apply(this, config);
13520 };
13521
13522 Roo.tree.TreeFilter.prototype = {
13523     clearBlank:false,
13524     reverse:false,
13525     autoClear:false,
13526     remove:false,
13527
13528      /**
13529      * Filter the data by a specific attribute.
13530      * @param {String/RegExp} value Either string that the attribute value
13531      * should start with or a RegExp to test against the attribute
13532      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13533      * @param {TreeNode} startNode (optional) The node to start the filter at.
13534      */
13535     filter : function(value, attr, startNode){
13536         attr = attr || "text";
13537         var f;
13538         if(typeof value == "string"){
13539             var vlen = value.length;
13540             // auto clear empty filter
13541             if(vlen == 0 && this.clearBlank){
13542                 this.clear();
13543                 return;
13544             }
13545             value = value.toLowerCase();
13546             f = function(n){
13547                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13548             };
13549         }else if(value.exec){ // regex?
13550             f = function(n){
13551                 return value.test(n.attributes[attr]);
13552             };
13553         }else{
13554             throw 'Illegal filter type, must be string or regex';
13555         }
13556         this.filterBy(f, null, startNode);
13557         },
13558
13559     /**
13560      * Filter by a function. The passed function will be called with each
13561      * node in the tree (or from the startNode). If the function returns true, the node is kept
13562      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13563      * @param {Function} fn The filter function
13564      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13565      */
13566     filterBy : function(fn, scope, startNode){
13567         startNode = startNode || this.tree.root;
13568         if(this.autoClear){
13569             this.clear();
13570         }
13571         var af = this.filtered, rv = this.reverse;
13572         var f = function(n){
13573             if(n == startNode){
13574                 return true;
13575             }
13576             if(af[n.id]){
13577                 return false;
13578             }
13579             var m = fn.call(scope || n, n);
13580             if(!m || rv){
13581                 af[n.id] = n;
13582                 n.ui.hide();
13583                 return false;
13584             }
13585             return true;
13586         };
13587         startNode.cascade(f);
13588         if(this.remove){
13589            for(var id in af){
13590                if(typeof id != "function"){
13591                    var n = af[id];
13592                    if(n && n.parentNode){
13593                        n.parentNode.removeChild(n);
13594                    }
13595                }
13596            }
13597         }
13598     },
13599
13600     /**
13601      * Clears the current filter. Note: with the "remove" option
13602      * set a filter cannot be cleared.
13603      */
13604     clear : function(){
13605         var t = this.tree;
13606         var af = this.filtered;
13607         for(var id in af){
13608             if(typeof id != "function"){
13609                 var n = af[id];
13610                 if(n){
13611                     n.ui.show();
13612                 }
13613             }
13614         }
13615         this.filtered = {};
13616     }
13617 };
13618 /*
13619  * Based on:
13620  * Ext JS Library 1.1.1
13621  * Copyright(c) 2006-2007, Ext JS, LLC.
13622  *
13623  * Originally Released Under LGPL - original licence link has changed is not relivant.
13624  *
13625  * Fork - LGPL
13626  * <script type="text/javascript">
13627  */
13628  
13629
13630 /**
13631  * @class Roo.tree.TreeSorter
13632  * Provides sorting of nodes in a TreePanel
13633  * 
13634  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13635  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13636  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13637  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13638  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13639  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13640  * @constructor
13641  * @param {TreePanel} tree
13642  * @param {Object} config
13643  */
13644 Roo.tree.TreeSorter = function(tree, config){
13645     Roo.apply(this, config);
13646     tree.on("beforechildrenrendered", this.doSort, this);
13647     tree.on("append", this.updateSort, this);
13648     tree.on("insert", this.updateSort, this);
13649     
13650     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13651     var p = this.property || "text";
13652     var sortType = this.sortType;
13653     var fs = this.folderSort;
13654     var cs = this.caseSensitive === true;
13655     var leafAttr = this.leafAttr || 'leaf';
13656
13657     this.sortFn = function(n1, n2){
13658         if(fs){
13659             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13660                 return 1;
13661             }
13662             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13663                 return -1;
13664             }
13665         }
13666         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13667         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13668         if(v1 < v2){
13669                         return dsc ? +1 : -1;
13670                 }else if(v1 > v2){
13671                         return dsc ? -1 : +1;
13672         }else{
13673                 return 0;
13674         }
13675     };
13676 };
13677
13678 Roo.tree.TreeSorter.prototype = {
13679     doSort : function(node){
13680         node.sort(this.sortFn);
13681     },
13682     
13683     compareNodes : function(n1, n2){
13684         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13685     },
13686     
13687     updateSort : function(tree, node){
13688         if(node.childrenRendered){
13689             this.doSort.defer(1, this, [node]);
13690         }
13691     }
13692 };/*
13693  * Based on:
13694  * Ext JS Library 1.1.1
13695  * Copyright(c) 2006-2007, Ext JS, LLC.
13696  *
13697  * Originally Released Under LGPL - original licence link has changed is not relivant.
13698  *
13699  * Fork - LGPL
13700  * <script type="text/javascript">
13701  */
13702
13703 if(Roo.dd.DropZone){
13704     
13705 Roo.tree.TreeDropZone = function(tree, config){
13706     this.allowParentInsert = false;
13707     this.allowContainerDrop = false;
13708     this.appendOnly = false;
13709     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13710     this.tree = tree;
13711     this.lastInsertClass = "x-tree-no-status";
13712     this.dragOverData = {};
13713 };
13714
13715 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13716     ddGroup : "TreeDD",
13717     scroll:  true,
13718     
13719     expandDelay : 1000,
13720     
13721     expandNode : function(node){
13722         if(node.hasChildNodes() && !node.isExpanded()){
13723             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13724         }
13725     },
13726     
13727     queueExpand : function(node){
13728         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13729     },
13730     
13731     cancelExpand : function(){
13732         if(this.expandProcId){
13733             clearTimeout(this.expandProcId);
13734             this.expandProcId = false;
13735         }
13736     },
13737     
13738     isValidDropPoint : function(n, pt, dd, e, data){
13739         if(!n || !data){ return false; }
13740         var targetNode = n.node;
13741         var dropNode = data.node;
13742         // default drop rules
13743         if(!(targetNode && targetNode.isTarget && pt)){
13744             return false;
13745         }
13746         if(pt == "append" && targetNode.allowChildren === false){
13747             return false;
13748         }
13749         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13750             return false;
13751         }
13752         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13753             return false;
13754         }
13755         // reuse the object
13756         var overEvent = this.dragOverData;
13757         overEvent.tree = this.tree;
13758         overEvent.target = targetNode;
13759         overEvent.data = data;
13760         overEvent.point = pt;
13761         overEvent.source = dd;
13762         overEvent.rawEvent = e;
13763         overEvent.dropNode = dropNode;
13764         overEvent.cancel = false;  
13765         var result = this.tree.fireEvent("nodedragover", overEvent);
13766         return overEvent.cancel === false && result !== false;
13767     },
13768     
13769     getDropPoint : function(e, n, dd)
13770     {
13771         var tn = n.node;
13772         if(tn.isRoot){
13773             return tn.allowChildren !== false ? "append" : false; // always append for root
13774         }
13775         var dragEl = n.ddel;
13776         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13777         var y = Roo.lib.Event.getPageY(e);
13778         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13779         
13780         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13781         var noAppend = tn.allowChildren === false;
13782         if(this.appendOnly || tn.parentNode.allowChildren === false){
13783             return noAppend ? false : "append";
13784         }
13785         var noBelow = false;
13786         if(!this.allowParentInsert){
13787             noBelow = tn.hasChildNodes() && tn.isExpanded();
13788         }
13789         var q = (b - t) / (noAppend ? 2 : 3);
13790         if(y >= t && y < (t + q)){
13791             return "above";
13792         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13793             return "below";
13794         }else{
13795             return "append";
13796         }
13797     },
13798     
13799     onNodeEnter : function(n, dd, e, data)
13800     {
13801         this.cancelExpand();
13802     },
13803     
13804     onNodeOver : function(n, dd, e, data)
13805     {
13806        
13807         var pt = this.getDropPoint(e, n, dd);
13808         var node = n.node;
13809         
13810         // auto node expand check
13811         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13812             this.queueExpand(node);
13813         }else if(pt != "append"){
13814             this.cancelExpand();
13815         }
13816         
13817         // set the insert point style on the target node
13818         var returnCls = this.dropNotAllowed;
13819         if(this.isValidDropPoint(n, pt, dd, e, data)){
13820            if(pt){
13821                var el = n.ddel;
13822                var cls;
13823                if(pt == "above"){
13824                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13825                    cls = "x-tree-drag-insert-above";
13826                }else if(pt == "below"){
13827                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13828                    cls = "x-tree-drag-insert-below";
13829                }else{
13830                    returnCls = "x-tree-drop-ok-append";
13831                    cls = "x-tree-drag-append";
13832                }
13833                if(this.lastInsertClass != cls){
13834                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13835                    this.lastInsertClass = cls;
13836                }
13837            }
13838        }
13839        return returnCls;
13840     },
13841     
13842     onNodeOut : function(n, dd, e, data){
13843         
13844         this.cancelExpand();
13845         this.removeDropIndicators(n);
13846     },
13847     
13848     onNodeDrop : function(n, dd, e, data){
13849         var point = this.getDropPoint(e, n, dd);
13850         var targetNode = n.node;
13851         targetNode.ui.startDrop();
13852         if(!this.isValidDropPoint(n, point, dd, e, data)){
13853             targetNode.ui.endDrop();
13854             return false;
13855         }
13856         // first try to find the drop node
13857         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13858         var dropEvent = {
13859             tree : this.tree,
13860             target: targetNode,
13861             data: data,
13862             point: point,
13863             source: dd,
13864             rawEvent: e,
13865             dropNode: dropNode,
13866             cancel: !dropNode   
13867         };
13868         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13869         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13870             targetNode.ui.endDrop();
13871             return false;
13872         }
13873         // allow target changing
13874         targetNode = dropEvent.target;
13875         if(point == "append" && !targetNode.isExpanded()){
13876             targetNode.expand(false, null, function(){
13877                 this.completeDrop(dropEvent);
13878             }.createDelegate(this));
13879         }else{
13880             this.completeDrop(dropEvent);
13881         }
13882         return true;
13883     },
13884     
13885     completeDrop : function(de){
13886         var ns = de.dropNode, p = de.point, t = de.target;
13887         if(!(ns instanceof Array)){
13888             ns = [ns];
13889         }
13890         var n;
13891         for(var i = 0, len = ns.length; i < len; i++){
13892             n = ns[i];
13893             if(p == "above"){
13894                 t.parentNode.insertBefore(n, t);
13895             }else if(p == "below"){
13896                 t.parentNode.insertBefore(n, t.nextSibling);
13897             }else{
13898                 t.appendChild(n);
13899             }
13900         }
13901         n.ui.focus();
13902         if(this.tree.hlDrop){
13903             n.ui.highlight();
13904         }
13905         t.ui.endDrop();
13906         this.tree.fireEvent("nodedrop", de);
13907     },
13908     
13909     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13910         if(this.tree.hlDrop){
13911             dropNode.ui.focus();
13912             dropNode.ui.highlight();
13913         }
13914         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13915     },
13916     
13917     getTree : function(){
13918         return this.tree;
13919     },
13920     
13921     removeDropIndicators : function(n){
13922         if(n && n.ddel){
13923             var el = n.ddel;
13924             Roo.fly(el).removeClass([
13925                     "x-tree-drag-insert-above",
13926                     "x-tree-drag-insert-below",
13927                     "x-tree-drag-append"]);
13928             this.lastInsertClass = "_noclass";
13929         }
13930     },
13931     
13932     beforeDragDrop : function(target, e, id){
13933         this.cancelExpand();
13934         return true;
13935     },
13936     
13937     afterRepair : function(data){
13938         if(data && Roo.enableFx){
13939             data.node.ui.highlight();
13940         }
13941         this.hideProxy();
13942     } 
13943     
13944 });
13945
13946 }
13947 /*
13948  * Based on:
13949  * Ext JS Library 1.1.1
13950  * Copyright(c) 2006-2007, Ext JS, LLC.
13951  *
13952  * Originally Released Under LGPL - original licence link has changed is not relivant.
13953  *
13954  * Fork - LGPL
13955  * <script type="text/javascript">
13956  */
13957  
13958
13959 if(Roo.dd.DragZone){
13960 Roo.tree.TreeDragZone = function(tree, config){
13961     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13962     this.tree = tree;
13963 };
13964
13965 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13966     ddGroup : "TreeDD",
13967    
13968     onBeforeDrag : function(data, e){
13969         var n = data.node;
13970         return n && n.draggable && !n.disabled;
13971     },
13972      
13973     
13974     onInitDrag : function(e){
13975         var data = this.dragData;
13976         this.tree.getSelectionModel().select(data.node);
13977         this.proxy.update("");
13978         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13979         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13980     },
13981     
13982     getRepairXY : function(e, data){
13983         return data.node.ui.getDDRepairXY();
13984     },
13985     
13986     onEndDrag : function(data, e){
13987         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13988         
13989         
13990     },
13991     
13992     onValidDrop : function(dd, e, id){
13993         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13994         this.hideProxy();
13995     },
13996     
13997     beforeInvalidDrop : function(e, id){
13998         // this scrolls the original position back into view
13999         var sm = this.tree.getSelectionModel();
14000         sm.clearSelections();
14001         sm.select(this.dragData.node);
14002     }
14003 });
14004 }/*
14005  * Based on:
14006  * Ext JS Library 1.1.1
14007  * Copyright(c) 2006-2007, Ext JS, LLC.
14008  *
14009  * Originally Released Under LGPL - original licence link has changed is not relivant.
14010  *
14011  * Fork - LGPL
14012  * <script type="text/javascript">
14013  */
14014 /**
14015  * @class Roo.tree.TreeEditor
14016  * @extends Roo.Editor
14017  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14018  * as the editor field.
14019  * @constructor
14020  * @param {Object} config (used to be the tree panel.)
14021  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14022  * 
14023  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14024  * @cfg {Roo.form.TextField} field [required] The field configuration
14025  *
14026  * 
14027  */
14028 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14029     var tree = config;
14030     var field;
14031     if (oldconfig) { // old style..
14032         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14033     } else {
14034         // new style..
14035         tree = config.tree;
14036         config.field = config.field  || {};
14037         config.field.xtype = 'TextField';
14038         field = Roo.factory(config.field, Roo.form);
14039     }
14040     config = config || {};
14041     
14042     
14043     this.addEvents({
14044         /**
14045          * @event beforenodeedit
14046          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14047          * false from the handler of this event.
14048          * @param {Editor} this
14049          * @param {Roo.tree.Node} node 
14050          */
14051         "beforenodeedit" : true
14052     });
14053     
14054     //Roo.log(config);
14055     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14056
14057     this.tree = tree;
14058
14059     tree.on('beforeclick', this.beforeNodeClick, this);
14060     tree.getTreeEl().on('mousedown', this.hide, this);
14061     this.on('complete', this.updateNode, this);
14062     this.on('beforestartedit', this.fitToTree, this);
14063     this.on('startedit', this.bindScroll, this, {delay:10});
14064     this.on('specialkey', this.onSpecialKey, this);
14065 };
14066
14067 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14068     /**
14069      * @cfg {String} alignment
14070      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14071      */
14072     alignment: "l-l",
14073     // inherit
14074     autoSize: false,
14075     /**
14076      * @cfg {Boolean} hideEl
14077      * True to hide the bound element while the editor is displayed (defaults to false)
14078      */
14079     hideEl : false,
14080     /**
14081      * @cfg {String} cls
14082      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14083      */
14084     cls: "x-small-editor x-tree-editor",
14085     /**
14086      * @cfg {Boolean} shim
14087      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14088      */
14089     shim:false,
14090     // inherit
14091     shadow:"frame",
14092     /**
14093      * @cfg {Number} maxWidth
14094      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14095      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14096      * scroll and client offsets into account prior to each edit.
14097      */
14098     maxWidth: 250,
14099
14100     editDelay : 350,
14101
14102     // private
14103     fitToTree : function(ed, el){
14104         var td = this.tree.getTreeEl().dom, nd = el.dom;
14105         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14106             td.scrollLeft = nd.offsetLeft;
14107         }
14108         var w = Math.min(
14109                 this.maxWidth,
14110                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14111         this.setSize(w, '');
14112         
14113         return this.fireEvent('beforenodeedit', this, this.editNode);
14114         
14115     },
14116
14117     // private
14118     triggerEdit : function(node){
14119         this.completeEdit();
14120         this.editNode = node;
14121         this.startEdit(node.ui.textNode, node.text);
14122     },
14123
14124     // private
14125     bindScroll : function(){
14126         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14127     },
14128
14129     // private
14130     beforeNodeClick : function(node, e){
14131         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14132         this.lastClick = new Date();
14133         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14134             e.stopEvent();
14135             this.triggerEdit(node);
14136             return false;
14137         }
14138         return true;
14139     },
14140
14141     // private
14142     updateNode : function(ed, value){
14143         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14144         this.editNode.setText(value);
14145     },
14146
14147     // private
14148     onHide : function(){
14149         Roo.tree.TreeEditor.superclass.onHide.call(this);
14150         if(this.editNode){
14151             this.editNode.ui.focus();
14152         }
14153     },
14154
14155     // private
14156     onSpecialKey : function(field, e){
14157         var k = e.getKey();
14158         if(k == e.ESC){
14159             e.stopEvent();
14160             this.cancelEdit();
14161         }else if(k == e.ENTER && !e.hasModifier()){
14162             e.stopEvent();
14163             this.completeEdit();
14164         }
14165     }
14166 });//<Script type="text/javascript">
14167 /*
14168  * Based on:
14169  * Ext JS Library 1.1.1
14170  * Copyright(c) 2006-2007, Ext JS, LLC.
14171  *
14172  * Originally Released Under LGPL - original licence link has changed is not relivant.
14173  *
14174  * Fork - LGPL
14175  * <script type="text/javascript">
14176  */
14177  
14178 /**
14179  * Not documented??? - probably should be...
14180  */
14181
14182 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14183     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14184     
14185     renderElements : function(n, a, targetNode, bulkRender){
14186         //consel.log("renderElements?");
14187         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14188
14189         var t = n.getOwnerTree();
14190         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14191         
14192         var cols = t.columns;
14193         var bw = t.borderWidth;
14194         var c = cols[0];
14195         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14196          var cb = typeof a.checked == "boolean";
14197         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14198         var colcls = 'x-t-' + tid + '-c0';
14199         var buf = [
14200             '<li class="x-tree-node">',
14201             
14202                 
14203                 '<div class="x-tree-node-el ', a.cls,'">',
14204                     // extran...
14205                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14206                 
14207                 
14208                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14209                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14210                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14211                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14212                            (a.iconCls ? ' '+a.iconCls : ''),
14213                            '" unselectable="on" />',
14214                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14215                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14216                              
14217                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14218                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14219                             '<span unselectable="on" qtip="' + tx + '">',
14220                              tx,
14221                              '</span></a>' ,
14222                     '</div>',
14223                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14224                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14225                  ];
14226         for(var i = 1, len = cols.length; i < len; i++){
14227             c = cols[i];
14228             colcls = 'x-t-' + tid + '-c' +i;
14229             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14230             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14231                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14232                       "</div>");
14233          }
14234          
14235          buf.push(
14236             '</a>',
14237             '<div class="x-clear"></div></div>',
14238             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14239             "</li>");
14240         
14241         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14242             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14243                                 n.nextSibling.ui.getEl(), buf.join(""));
14244         }else{
14245             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14246         }
14247         var el = this.wrap.firstChild;
14248         this.elRow = el;
14249         this.elNode = el.firstChild;
14250         this.ranchor = el.childNodes[1];
14251         this.ctNode = this.wrap.childNodes[1];
14252         var cs = el.firstChild.childNodes;
14253         this.indentNode = cs[0];
14254         this.ecNode = cs[1];
14255         this.iconNode = cs[2];
14256         var index = 3;
14257         if(cb){
14258             this.checkbox = cs[3];
14259             index++;
14260         }
14261         this.anchor = cs[index];
14262         
14263         this.textNode = cs[index].firstChild;
14264         
14265         //el.on("click", this.onClick, this);
14266         //el.on("dblclick", this.onDblClick, this);
14267         
14268         
14269        // console.log(this);
14270     },
14271     initEvents : function(){
14272         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14273         
14274             
14275         var a = this.ranchor;
14276
14277         var el = Roo.get(a);
14278
14279         if(Roo.isOpera){ // opera render bug ignores the CSS
14280             el.setStyle("text-decoration", "none");
14281         }
14282
14283         el.on("click", this.onClick, this);
14284         el.on("dblclick", this.onDblClick, this);
14285         el.on("contextmenu", this.onContextMenu, this);
14286         
14287     },
14288     
14289     /*onSelectedChange : function(state){
14290         if(state){
14291             this.focus();
14292             this.addClass("x-tree-selected");
14293         }else{
14294             //this.blur();
14295             this.removeClass("x-tree-selected");
14296         }
14297     },*/
14298     addClass : function(cls){
14299         if(this.elRow){
14300             Roo.fly(this.elRow).addClass(cls);
14301         }
14302         
14303     },
14304     
14305     
14306     removeClass : function(cls){
14307         if(this.elRow){
14308             Roo.fly(this.elRow).removeClass(cls);
14309         }
14310     }
14311
14312     
14313     
14314 });//<Script type="text/javascript">
14315
14316 /*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326  
14327
14328 /**
14329  * @class Roo.tree.ColumnTree
14330  * @extends Roo.tree.TreePanel
14331  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14332  * @cfg {int} borderWidth  compined right/left border allowance
14333  * @constructor
14334  * @param {String/HTMLElement/Element} el The container element
14335  * @param {Object} config
14336  */
14337 Roo.tree.ColumnTree =  function(el, config)
14338 {
14339    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14340    this.addEvents({
14341         /**
14342         * @event resize
14343         * Fire this event on a container when it resizes
14344         * @param {int} w Width
14345         * @param {int} h Height
14346         */
14347        "resize" : true
14348     });
14349     this.on('resize', this.onResize, this);
14350 };
14351
14352 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14353     //lines:false,
14354     
14355     
14356     borderWidth: Roo.isBorderBox ? 0 : 2, 
14357     headEls : false,
14358     
14359     render : function(){
14360         // add the header.....
14361        
14362         Roo.tree.ColumnTree.superclass.render.apply(this);
14363         
14364         this.el.addClass('x-column-tree');
14365         
14366         this.headers = this.el.createChild(
14367             {cls:'x-tree-headers'},this.innerCt.dom);
14368    
14369         var cols = this.columns, c;
14370         var totalWidth = 0;
14371         this.headEls = [];
14372         var  len = cols.length;
14373         for(var i = 0; i < len; i++){
14374              c = cols[i];
14375              totalWidth += c.width;
14376             this.headEls.push(this.headers.createChild({
14377                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14378                  cn: {
14379                      cls:'x-tree-hd-text',
14380                      html: c.header
14381                  },
14382                  style:'width:'+(c.width-this.borderWidth)+'px;'
14383              }));
14384         }
14385         this.headers.createChild({cls:'x-clear'});
14386         // prevent floats from wrapping when clipped
14387         this.headers.setWidth(totalWidth);
14388         //this.innerCt.setWidth(totalWidth);
14389         this.innerCt.setStyle({ overflow: 'auto' });
14390         this.onResize(this.width, this.height);
14391              
14392         
14393     },
14394     onResize : function(w,h)
14395     {
14396         this.height = h;
14397         this.width = w;
14398         // resize cols..
14399         this.innerCt.setWidth(this.width);
14400         this.innerCt.setHeight(this.height-20);
14401         
14402         // headers...
14403         var cols = this.columns, c;
14404         var totalWidth = 0;
14405         var expEl = false;
14406         var len = cols.length;
14407         for(var i = 0; i < len; i++){
14408             c = cols[i];
14409             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14410                 // it's the expander..
14411                 expEl  = this.headEls[i];
14412                 continue;
14413             }
14414             totalWidth += c.width;
14415             
14416         }
14417         if (expEl) {
14418             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14419         }
14420         this.headers.setWidth(w-20);
14421
14422         
14423         
14424         
14425     }
14426 });
14427 /*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437  
14438 /**
14439  * @class Roo.menu.Menu
14440  * @extends Roo.util.Observable
14441  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14442  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14443  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14444  * @constructor
14445  * Creates a new Menu
14446  * @param {Object} config Configuration options
14447  */
14448 Roo.menu.Menu = function(config){
14449     
14450     Roo.menu.Menu.superclass.constructor.call(this, config);
14451     
14452     this.id = this.id || Roo.id();
14453     this.addEvents({
14454         /**
14455          * @event beforeshow
14456          * Fires before this menu is displayed
14457          * @param {Roo.menu.Menu} this
14458          */
14459         beforeshow : true,
14460         /**
14461          * @event beforehide
14462          * Fires before this menu is hidden
14463          * @param {Roo.menu.Menu} this
14464          */
14465         beforehide : true,
14466         /**
14467          * @event show
14468          * Fires after this menu is displayed
14469          * @param {Roo.menu.Menu} this
14470          */
14471         show : true,
14472         /**
14473          * @event hide
14474          * Fires after this menu is hidden
14475          * @param {Roo.menu.Menu} this
14476          */
14477         hide : true,
14478         /**
14479          * @event click
14480          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14481          * @param {Roo.menu.Menu} this
14482          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14483          * @param {Roo.EventObject} e
14484          */
14485         click : true,
14486         /**
14487          * @event mouseover
14488          * Fires when the mouse is hovering over this menu
14489          * @param {Roo.menu.Menu} this
14490          * @param {Roo.EventObject} e
14491          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14492          */
14493         mouseover : true,
14494         /**
14495          * @event mouseout
14496          * Fires when the mouse exits this menu
14497          * @param {Roo.menu.Menu} this
14498          * @param {Roo.EventObject} e
14499          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14500          */
14501         mouseout : true,
14502         /**
14503          * @event itemclick
14504          * Fires when a menu item contained in this menu is clicked
14505          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14506          * @param {Roo.EventObject} e
14507          */
14508         itemclick: true
14509     });
14510     if (this.registerMenu) {
14511         Roo.menu.MenuMgr.register(this);
14512     }
14513     
14514     var mis = this.items;
14515     this.items = new Roo.util.MixedCollection();
14516     if(mis){
14517         this.add.apply(this, mis);
14518     }
14519 };
14520
14521 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14522     /**
14523      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14524      */
14525     minWidth : 120,
14526     /**
14527      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14528      * for bottom-right shadow (defaults to "sides")
14529      */
14530     shadow : "sides",
14531     /**
14532      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14533      * this menu (defaults to "tl-tr?")
14534      */
14535     subMenuAlign : "tl-tr?",
14536     /**
14537      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14538      * relative to its element of origin (defaults to "tl-bl?")
14539      */
14540     defaultAlign : "tl-bl?",
14541     /**
14542      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14543      */
14544     allowOtherMenus : false,
14545     /**
14546      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14547      */
14548     registerMenu : true,
14549
14550     hidden:true,
14551
14552     // private
14553     render : function(){
14554         if(this.el){
14555             return;
14556         }
14557         var el = this.el = new Roo.Layer({
14558             cls: "x-menu",
14559             shadow:this.shadow,
14560             constrain: false,
14561             parentEl: this.parentEl || document.body,
14562             zindex:15000
14563         });
14564
14565         this.keyNav = new Roo.menu.MenuNav(this);
14566
14567         if(this.plain){
14568             el.addClass("x-menu-plain");
14569         }
14570         if(this.cls){
14571             el.addClass(this.cls);
14572         }
14573         // generic focus element
14574         this.focusEl = el.createChild({
14575             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14576         });
14577         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14578         //disabling touch- as it's causing issues ..
14579         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14580         ul.on('click'   , this.onClick, this);
14581         
14582         
14583         ul.on("mouseover", this.onMouseOver, this);
14584         ul.on("mouseout", this.onMouseOut, this);
14585         this.items.each(function(item){
14586             if (item.hidden) {
14587                 return;
14588             }
14589             
14590             var li = document.createElement("li");
14591             li.className = "x-menu-list-item";
14592             ul.dom.appendChild(li);
14593             item.render(li, this);
14594         }, this);
14595         this.ul = ul;
14596         this.autoWidth();
14597     },
14598
14599     // private
14600     autoWidth : function(){
14601         var el = this.el, ul = this.ul;
14602         if(!el){
14603             return;
14604         }
14605         var w = this.width;
14606         if(w){
14607             el.setWidth(w);
14608         }else if(Roo.isIE){
14609             el.setWidth(this.minWidth);
14610             var t = el.dom.offsetWidth; // force recalc
14611             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14612         }
14613     },
14614
14615     // private
14616     delayAutoWidth : function(){
14617         if(this.rendered){
14618             if(!this.awTask){
14619                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14620             }
14621             this.awTask.delay(20);
14622         }
14623     },
14624
14625     // private
14626     findTargetItem : function(e){
14627         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14628         if(t && t.menuItemId){
14629             return this.items.get(t.menuItemId);
14630         }
14631     },
14632
14633     // private
14634     onClick : function(e){
14635         Roo.log("menu.onClick");
14636         var t = this.findTargetItem(e);
14637         if(!t){
14638             return;
14639         }
14640         Roo.log(e);
14641         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14642             if(t == this.activeItem && t.shouldDeactivate(e)){
14643                 this.activeItem.deactivate();
14644                 delete this.activeItem;
14645                 return;
14646             }
14647             if(t.canActivate){
14648                 this.setActiveItem(t, true);
14649             }
14650             return;
14651             
14652             
14653         }
14654         
14655         t.onClick(e);
14656         this.fireEvent("click", this, t, e);
14657     },
14658
14659     // private
14660     setActiveItem : function(item, autoExpand){
14661         if(item != this.activeItem){
14662             if(this.activeItem){
14663                 this.activeItem.deactivate();
14664             }
14665             this.activeItem = item;
14666             item.activate(autoExpand);
14667         }else if(autoExpand){
14668             item.expandMenu();
14669         }
14670     },
14671
14672     // private
14673     tryActivate : function(start, step){
14674         var items = this.items;
14675         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14676             var item = items.get(i);
14677             if(!item.disabled && item.canActivate){
14678                 this.setActiveItem(item, false);
14679                 return item;
14680             }
14681         }
14682         return false;
14683     },
14684
14685     // private
14686     onMouseOver : function(e){
14687         var t;
14688         if(t = this.findTargetItem(e)){
14689             if(t.canActivate && !t.disabled){
14690                 this.setActiveItem(t, true);
14691             }
14692         }
14693         this.fireEvent("mouseover", this, e, t);
14694     },
14695
14696     // private
14697     onMouseOut : function(e){
14698         var t;
14699         if(t = this.findTargetItem(e)){
14700             if(t == this.activeItem && t.shouldDeactivate(e)){
14701                 this.activeItem.deactivate();
14702                 delete this.activeItem;
14703             }
14704         }
14705         this.fireEvent("mouseout", this, e, t);
14706     },
14707
14708     /**
14709      * Read-only.  Returns true if the menu is currently displayed, else false.
14710      * @type Boolean
14711      */
14712     isVisible : function(){
14713         return this.el && !this.hidden;
14714     },
14715
14716     /**
14717      * Displays this menu relative to another element
14718      * @param {String/HTMLElement/Roo.Element} element The element to align to
14719      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14720      * the element (defaults to this.defaultAlign)
14721      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14722      */
14723     show : function(el, pos, parentMenu){
14724         this.parentMenu = parentMenu;
14725         if(!this.el){
14726             this.render();
14727         }
14728         this.fireEvent("beforeshow", this);
14729         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14730     },
14731
14732     /**
14733      * Displays this menu at a specific xy position
14734      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14735      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14736      */
14737     showAt : function(xy, parentMenu, /* private: */_e){
14738         this.parentMenu = parentMenu;
14739         if(!this.el){
14740             this.render();
14741         }
14742         if(_e !== false){
14743             this.fireEvent("beforeshow", this);
14744             xy = this.el.adjustForConstraints(xy);
14745         }
14746         this.el.setXY(xy);
14747         this.el.show();
14748         this.hidden = false;
14749         this.focus();
14750         this.fireEvent("show", this);
14751     },
14752
14753     focus : function(){
14754         if(!this.hidden){
14755             this.doFocus.defer(50, this);
14756         }
14757     },
14758
14759     doFocus : function(){
14760         if(!this.hidden){
14761             this.focusEl.focus();
14762         }
14763     },
14764
14765     /**
14766      * Hides this menu and optionally all parent menus
14767      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14768      */
14769     hide : function(deep){
14770         if(this.el && this.isVisible()){
14771             this.fireEvent("beforehide", this);
14772             if(this.activeItem){
14773                 this.activeItem.deactivate();
14774                 this.activeItem = null;
14775             }
14776             this.el.hide();
14777             this.hidden = true;
14778             this.fireEvent("hide", this);
14779         }
14780         if(deep === true && this.parentMenu){
14781             this.parentMenu.hide(true);
14782         }
14783     },
14784
14785     /**
14786      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14787      * Any of the following are valid:
14788      * <ul>
14789      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14790      * <li>An HTMLElement object which will be converted to a menu item</li>
14791      * <li>A menu item config object that will be created as a new menu item</li>
14792      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14793      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14794      * </ul>
14795      * Usage:
14796      * <pre><code>
14797 // Create the menu
14798 var menu = new Roo.menu.Menu();
14799
14800 // Create a menu item to add by reference
14801 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14802
14803 // Add a bunch of items at once using different methods.
14804 // Only the last item added will be returned.
14805 var item = menu.add(
14806     menuItem,                // add existing item by ref
14807     'Dynamic Item',          // new TextItem
14808     '-',                     // new separator
14809     { text: 'Config Item' }  // new item by config
14810 );
14811 </code></pre>
14812      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14813      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14814      */
14815     add : function(){
14816         var a = arguments, l = a.length, item;
14817         for(var i = 0; i < l; i++){
14818             var el = a[i];
14819             if ((typeof(el) == "object") && el.xtype && el.xns) {
14820                 el = Roo.factory(el, Roo.menu);
14821             }
14822             
14823             if(el.render){ // some kind of Item
14824                 item = this.addItem(el);
14825             }else if(typeof el == "string"){ // string
14826                 if(el == "separator" || el == "-"){
14827                     item = this.addSeparator();
14828                 }else{
14829                     item = this.addText(el);
14830                 }
14831             }else if(el.tagName || el.el){ // element
14832                 item = this.addElement(el);
14833             }else if(typeof el == "object"){ // must be menu item config?
14834                 item = this.addMenuItem(el);
14835             }
14836         }
14837         return item;
14838     },
14839
14840     /**
14841      * Returns this menu's underlying {@link Roo.Element} object
14842      * @return {Roo.Element} The element
14843      */
14844     getEl : function(){
14845         if(!this.el){
14846             this.render();
14847         }
14848         return this.el;
14849     },
14850
14851     /**
14852      * Adds a separator bar to the menu
14853      * @return {Roo.menu.Item} The menu item that was added
14854      */
14855     addSeparator : function(){
14856         return this.addItem(new Roo.menu.Separator());
14857     },
14858
14859     /**
14860      * Adds an {@link Roo.Element} object to the menu
14861      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14862      * @return {Roo.menu.Item} The menu item that was added
14863      */
14864     addElement : function(el){
14865         return this.addItem(new Roo.menu.BaseItem(el));
14866     },
14867
14868     /**
14869      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14870      * @param {Roo.menu.Item} item The menu item to add
14871      * @return {Roo.menu.Item} The menu item that was added
14872      */
14873     addItem : function(item){
14874         this.items.add(item);
14875         if(this.ul){
14876             var li = document.createElement("li");
14877             li.className = "x-menu-list-item";
14878             this.ul.dom.appendChild(li);
14879             item.render(li, this);
14880             this.delayAutoWidth();
14881         }
14882         return item;
14883     },
14884
14885     /**
14886      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14887      * @param {Object} config A MenuItem config object
14888      * @return {Roo.menu.Item} The menu item that was added
14889      */
14890     addMenuItem : function(config){
14891         if(!(config instanceof Roo.menu.Item)){
14892             if(typeof config.checked == "boolean"){ // must be check menu item config?
14893                 config = new Roo.menu.CheckItem(config);
14894             }else{
14895                 config = new Roo.menu.Item(config);
14896             }
14897         }
14898         return this.addItem(config);
14899     },
14900
14901     /**
14902      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14903      * @param {String} text The text to display in the menu item
14904      * @return {Roo.menu.Item} The menu item that was added
14905      */
14906     addText : function(text){
14907         return this.addItem(new Roo.menu.TextItem({ text : text }));
14908     },
14909
14910     /**
14911      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14912      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14913      * @param {Roo.menu.Item} item The menu item to add
14914      * @return {Roo.menu.Item} The menu item that was added
14915      */
14916     insert : function(index, item){
14917         this.items.insert(index, item);
14918         if(this.ul){
14919             var li = document.createElement("li");
14920             li.className = "x-menu-list-item";
14921             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14922             item.render(li, this);
14923             this.delayAutoWidth();
14924         }
14925         return item;
14926     },
14927
14928     /**
14929      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14930      * @param {Roo.menu.Item} item The menu item to remove
14931      */
14932     remove : function(item){
14933         this.items.removeKey(item.id);
14934         item.destroy();
14935     },
14936
14937     /**
14938      * Removes and destroys all items in the menu
14939      */
14940     removeAll : function(){
14941         var f;
14942         while(f = this.items.first()){
14943             this.remove(f);
14944         }
14945     }
14946 });
14947
14948 // MenuNav is a private utility class used internally by the Menu
14949 Roo.menu.MenuNav = function(menu){
14950     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14951     this.scope = this.menu = menu;
14952 };
14953
14954 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14955     doRelay : function(e, h){
14956         var k = e.getKey();
14957         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14958             this.menu.tryActivate(0, 1);
14959             return false;
14960         }
14961         return h.call(this.scope || this, e, this.menu);
14962     },
14963
14964     up : function(e, m){
14965         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14966             m.tryActivate(m.items.length-1, -1);
14967         }
14968     },
14969
14970     down : function(e, m){
14971         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14972             m.tryActivate(0, 1);
14973         }
14974     },
14975
14976     right : function(e, m){
14977         if(m.activeItem){
14978             m.activeItem.expandMenu(true);
14979         }
14980     },
14981
14982     left : function(e, m){
14983         m.hide();
14984         if(m.parentMenu && m.parentMenu.activeItem){
14985             m.parentMenu.activeItem.activate();
14986         }
14987     },
14988
14989     enter : function(e, m){
14990         if(m.activeItem){
14991             e.stopPropagation();
14992             m.activeItem.onClick(e);
14993             m.fireEvent("click", this, m.activeItem);
14994             return true;
14995         }
14996     }
14997 });/*
14998  * Based on:
14999  * Ext JS Library 1.1.1
15000  * Copyright(c) 2006-2007, Ext JS, LLC.
15001  *
15002  * Originally Released Under LGPL - original licence link has changed is not relivant.
15003  *
15004  * Fork - LGPL
15005  * <script type="text/javascript">
15006  */
15007  
15008 /**
15009  * @class Roo.menu.MenuMgr
15010  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15011  * @static
15012  */
15013 Roo.menu.MenuMgr = function(){
15014    var menus, active, groups = {}, attached = false, lastShow = new Date();
15015
15016    // private - called when first menu is created
15017    function init(){
15018        menus = {};
15019        active = new Roo.util.MixedCollection();
15020        Roo.get(document).addKeyListener(27, function(){
15021            if(active.length > 0){
15022                hideAll();
15023            }
15024        });
15025    }
15026
15027    // private
15028    function hideAll(){
15029        if(active && active.length > 0){
15030            var c = active.clone();
15031            c.each(function(m){
15032                m.hide();
15033            });
15034        }
15035    }
15036
15037    // private
15038    function onHide(m){
15039        active.remove(m);
15040        if(active.length < 1){
15041            Roo.get(document).un("mousedown", onMouseDown);
15042            attached = false;
15043        }
15044    }
15045
15046    // private
15047    function onShow(m){
15048        var last = active.last();
15049        lastShow = new Date();
15050        active.add(m);
15051        if(!attached){
15052            Roo.get(document).on("mousedown", onMouseDown);
15053            attached = true;
15054        }
15055        if(m.parentMenu){
15056           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15057           m.parentMenu.activeChild = m;
15058        }else if(last && last.isVisible()){
15059           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15060        }
15061    }
15062
15063    // private
15064    function onBeforeHide(m){
15065        if(m.activeChild){
15066            m.activeChild.hide();
15067        }
15068        if(m.autoHideTimer){
15069            clearTimeout(m.autoHideTimer);
15070            delete m.autoHideTimer;
15071        }
15072    }
15073
15074    // private
15075    function onBeforeShow(m){
15076        var pm = m.parentMenu;
15077        if(!pm && !m.allowOtherMenus){
15078            hideAll();
15079        }else if(pm && pm.activeChild && active != m){
15080            pm.activeChild.hide();
15081        }
15082    }
15083
15084    // private
15085    function onMouseDown(e){
15086        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15087            hideAll();
15088        }
15089    }
15090
15091    // private
15092    function onBeforeCheck(mi, state){
15093        if(state){
15094            var g = groups[mi.group];
15095            for(var i = 0, l = g.length; i < l; i++){
15096                if(g[i] != mi){
15097                    g[i].setChecked(false);
15098                }
15099            }
15100        }
15101    }
15102
15103    return {
15104
15105        /**
15106         * Hides all menus that are currently visible
15107         */
15108        hideAll : function(){
15109             hideAll();  
15110        },
15111
15112        // private
15113        register : function(menu){
15114            if(!menus){
15115                init();
15116            }
15117            menus[menu.id] = menu;
15118            menu.on("beforehide", onBeforeHide);
15119            menu.on("hide", onHide);
15120            menu.on("beforeshow", onBeforeShow);
15121            menu.on("show", onShow);
15122            var g = menu.group;
15123            if(g && menu.events["checkchange"]){
15124                if(!groups[g]){
15125                    groups[g] = [];
15126                }
15127                groups[g].push(menu);
15128                menu.on("checkchange", onCheck);
15129            }
15130        },
15131
15132         /**
15133          * Returns a {@link Roo.menu.Menu} object
15134          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15135          * be used to generate and return a new Menu instance.
15136          */
15137        get : function(menu){
15138            if(typeof menu == "string"){ // menu id
15139                return menus[menu];
15140            }else if(menu.events){  // menu instance
15141                return menu;
15142            }else if(typeof menu.length == 'number'){ // array of menu items?
15143                return new Roo.menu.Menu({items:menu});
15144            }else{ // otherwise, must be a config
15145                return new Roo.menu.Menu(menu);
15146            }
15147        },
15148
15149        // private
15150        unregister : function(menu){
15151            delete menus[menu.id];
15152            menu.un("beforehide", onBeforeHide);
15153            menu.un("hide", onHide);
15154            menu.un("beforeshow", onBeforeShow);
15155            menu.un("show", onShow);
15156            var g = menu.group;
15157            if(g && menu.events["checkchange"]){
15158                groups[g].remove(menu);
15159                menu.un("checkchange", onCheck);
15160            }
15161        },
15162
15163        // private
15164        registerCheckable : function(menuItem){
15165            var g = menuItem.group;
15166            if(g){
15167                if(!groups[g]){
15168                    groups[g] = [];
15169                }
15170                groups[g].push(menuItem);
15171                menuItem.on("beforecheckchange", onBeforeCheck);
15172            }
15173        },
15174
15175        // private
15176        unregisterCheckable : function(menuItem){
15177            var g = menuItem.group;
15178            if(g){
15179                groups[g].remove(menuItem);
15180                menuItem.un("beforecheckchange", onBeforeCheck);
15181            }
15182        }
15183    };
15184 }();/*
15185  * Based on:
15186  * Ext JS Library 1.1.1
15187  * Copyright(c) 2006-2007, Ext JS, LLC.
15188  *
15189  * Originally Released Under LGPL - original licence link has changed is not relivant.
15190  *
15191  * Fork - LGPL
15192  * <script type="text/javascript">
15193  */
15194  
15195
15196 /**
15197  * @class Roo.menu.BaseItem
15198  * @extends Roo.Component
15199  * @abstract
15200  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15201  * management and base configuration options shared by all menu components.
15202  * @constructor
15203  * Creates a new BaseItem
15204  * @param {Object} config Configuration options
15205  */
15206 Roo.menu.BaseItem = function(config){
15207     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15208
15209     this.addEvents({
15210         /**
15211          * @event click
15212          * Fires when this item is clicked
15213          * @param {Roo.menu.BaseItem} this
15214          * @param {Roo.EventObject} e
15215          */
15216         click: true,
15217         /**
15218          * @event activate
15219          * Fires when this item is activated
15220          * @param {Roo.menu.BaseItem} this
15221          */
15222         activate : true,
15223         /**
15224          * @event deactivate
15225          * Fires when this item is deactivated
15226          * @param {Roo.menu.BaseItem} this
15227          */
15228         deactivate : true
15229     });
15230
15231     if(this.handler){
15232         this.on("click", this.handler, this.scope, true);
15233     }
15234 };
15235
15236 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15237     /**
15238      * @cfg {Function} handler
15239      * A function that will handle the click event of this menu item (defaults to undefined)
15240      */
15241     /**
15242      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15243      */
15244     canActivate : false,
15245     
15246      /**
15247      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15248      */
15249     hidden: false,
15250     
15251     /**
15252      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15253      */
15254     activeClass : "x-menu-item-active",
15255     /**
15256      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15257      */
15258     hideOnClick : true,
15259     /**
15260      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15261      */
15262     hideDelay : 100,
15263
15264     // private
15265     ctype: "Roo.menu.BaseItem",
15266
15267     // private
15268     actionMode : "container",
15269
15270     // private
15271     render : function(container, parentMenu){
15272         this.parentMenu = parentMenu;
15273         Roo.menu.BaseItem.superclass.render.call(this, container);
15274         this.container.menuItemId = this.id;
15275     },
15276
15277     // private
15278     onRender : function(container, position){
15279         this.el = Roo.get(this.el);
15280         container.dom.appendChild(this.el.dom);
15281     },
15282
15283     // private
15284     onClick : function(e){
15285         if(!this.disabled && this.fireEvent("click", this, e) !== false
15286                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15287             this.handleClick(e);
15288         }else{
15289             e.stopEvent();
15290         }
15291     },
15292
15293     // private
15294     activate : function(){
15295         if(this.disabled){
15296             return false;
15297         }
15298         var li = this.container;
15299         li.addClass(this.activeClass);
15300         this.region = li.getRegion().adjust(2, 2, -2, -2);
15301         this.fireEvent("activate", this);
15302         return true;
15303     },
15304
15305     // private
15306     deactivate : function(){
15307         this.container.removeClass(this.activeClass);
15308         this.fireEvent("deactivate", this);
15309     },
15310
15311     // private
15312     shouldDeactivate : function(e){
15313         return !this.region || !this.region.contains(e.getPoint());
15314     },
15315
15316     // private
15317     handleClick : function(e){
15318         if(this.hideOnClick){
15319             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15320         }
15321     },
15322
15323     // private
15324     expandMenu : function(autoActivate){
15325         // do nothing
15326     },
15327
15328     // private
15329     hideMenu : function(){
15330         // do nothing
15331     }
15332 });/*
15333  * Based on:
15334  * Ext JS Library 1.1.1
15335  * Copyright(c) 2006-2007, Ext JS, LLC.
15336  *
15337  * Originally Released Under LGPL - original licence link has changed is not relivant.
15338  *
15339  * Fork - LGPL
15340  * <script type="text/javascript">
15341  */
15342  
15343 /**
15344  * @class Roo.menu.Adapter
15345  * @extends Roo.menu.BaseItem
15346  * @abstract
15347  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15348  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15349  * @constructor
15350  * Creates a new Adapter
15351  * @param {Object} config Configuration options
15352  */
15353 Roo.menu.Adapter = function(component, config){
15354     Roo.menu.Adapter.superclass.constructor.call(this, config);
15355     this.component = component;
15356 };
15357 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15358     // private
15359     canActivate : true,
15360
15361     // private
15362     onRender : function(container, position){
15363         this.component.render(container);
15364         this.el = this.component.getEl();
15365     },
15366
15367     // private
15368     activate : function(){
15369         if(this.disabled){
15370             return false;
15371         }
15372         this.component.focus();
15373         this.fireEvent("activate", this);
15374         return true;
15375     },
15376
15377     // private
15378     deactivate : function(){
15379         this.fireEvent("deactivate", this);
15380     },
15381
15382     // private
15383     disable : function(){
15384         this.component.disable();
15385         Roo.menu.Adapter.superclass.disable.call(this);
15386     },
15387
15388     // private
15389     enable : function(){
15390         this.component.enable();
15391         Roo.menu.Adapter.superclass.enable.call(this);
15392     }
15393 });/*
15394  * Based on:
15395  * Ext JS Library 1.1.1
15396  * Copyright(c) 2006-2007, Ext JS, LLC.
15397  *
15398  * Originally Released Under LGPL - original licence link has changed is not relivant.
15399  *
15400  * Fork - LGPL
15401  * <script type="text/javascript">
15402  */
15403
15404 /**
15405  * @class Roo.menu.TextItem
15406  * @extends Roo.menu.BaseItem
15407  * Adds a static text string to a menu, usually used as either a heading or group separator.
15408  * Note: old style constructor with text is still supported.
15409  * 
15410  * @constructor
15411  * Creates a new TextItem
15412  * @param {Object} cfg Configuration
15413  */
15414 Roo.menu.TextItem = function(cfg){
15415     if (typeof(cfg) == 'string') {
15416         this.text = cfg;
15417     } else {
15418         Roo.apply(this,cfg);
15419     }
15420     
15421     Roo.menu.TextItem.superclass.constructor.call(this);
15422 };
15423
15424 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15425     /**
15426      * @cfg {String} text Text to show on item.
15427      */
15428     text : '',
15429     
15430     /**
15431      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15432      */
15433     hideOnClick : false,
15434     /**
15435      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15436      */
15437     itemCls : "x-menu-text",
15438
15439     // private
15440     onRender : function(){
15441         var s = document.createElement("span");
15442         s.className = this.itemCls;
15443         s.innerHTML = this.text;
15444         this.el = s;
15445         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15446     }
15447 });/*
15448  * Based on:
15449  * Ext JS Library 1.1.1
15450  * Copyright(c) 2006-2007, Ext JS, LLC.
15451  *
15452  * Originally Released Under LGPL - original licence link has changed is not relivant.
15453  *
15454  * Fork - LGPL
15455  * <script type="text/javascript">
15456  */
15457
15458 /**
15459  * @class Roo.menu.Separator
15460  * @extends Roo.menu.BaseItem
15461  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15462  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15463  * @constructor
15464  * @param {Object} config Configuration options
15465  */
15466 Roo.menu.Separator = function(config){
15467     Roo.menu.Separator.superclass.constructor.call(this, config);
15468 };
15469
15470 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15471     /**
15472      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15473      */
15474     itemCls : "x-menu-sep",
15475     /**
15476      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15477      */
15478     hideOnClick : false,
15479
15480     // private
15481     onRender : function(li){
15482         var s = document.createElement("span");
15483         s.className = this.itemCls;
15484         s.innerHTML = "&#160;";
15485         this.el = s;
15486         li.addClass("x-menu-sep-li");
15487         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15488     }
15489 });/*
15490  * Based on:
15491  * Ext JS Library 1.1.1
15492  * Copyright(c) 2006-2007, Ext JS, LLC.
15493  *
15494  * Originally Released Under LGPL - original licence link has changed is not relivant.
15495  *
15496  * Fork - LGPL
15497  * <script type="text/javascript">
15498  */
15499 /**
15500  * @class Roo.menu.Item
15501  * @extends Roo.menu.BaseItem
15502  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15503  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15504  * activation and click handling.
15505  * @constructor
15506  * Creates a new Item
15507  * @param {Object} config Configuration options
15508  */
15509 Roo.menu.Item = function(config){
15510     Roo.menu.Item.superclass.constructor.call(this, config);
15511     if(this.menu){
15512         this.menu = Roo.menu.MenuMgr.get(this.menu);
15513     }
15514 };
15515 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15516     /**
15517      * @cfg {Roo.menu.Menu} menu
15518      * A Sub menu
15519      */
15520     /**
15521      * @cfg {String} text
15522      * The text to show on the menu item.
15523      */
15524     text: '',
15525      /**
15526      * @cfg {String} html to render in menu
15527      * The text to show on the menu item (HTML version).
15528      */
15529     html: '',
15530     /**
15531      * @cfg {String} icon
15532      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15533      */
15534     icon: undefined,
15535     /**
15536      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15537      */
15538     itemCls : "x-menu-item",
15539     /**
15540      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15541      */
15542     canActivate : true,
15543     /**
15544      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15545      */
15546     showDelay: 200,
15547     // doc'd in BaseItem
15548     hideDelay: 200,
15549
15550     // private
15551     ctype: "Roo.menu.Item",
15552     
15553     // private
15554     onRender : function(container, position){
15555         var el = document.createElement("a");
15556         el.hideFocus = true;
15557         el.unselectable = "on";
15558         el.href = this.href || "#";
15559         if(this.hrefTarget){
15560             el.target = this.hrefTarget;
15561         }
15562         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15563         
15564         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15565         
15566         el.innerHTML = String.format(
15567                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15568                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15569         this.el = el;
15570         Roo.menu.Item.superclass.onRender.call(this, container, position);
15571     },
15572
15573     /**
15574      * Sets the text to display in this menu item
15575      * @param {String} text The text to display
15576      * @param {Boolean} isHTML true to indicate text is pure html.
15577      */
15578     setText : function(text, isHTML){
15579         if (isHTML) {
15580             this.html = text;
15581         } else {
15582             this.text = text;
15583             this.html = '';
15584         }
15585         if(this.rendered){
15586             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15587      
15588             this.el.update(String.format(
15589                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15590                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15591             this.parentMenu.autoWidth();
15592         }
15593     },
15594
15595     // private
15596     handleClick : function(e){
15597         if(!this.href){ // if no link defined, stop the event automatically
15598             e.stopEvent();
15599         }
15600         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15601     },
15602
15603     // private
15604     activate : function(autoExpand){
15605         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15606             this.focus();
15607             if(autoExpand){
15608                 this.expandMenu();
15609             }
15610         }
15611         return true;
15612     },
15613
15614     // private
15615     shouldDeactivate : function(e){
15616         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15617             if(this.menu && this.menu.isVisible()){
15618                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15619             }
15620             return true;
15621         }
15622         return false;
15623     },
15624
15625     // private
15626     deactivate : function(){
15627         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15628         this.hideMenu();
15629     },
15630
15631     // private
15632     expandMenu : function(autoActivate){
15633         if(!this.disabled && this.menu){
15634             clearTimeout(this.hideTimer);
15635             delete this.hideTimer;
15636             if(!this.menu.isVisible() && !this.showTimer){
15637                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15638             }else if (this.menu.isVisible() && autoActivate){
15639                 this.menu.tryActivate(0, 1);
15640             }
15641         }
15642     },
15643
15644     // private
15645     deferExpand : function(autoActivate){
15646         delete this.showTimer;
15647         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15648         if(autoActivate){
15649             this.menu.tryActivate(0, 1);
15650         }
15651     },
15652
15653     // private
15654     hideMenu : function(){
15655         clearTimeout(this.showTimer);
15656         delete this.showTimer;
15657         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15658             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15659         }
15660     },
15661
15662     // private
15663     deferHide : function(){
15664         delete this.hideTimer;
15665         this.menu.hide();
15666     }
15667 });/*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677  
15678 /**
15679  * @class Roo.menu.CheckItem
15680  * @extends Roo.menu.Item
15681  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15682  * @constructor
15683  * Creates a new CheckItem
15684  * @param {Object} config Configuration options
15685  */
15686 Roo.menu.CheckItem = function(config){
15687     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15688     this.addEvents({
15689         /**
15690          * @event beforecheckchange
15691          * Fires before the checked value is set, providing an opportunity to cancel if needed
15692          * @param {Roo.menu.CheckItem} this
15693          * @param {Boolean} checked The new checked value that will be set
15694          */
15695         "beforecheckchange" : true,
15696         /**
15697          * @event checkchange
15698          * Fires after the checked value has been set
15699          * @param {Roo.menu.CheckItem} this
15700          * @param {Boolean} checked The checked value that was set
15701          */
15702         "checkchange" : true
15703     });
15704     if(this.checkHandler){
15705         this.on('checkchange', this.checkHandler, this.scope);
15706     }
15707 };
15708 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15709     /**
15710      * @cfg {String} group
15711      * All check items with the same group name will automatically be grouped into a single-select
15712      * radio button group (defaults to '')
15713      */
15714     /**
15715      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15716      */
15717     itemCls : "x-menu-item x-menu-check-item",
15718     /**
15719      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15720      */
15721     groupClass : "x-menu-group-item",
15722
15723     /**
15724      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15725      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15726      * initialized with checked = true will be rendered as checked.
15727      */
15728     checked: false,
15729
15730     // private
15731     ctype: "Roo.menu.CheckItem",
15732
15733     // private
15734     onRender : function(c){
15735         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15736         if(this.group){
15737             this.el.addClass(this.groupClass);
15738         }
15739         Roo.menu.MenuMgr.registerCheckable(this);
15740         if(this.checked){
15741             this.checked = false;
15742             this.setChecked(true, true);
15743         }
15744     },
15745
15746     // private
15747     destroy : function(){
15748         if(this.rendered){
15749             Roo.menu.MenuMgr.unregisterCheckable(this);
15750         }
15751         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15752     },
15753
15754     /**
15755      * Set the checked state of this item
15756      * @param {Boolean} checked The new checked value
15757      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15758      */
15759     setChecked : function(state, suppressEvent){
15760         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15761             if(this.container){
15762                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15763             }
15764             this.checked = state;
15765             if(suppressEvent !== true){
15766                 this.fireEvent("checkchange", this, state);
15767             }
15768         }
15769     },
15770
15771     // private
15772     handleClick : function(e){
15773        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15774            this.setChecked(!this.checked);
15775        }
15776        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15777     }
15778 });/*
15779  * Based on:
15780  * Ext JS Library 1.1.1
15781  * Copyright(c) 2006-2007, Ext JS, LLC.
15782  *
15783  * Originally Released Under LGPL - original licence link has changed is not relivant.
15784  *
15785  * Fork - LGPL
15786  * <script type="text/javascript">
15787  */
15788  
15789 /**
15790  * @class Roo.menu.DateItem
15791  * @extends Roo.menu.Adapter
15792  * A menu item that wraps the {@link Roo.DatPicker} component.
15793  * @constructor
15794  * Creates a new DateItem
15795  * @param {Object} config Configuration options
15796  */
15797 Roo.menu.DateItem = function(config){
15798     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15799     /** The Roo.DatePicker object @type Roo.DatePicker */
15800     this.picker = this.component;
15801     this.addEvents({select: true});
15802     
15803     this.picker.on("render", function(picker){
15804         picker.getEl().swallowEvent("click");
15805         picker.container.addClass("x-menu-date-item");
15806     });
15807
15808     this.picker.on("select", this.onSelect, this);
15809 };
15810
15811 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15812     // private
15813     onSelect : function(picker, date){
15814         this.fireEvent("select", this, date, picker);
15815         Roo.menu.DateItem.superclass.handleClick.call(this);
15816     }
15817 });/*
15818  * Based on:
15819  * Ext JS Library 1.1.1
15820  * Copyright(c) 2006-2007, Ext JS, LLC.
15821  *
15822  * Originally Released Under LGPL - original licence link has changed is not relivant.
15823  *
15824  * Fork - LGPL
15825  * <script type="text/javascript">
15826  */
15827  
15828 /**
15829  * @class Roo.menu.ColorItem
15830  * @extends Roo.menu.Adapter
15831  * A menu item that wraps the {@link Roo.ColorPalette} component.
15832  * @constructor
15833  * Creates a new ColorItem
15834  * @param {Object} config Configuration options
15835  */
15836 Roo.menu.ColorItem = function(config){
15837     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15838     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15839     this.palette = this.component;
15840     this.relayEvents(this.palette, ["select"]);
15841     if(this.selectHandler){
15842         this.on('select', this.selectHandler, this.scope);
15843     }
15844 };
15845 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15846  * Based on:
15847  * Ext JS Library 1.1.1
15848  * Copyright(c) 2006-2007, Ext JS, LLC.
15849  *
15850  * Originally Released Under LGPL - original licence link has changed is not relivant.
15851  *
15852  * Fork - LGPL
15853  * <script type="text/javascript">
15854  */
15855  
15856
15857 /**
15858  * @class Roo.menu.DateMenu
15859  * @extends Roo.menu.Menu
15860  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15861  * @constructor
15862  * Creates a new DateMenu
15863  * @param {Object} config Configuration options
15864  */
15865 Roo.menu.DateMenu = function(config){
15866     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15867     this.plain = true;
15868     var di = new Roo.menu.DateItem(config);
15869     this.add(di);
15870     /**
15871      * The {@link Roo.DatePicker} instance for this DateMenu
15872      * @type DatePicker
15873      */
15874     this.picker = di.picker;
15875     /**
15876      * @event select
15877      * @param {DatePicker} picker
15878      * @param {Date} date
15879      */
15880     this.relayEvents(di, ["select"]);
15881     this.on('beforeshow', function(){
15882         if(this.picker){
15883             this.picker.hideMonthPicker(false);
15884         }
15885     }, this);
15886 };
15887 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15888     cls:'x-date-menu'
15889 });/*
15890  * Based on:
15891  * Ext JS Library 1.1.1
15892  * Copyright(c) 2006-2007, Ext JS, LLC.
15893  *
15894  * Originally Released Under LGPL - original licence link has changed is not relivant.
15895  *
15896  * Fork - LGPL
15897  * <script type="text/javascript">
15898  */
15899  
15900
15901 /**
15902  * @class Roo.menu.ColorMenu
15903  * @extends Roo.menu.Menu
15904  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15905  * @constructor
15906  * Creates a new ColorMenu
15907  * @param {Object} config Configuration options
15908  */
15909 Roo.menu.ColorMenu = function(config){
15910     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15911     this.plain = true;
15912     var ci = new Roo.menu.ColorItem(config);
15913     this.add(ci);
15914     /**
15915      * The {@link Roo.ColorPalette} instance for this ColorMenu
15916      * @type ColorPalette
15917      */
15918     this.palette = ci.palette;
15919     /**
15920      * @event select
15921      * @param {ColorPalette} palette
15922      * @param {String} color
15923      */
15924     this.relayEvents(ci, ["select"]);
15925 };
15926 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15927  * Based on:
15928  * Ext JS Library 1.1.1
15929  * Copyright(c) 2006-2007, Ext JS, LLC.
15930  *
15931  * Originally Released Under LGPL - original licence link has changed is not relivant.
15932  *
15933  * Fork - LGPL
15934  * <script type="text/javascript">
15935  */
15936  
15937 /**
15938  * @class Roo.form.TextItem
15939  * @extends Roo.BoxComponent
15940  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15941  * @constructor
15942  * Creates a new TextItem
15943  * @param {Object} config Configuration options
15944  */
15945 Roo.form.TextItem = function(config){
15946     Roo.form.TextItem.superclass.constructor.call(this, config);
15947 };
15948
15949 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15950     
15951     /**
15952      * @cfg {String} tag the tag for this item (default div)
15953      */
15954     tag : 'div',
15955     /**
15956      * @cfg {String} html the content for this item
15957      */
15958     html : '',
15959     
15960     getAutoCreate : function()
15961     {
15962         var cfg = {
15963             id: this.id,
15964             tag: this.tag,
15965             html: this.html,
15966             cls: 'x-form-item'
15967         };
15968         
15969         return cfg;
15970         
15971     },
15972     
15973     onRender : function(ct, position)
15974     {
15975         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15976         
15977         if(!this.el){
15978             var cfg = this.getAutoCreate();
15979             if(!cfg.name){
15980                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15981             }
15982             if (!cfg.name.length) {
15983                 delete cfg.name;
15984             }
15985             this.el = ct.createChild(cfg, position);
15986         }
15987     },
15988     /*
15989      * setHTML
15990      * @param {String} html update the Contents of the element.
15991      */
15992     setHTML : function(html)
15993     {
15994         this.fieldEl.dom.innerHTML = html;
15995     }
15996     
15997 });/*
15998  * Based on:
15999  * Ext JS Library 1.1.1
16000  * Copyright(c) 2006-2007, Ext JS, LLC.
16001  *
16002  * Originally Released Under LGPL - original licence link has changed is not relivant.
16003  *
16004  * Fork - LGPL
16005  * <script type="text/javascript">
16006  */
16007  
16008 /**
16009  * @class Roo.form.Field
16010  * @extends Roo.BoxComponent
16011  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16012  * @constructor
16013  * Creates a new Field
16014  * @param {Object} config Configuration options
16015  */
16016 Roo.form.Field = function(config){
16017     Roo.form.Field.superclass.constructor.call(this, config);
16018 };
16019
16020 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16021     /**
16022      * @cfg {String} fieldLabel Label to use when rendering a form.
16023      */
16024        /**
16025      * @cfg {String} qtip Mouse over tip
16026      */
16027      
16028     /**
16029      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16030      */
16031     invalidClass : "x-form-invalid",
16032     /**
16033      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
16034      */
16035     invalidText : "The value in this field is invalid",
16036     /**
16037      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16038      */
16039     focusClass : "x-form-focus",
16040     /**
16041      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16042       automatic validation (defaults to "keyup").
16043      */
16044     validationEvent : "keyup",
16045     /**
16046      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16047      */
16048     validateOnBlur : true,
16049     /**
16050      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16051      */
16052     validationDelay : 250,
16053     /**
16054      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16055      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16056      */
16057     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16058     /**
16059      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16060      */
16061     fieldClass : "x-form-field",
16062     /**
16063      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16064      *<pre>
16065 Value         Description
16066 -----------   ----------------------------------------------------------------------
16067 qtip          Display a quick tip when the user hovers over the field
16068 title         Display a default browser title attribute popup
16069 under         Add a block div beneath the field containing the error text
16070 side          Add an error icon to the right of the field with a popup on hover
16071 [element id]  Add the error text directly to the innerHTML of the specified element
16072 </pre>
16073      */
16074     msgTarget : 'qtip',
16075     /**
16076      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16077      */
16078     msgFx : 'normal',
16079
16080     /**
16081      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16082      */
16083     readOnly : false,
16084
16085     /**
16086      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16087      */
16088     disabled : false,
16089
16090     /**
16091      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16092      */
16093     inputType : undefined,
16094     
16095     /**
16096      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16097          */
16098         tabIndex : undefined,
16099         
16100     // private
16101     isFormField : true,
16102
16103     // private
16104     hasFocus : false,
16105     /**
16106      * @property {Roo.Element} fieldEl
16107      * Element Containing the rendered Field (with label etc.)
16108      */
16109     /**
16110      * @cfg {Mixed} value A value to initialize this field with.
16111      */
16112     value : undefined,
16113
16114     /**
16115      * @cfg {String} name The field's HTML name attribute.
16116      */
16117     /**
16118      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16119      */
16120     // private
16121     loadedValue : false,
16122      
16123      
16124         // private ??
16125         initComponent : function(){
16126         Roo.form.Field.superclass.initComponent.call(this);
16127         this.addEvents({
16128             /**
16129              * @event focus
16130              * Fires when this field receives input focus.
16131              * @param {Roo.form.Field} this
16132              */
16133             focus : true,
16134             /**
16135              * @event blur
16136              * Fires when this field loses input focus.
16137              * @param {Roo.form.Field} this
16138              */
16139             blur : true,
16140             /**
16141              * @event specialkey
16142              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16143              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16144              * @param {Roo.form.Field} this
16145              * @param {Roo.EventObject} e The event object
16146              */
16147             specialkey : true,
16148             /**
16149              * @event change
16150              * Fires just before the field blurs if the field value has changed.
16151              * @param {Roo.form.Field} this
16152              * @param {Mixed} newValue The new value
16153              * @param {Mixed} oldValue The original value
16154              */
16155             change : true,
16156             /**
16157              * @event invalid
16158              * Fires after the field has been marked as invalid.
16159              * @param {Roo.form.Field} this
16160              * @param {String} msg The validation message
16161              */
16162             invalid : true,
16163             /**
16164              * @event valid
16165              * Fires after the field has been validated with no errors.
16166              * @param {Roo.form.Field} this
16167              */
16168             valid : true,
16169              /**
16170              * @event keyup
16171              * Fires after the key up
16172              * @param {Roo.form.Field} this
16173              * @param {Roo.EventObject}  e The event Object
16174              */
16175             keyup : true
16176         });
16177     },
16178
16179     /**
16180      * Returns the name attribute of the field if available
16181      * @return {String} name The field name
16182      */
16183     getName: function(){
16184          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16185     },
16186
16187     // private
16188     onRender : function(ct, position){
16189         Roo.form.Field.superclass.onRender.call(this, ct, position);
16190         if(!this.el){
16191             var cfg = this.getAutoCreate();
16192             if(!cfg.name){
16193                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16194             }
16195             if (!cfg.name.length) {
16196                 delete cfg.name;
16197             }
16198             if(this.inputType){
16199                 cfg.type = this.inputType;
16200             }
16201             this.el = ct.createChild(cfg, position);
16202         }
16203         var type = this.el.dom.type;
16204         if(type){
16205             if(type == 'password'){
16206                 type = 'text';
16207             }
16208             this.el.addClass('x-form-'+type);
16209         }
16210         if(this.readOnly){
16211             this.el.dom.readOnly = true;
16212         }
16213         if(this.tabIndex !== undefined){
16214             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16215         }
16216
16217         this.el.addClass([this.fieldClass, this.cls]);
16218         this.initValue();
16219     },
16220
16221     /**
16222      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16223      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16224      * @return {Roo.form.Field} this
16225      */
16226     applyTo : function(target){
16227         this.allowDomMove = false;
16228         this.el = Roo.get(target);
16229         this.render(this.el.dom.parentNode);
16230         return this;
16231     },
16232
16233     // private
16234     initValue : function(){
16235         if(this.value !== undefined){
16236             this.setValue(this.value);
16237         }else if(this.el.dom.value.length > 0){
16238             this.setValue(this.el.dom.value);
16239         }
16240     },
16241
16242     /**
16243      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16244      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16245      */
16246     isDirty : function() {
16247         if(this.disabled) {
16248             return false;
16249         }
16250         return String(this.getValue()) !== String(this.originalValue);
16251     },
16252
16253     /**
16254      * stores the current value in loadedValue
16255      */
16256     resetHasChanged : function()
16257     {
16258         this.loadedValue = String(this.getValue());
16259     },
16260     /**
16261      * checks the current value against the 'loaded' value.
16262      * Note - will return false if 'resetHasChanged' has not been called first.
16263      */
16264     hasChanged : function()
16265     {
16266         if(this.disabled || this.readOnly) {
16267             return false;
16268         }
16269         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16270     },
16271     
16272     
16273     
16274     // private
16275     afterRender : function(){
16276         Roo.form.Field.superclass.afterRender.call(this);
16277         this.initEvents();
16278     },
16279
16280     // private
16281     fireKey : function(e){
16282         //Roo.log('field ' + e.getKey());
16283         if(e.isNavKeyPress()){
16284             this.fireEvent("specialkey", this, e);
16285         }
16286     },
16287
16288     /**
16289      * Resets the current field value to the originally loaded value and clears any validation messages
16290      */
16291     reset : function(){
16292         this.setValue(this.resetValue);
16293         this.originalValue = this.getValue();
16294         this.clearInvalid();
16295     },
16296
16297     // private
16298     initEvents : function(){
16299         // safari killled keypress - so keydown is now used..
16300         this.el.on("keydown" , this.fireKey,  this);
16301         this.el.on("focus", this.onFocus,  this);
16302         this.el.on("blur", this.onBlur,  this);
16303         this.el.relayEvent('keyup', this);
16304
16305         // reference to original value for reset
16306         this.originalValue = this.getValue();
16307         this.resetValue =  this.getValue();
16308     },
16309
16310     // private
16311     onFocus : function(){
16312         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16313             this.el.addClass(this.focusClass);
16314         }
16315         if(!this.hasFocus){
16316             this.hasFocus = true;
16317             this.startValue = this.getValue();
16318             this.fireEvent("focus", this);
16319         }
16320     },
16321
16322     beforeBlur : Roo.emptyFn,
16323
16324     // private
16325     onBlur : function(){
16326         this.beforeBlur();
16327         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16328             this.el.removeClass(this.focusClass);
16329         }
16330         this.hasFocus = false;
16331         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16332             this.validate();
16333         }
16334         var v = this.getValue();
16335         if(String(v) !== String(this.startValue)){
16336             this.fireEvent('change', this, v, this.startValue);
16337         }
16338         this.fireEvent("blur", this);
16339     },
16340
16341     /**
16342      * Returns whether or not the field value is currently valid
16343      * @param {Boolean} preventMark True to disable marking the field invalid
16344      * @return {Boolean} True if the value is valid, else false
16345      */
16346     isValid : function(preventMark){
16347         if(this.disabled){
16348             return true;
16349         }
16350         var restore = this.preventMark;
16351         this.preventMark = preventMark === true;
16352         var v = this.validateValue(this.processValue(this.getRawValue()));
16353         this.preventMark = restore;
16354         return v;
16355     },
16356
16357     /**
16358      * Validates the field value
16359      * @return {Boolean} True if the value is valid, else false
16360      */
16361     validate : function(){
16362         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16363             this.clearInvalid();
16364             return true;
16365         }
16366         return false;
16367     },
16368
16369     processValue : function(value){
16370         return value;
16371     },
16372
16373     // private
16374     // Subclasses should provide the validation implementation by overriding this
16375     validateValue : function(value){
16376         return true;
16377     },
16378
16379     /**
16380      * Mark this field as invalid
16381      * @param {String} msg The validation message
16382      */
16383     markInvalid : function(msg){
16384         if(!this.rendered || this.preventMark){ // not rendered
16385             return;
16386         }
16387         
16388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16389         
16390         obj.el.addClass(this.invalidClass);
16391         msg = msg || this.invalidText;
16392         switch(this.msgTarget){
16393             case 'qtip':
16394                 obj.el.dom.qtip = msg;
16395                 obj.el.dom.qclass = 'x-form-invalid-tip';
16396                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16397                     Roo.QuickTips.enable();
16398                 }
16399                 break;
16400             case 'title':
16401                 this.el.dom.title = msg;
16402                 break;
16403             case 'under':
16404                 if(!this.errorEl){
16405                     var elp = this.el.findParent('.x-form-element', 5, true);
16406                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16407                     this.errorEl.setWidth(elp.getWidth(true)-20);
16408                 }
16409                 this.errorEl.update(msg);
16410                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16411                 break;
16412             case 'side':
16413                 if(!this.errorIcon){
16414                     var elp = this.el.findParent('.x-form-element', 5, true);
16415                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16416                 }
16417                 this.alignErrorIcon();
16418                 this.errorIcon.dom.qtip = msg;
16419                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16420                 this.errorIcon.show();
16421                 this.on('resize', this.alignErrorIcon, this);
16422                 break;
16423             default:
16424                 var t = Roo.getDom(this.msgTarget);
16425                 t.innerHTML = msg;
16426                 t.style.display = this.msgDisplay;
16427                 break;
16428         }
16429         this.fireEvent('invalid', this, msg);
16430     },
16431
16432     // private
16433     alignErrorIcon : function(){
16434         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16435     },
16436
16437     /**
16438      * Clear any invalid styles/messages for this field
16439      */
16440     clearInvalid : function(){
16441         if(!this.rendered || this.preventMark){ // not rendered
16442             return;
16443         }
16444         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16445         
16446         obj.el.removeClass(this.invalidClass);
16447         switch(this.msgTarget){
16448             case 'qtip':
16449                 obj.el.dom.qtip = '';
16450                 break;
16451             case 'title':
16452                 this.el.dom.title = '';
16453                 break;
16454             case 'under':
16455                 if(this.errorEl){
16456                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16457                 }
16458                 break;
16459             case 'side':
16460                 if(this.errorIcon){
16461                     this.errorIcon.dom.qtip = '';
16462                     this.errorIcon.hide();
16463                     this.un('resize', this.alignErrorIcon, this);
16464                 }
16465                 break;
16466             default:
16467                 var t = Roo.getDom(this.msgTarget);
16468                 t.innerHTML = '';
16469                 t.style.display = 'none';
16470                 break;
16471         }
16472         this.fireEvent('valid', this);
16473     },
16474
16475     /**
16476      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16477      * @return {Mixed} value The field value
16478      */
16479     getRawValue : function(){
16480         var v = this.el.getValue();
16481         
16482         return v;
16483     },
16484
16485     /**
16486      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16487      * @return {Mixed} value The field value
16488      */
16489     getValue : function(){
16490         var v = this.el.getValue();
16491          
16492         return v;
16493     },
16494
16495     /**
16496      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16497      * @param {Mixed} value The value to set
16498      */
16499     setRawValue : function(v){
16500         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16501     },
16502
16503     /**
16504      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16505      * @param {Mixed} value The value to set
16506      */
16507     setValue : function(v){
16508         this.value = v;
16509         if(this.rendered){
16510             this.el.dom.value = (v === null || v === undefined ? '' : v);
16511              this.validate();
16512         }
16513     },
16514
16515     adjustSize : function(w, h){
16516         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16517         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16518         return s;
16519     },
16520
16521     adjustWidth : function(tag, w){
16522         tag = tag.toLowerCase();
16523         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16524             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16525                 if(tag == 'input'){
16526                     return w + 2;
16527                 }
16528                 if(tag == 'textarea'){
16529                     return w-2;
16530                 }
16531             }else if(Roo.isOpera){
16532                 if(tag == 'input'){
16533                     return w + 2;
16534                 }
16535                 if(tag == 'textarea'){
16536                     return w-2;
16537                 }
16538             }
16539         }
16540         return w;
16541     }
16542 });
16543
16544
16545 // anything other than normal should be considered experimental
16546 Roo.form.Field.msgFx = {
16547     normal : {
16548         show: function(msgEl, f){
16549             msgEl.setDisplayed('block');
16550         },
16551
16552         hide : function(msgEl, f){
16553             msgEl.setDisplayed(false).update('');
16554         }
16555     },
16556
16557     slide : {
16558         show: function(msgEl, f){
16559             msgEl.slideIn('t', {stopFx:true});
16560         },
16561
16562         hide : function(msgEl, f){
16563             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16564         }
16565     },
16566
16567     slideRight : {
16568         show: function(msgEl, f){
16569             msgEl.fixDisplay();
16570             msgEl.alignTo(f.el, 'tl-tr');
16571             msgEl.slideIn('l', {stopFx:true});
16572         },
16573
16574         hide : function(msgEl, f){
16575             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16576         }
16577     }
16578 };/*
16579  * Based on:
16580  * Ext JS Library 1.1.1
16581  * Copyright(c) 2006-2007, Ext JS, LLC.
16582  *
16583  * Originally Released Under LGPL - original licence link has changed is not relivant.
16584  *
16585  * Fork - LGPL
16586  * <script type="text/javascript">
16587  */
16588  
16589
16590 /**
16591  * @class Roo.form.TextField
16592  * @extends Roo.form.Field
16593  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16594  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16595  * @constructor
16596  * Creates a new TextField
16597  * @param {Object} config Configuration options
16598  */
16599 Roo.form.TextField = function(config){
16600     Roo.form.TextField.superclass.constructor.call(this, config);
16601     this.addEvents({
16602         /**
16603          * @event autosize
16604          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16605          * according to the default logic, but this event provides a hook for the developer to apply additional
16606          * logic at runtime to resize the field if needed.
16607              * @param {Roo.form.Field} this This text field
16608              * @param {Number} width The new field width
16609              */
16610         autosize : true
16611     });
16612 };
16613
16614 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16615     /**
16616      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16617      */
16618     grow : false,
16619     /**
16620      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16621      */
16622     growMin : 30,
16623     /**
16624      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16625      */
16626     growMax : 800,
16627     /**
16628      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16629      */
16630     vtype : null,
16631     /**
16632      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16633      */
16634     maskRe : null,
16635     /**
16636      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16637      */
16638     disableKeyFilter : false,
16639     /**
16640      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16641      */
16642     allowBlank : true,
16643     /**
16644      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16645      */
16646     minLength : 0,
16647     /**
16648      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16649      */
16650     maxLength : Number.MAX_VALUE,
16651     /**
16652      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16653      */
16654     minLengthText : "The minimum length for this field is {0}",
16655     /**
16656      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16657      */
16658     maxLengthText : "The maximum length for this field is {0}",
16659     /**
16660      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16661      */
16662     selectOnFocus : false,
16663     /**
16664      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16665      */    
16666     allowLeadingSpace : false,
16667     /**
16668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16669      */
16670     blankText : "This field is required",
16671     /**
16672      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16673      * If available, this function will be called only after the basic validators all return true, and will be passed the
16674      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16675      */
16676     validator : null,
16677     /**
16678      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16679      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16680      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16681      */
16682     regex : null,
16683     /**
16684      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16685      */
16686     regexText : "",
16687     /**
16688      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16689      */
16690     emptyText : null,
16691    
16692
16693     // private
16694     initEvents : function()
16695     {
16696         if (this.emptyText) {
16697             this.el.attr('placeholder', this.emptyText);
16698         }
16699         
16700         Roo.form.TextField.superclass.initEvents.call(this);
16701         if(this.validationEvent == 'keyup'){
16702             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16703             this.el.on('keyup', this.filterValidation, this);
16704         }
16705         else if(this.validationEvent !== false){
16706             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16707         }
16708         
16709         if(this.selectOnFocus){
16710             this.on("focus", this.preFocus, this);
16711         }
16712         if (!this.allowLeadingSpace) {
16713             this.on('blur', this.cleanLeadingSpace, this);
16714         }
16715         
16716         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16717             this.el.on("keypress", this.filterKeys, this);
16718         }
16719         if(this.grow){
16720             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16721             this.el.on("click", this.autoSize,  this);
16722         }
16723         if(this.el.is('input[type=password]') && Roo.isSafari){
16724             this.el.on('keydown', this.SafariOnKeyDown, this);
16725         }
16726     },
16727
16728     processValue : function(value){
16729         if(this.stripCharsRe){
16730             var newValue = value.replace(this.stripCharsRe, '');
16731             if(newValue !== value){
16732                 this.setRawValue(newValue);
16733                 return newValue;
16734             }
16735         }
16736         return value;
16737     },
16738
16739     filterValidation : function(e){
16740         if(!e.isNavKeyPress()){
16741             this.validationTask.delay(this.validationDelay);
16742         }
16743     },
16744
16745     // private
16746     onKeyUp : function(e){
16747         if(!e.isNavKeyPress()){
16748             this.autoSize();
16749         }
16750     },
16751     // private - clean the leading white space
16752     cleanLeadingSpace : function(e)
16753     {
16754         if ( this.inputType == 'file') {
16755             return;
16756         }
16757         
16758         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16759     },
16760     /**
16761      * Resets the current field value to the originally-loaded value and clears any validation messages.
16762      *  
16763      */
16764     reset : function(){
16765         Roo.form.TextField.superclass.reset.call(this);
16766        
16767     }, 
16768     // private
16769     preFocus : function(){
16770         
16771         if(this.selectOnFocus){
16772             this.el.dom.select();
16773         }
16774     },
16775
16776     
16777     // private
16778     filterKeys : function(e){
16779         var k = e.getKey();
16780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16781             return;
16782         }
16783         var c = e.getCharCode(), cc = String.fromCharCode(c);
16784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16785             return;
16786         }
16787         if(!this.maskRe.test(cc)){
16788             e.stopEvent();
16789         }
16790     },
16791
16792     setValue : function(v){
16793         
16794         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16795         
16796         this.autoSize();
16797     },
16798
16799     /**
16800      * Validates a value according to the field's validation rules and marks the field as invalid
16801      * if the validation fails
16802      * @param {Mixed} value The value to validate
16803      * @return {Boolean} True if the value is valid, else false
16804      */
16805     validateValue : function(value){
16806         if(value.length < 1)  { // if it's blank
16807              if(this.allowBlank){
16808                 this.clearInvalid();
16809                 return true;
16810              }else{
16811                 this.markInvalid(this.blankText);
16812                 return false;
16813              }
16814         }
16815         if(value.length < this.minLength){
16816             this.markInvalid(String.format(this.minLengthText, this.minLength));
16817             return false;
16818         }
16819         if(value.length > this.maxLength){
16820             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16821             return false;
16822         }
16823         if(this.vtype){
16824             var vt = Roo.form.VTypes;
16825             if(!vt[this.vtype](value, this)){
16826                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16827                 return false;
16828             }
16829         }
16830         if(typeof this.validator == "function"){
16831             var msg = this.validator(value);
16832             if(msg !== true){
16833                 this.markInvalid(msg);
16834                 return false;
16835             }
16836         }
16837         if(this.regex && !this.regex.test(value)){
16838             this.markInvalid(this.regexText);
16839             return false;
16840         }
16841         return true;
16842     },
16843
16844     /**
16845      * Selects text in this field
16846      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16847      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16848      */
16849     selectText : function(start, end){
16850         var v = this.getRawValue();
16851         if(v.length > 0){
16852             start = start === undefined ? 0 : start;
16853             end = end === undefined ? v.length : end;
16854             var d = this.el.dom;
16855             if(d.setSelectionRange){
16856                 d.setSelectionRange(start, end);
16857             }else if(d.createTextRange){
16858                 var range = d.createTextRange();
16859                 range.moveStart("character", start);
16860                 range.moveEnd("character", v.length-end);
16861                 range.select();
16862             }
16863         }
16864     },
16865
16866     /**
16867      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16868      * This only takes effect if grow = true, and fires the autosize event.
16869      */
16870     autoSize : function(){
16871         if(!this.grow || !this.rendered){
16872             return;
16873         }
16874         if(!this.metrics){
16875             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16876         }
16877         var el = this.el;
16878         var v = el.dom.value;
16879         var d = document.createElement('div');
16880         d.appendChild(document.createTextNode(v));
16881         v = d.innerHTML;
16882         d = null;
16883         v += "&#160;";
16884         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16885         this.el.setWidth(w);
16886         this.fireEvent("autosize", this, w);
16887     },
16888     
16889     // private
16890     SafariOnKeyDown : function(event)
16891     {
16892         // this is a workaround for a password hang bug on chrome/ webkit.
16893         
16894         var isSelectAll = false;
16895         
16896         if(this.el.dom.selectionEnd > 0){
16897             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16898         }
16899         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16900             event.preventDefault();
16901             this.setValue('');
16902             return;
16903         }
16904         
16905         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16906             
16907             event.preventDefault();
16908             // this is very hacky as keydown always get's upper case.
16909             
16910             var cc = String.fromCharCode(event.getCharCode());
16911             
16912             
16913             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16914             
16915         }
16916         
16917         
16918     }
16919 });/*
16920  * Based on:
16921  * Ext JS Library 1.1.1
16922  * Copyright(c) 2006-2007, Ext JS, LLC.
16923  *
16924  * Originally Released Under LGPL - original licence link has changed is not relivant.
16925  *
16926  * Fork - LGPL
16927  * <script type="text/javascript">
16928  */
16929  
16930 /**
16931  * @class Roo.form.Hidden
16932  * @extends Roo.form.TextField
16933  * Simple Hidden element used on forms 
16934  * 
16935  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16936  * 
16937  * @constructor
16938  * Creates a new Hidden form element.
16939  * @param {Object} config Configuration options
16940  */
16941
16942
16943
16944 // easy hidden field...
16945 Roo.form.Hidden = function(config){
16946     Roo.form.Hidden.superclass.constructor.call(this, config);
16947 };
16948   
16949 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16950     fieldLabel:      '',
16951     inputType:      'hidden',
16952     width:          50,
16953     allowBlank:     true,
16954     labelSeparator: '',
16955     hidden:         true,
16956     itemCls :       'x-form-item-display-none'
16957
16958
16959 });
16960
16961
16962 /*
16963  * Based on:
16964  * Ext JS Library 1.1.1
16965  * Copyright(c) 2006-2007, Ext JS, LLC.
16966  *
16967  * Originally Released Under LGPL - original licence link has changed is not relivant.
16968  *
16969  * Fork - LGPL
16970  * <script type="text/javascript">
16971  */
16972  
16973 /**
16974  * @class Roo.form.TriggerField
16975  * @extends Roo.form.TextField
16976  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16977  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16978  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16979  * for which you can provide a custom implementation.  For example:
16980  * <pre><code>
16981 var trigger = new Roo.form.TriggerField();
16982 trigger.onTriggerClick = myTriggerFn;
16983 trigger.applyTo('my-field');
16984 </code></pre>
16985  *
16986  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16987  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16988  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16989  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16990  * @constructor
16991  * Create a new TriggerField.
16992  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16993  * to the base TextField)
16994  */
16995 Roo.form.TriggerField = function(config){
16996     this.mimicing = false;
16997     Roo.form.TriggerField.superclass.constructor.call(this, config);
16998 };
16999
17000 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17001     /**
17002      * @cfg {String} triggerClass A CSS class to apply to the trigger
17003      */
17004     /**
17005      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17006      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17007      */
17008     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17009     /**
17010      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17011      */
17012     hideTrigger:false,
17013
17014     /** @cfg {Boolean} grow @hide */
17015     /** @cfg {Number} growMin @hide */
17016     /** @cfg {Number} growMax @hide */
17017
17018     /**
17019      * @hide 
17020      * @method
17021      */
17022     autoSize: Roo.emptyFn,
17023     // private
17024     monitorTab : true,
17025     // private
17026     deferHeight : true,
17027
17028     
17029     actionMode : 'wrap',
17030     // private
17031     onResize : function(w, h){
17032         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17033         if(typeof w == 'number'){
17034             var x = w - this.trigger.getWidth();
17035             this.el.setWidth(this.adjustWidth('input', x));
17036             this.trigger.setStyle('left', x+'px');
17037         }
17038     },
17039
17040     // private
17041     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17042
17043     // private
17044     getResizeEl : function(){
17045         return this.wrap;
17046     },
17047
17048     // private
17049     getPositionEl : function(){
17050         return this.wrap;
17051     },
17052
17053     // private
17054     alignErrorIcon : function(){
17055         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17056     },
17057
17058     // private
17059     onRender : function(ct, position){
17060         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17061         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17062         this.trigger = this.wrap.createChild(this.triggerConfig ||
17063                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17064         if(this.hideTrigger){
17065             this.trigger.setDisplayed(false);
17066         }
17067         this.initTrigger();
17068         if(!this.width){
17069             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17070         }
17071     },
17072
17073     // private
17074     initTrigger : function(){
17075         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17076         this.trigger.addClassOnOver('x-form-trigger-over');
17077         this.trigger.addClassOnClick('x-form-trigger-click');
17078     },
17079
17080     // private
17081     onDestroy : function(){
17082         if(this.trigger){
17083             this.trigger.removeAllListeners();
17084             this.trigger.remove();
17085         }
17086         if(this.wrap){
17087             this.wrap.remove();
17088         }
17089         Roo.form.TriggerField.superclass.onDestroy.call(this);
17090     },
17091
17092     // private
17093     onFocus : function(){
17094         Roo.form.TriggerField.superclass.onFocus.call(this);
17095         if(!this.mimicing){
17096             this.wrap.addClass('x-trigger-wrap-focus');
17097             this.mimicing = true;
17098             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17099             if(this.monitorTab){
17100                 this.el.on("keydown", this.checkTab, this);
17101             }
17102         }
17103     },
17104
17105     // private
17106     checkTab : function(e){
17107         if(e.getKey() == e.TAB){
17108             this.triggerBlur();
17109         }
17110     },
17111
17112     // private
17113     onBlur : function(){
17114         // do nothing
17115     },
17116
17117     // private
17118     mimicBlur : function(e, t){
17119         if(!this.wrap.contains(t) && this.validateBlur()){
17120             this.triggerBlur();
17121         }
17122     },
17123
17124     // private
17125     triggerBlur : function(){
17126         this.mimicing = false;
17127         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17128         if(this.monitorTab){
17129             this.el.un("keydown", this.checkTab, this);
17130         }
17131         this.wrap.removeClass('x-trigger-wrap-focus');
17132         Roo.form.TriggerField.superclass.onBlur.call(this);
17133     },
17134
17135     // private
17136     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17137     validateBlur : function(e, t){
17138         return true;
17139     },
17140
17141     // private
17142     onDisable : function(){
17143         Roo.form.TriggerField.superclass.onDisable.call(this);
17144         if(this.wrap){
17145             this.wrap.addClass('x-item-disabled');
17146         }
17147     },
17148
17149     // private
17150     onEnable : function(){
17151         Roo.form.TriggerField.superclass.onEnable.call(this);
17152         if(this.wrap){
17153             this.wrap.removeClass('x-item-disabled');
17154         }
17155     },
17156
17157     // private
17158     onShow : function(){
17159         var ae = this.getActionEl();
17160         
17161         if(ae){
17162             ae.dom.style.display = '';
17163             ae.dom.style.visibility = 'visible';
17164         }
17165     },
17166
17167     // private
17168     
17169     onHide : function(){
17170         var ae = this.getActionEl();
17171         ae.dom.style.display = 'none';
17172     },
17173
17174     /**
17175      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17176      * by an implementing function.
17177      * @method
17178      * @param {EventObject} e
17179      */
17180     onTriggerClick : Roo.emptyFn
17181 });
17182
17183 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17184 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17185 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17186 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17187     initComponent : function(){
17188         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17189
17190         this.triggerConfig = {
17191             tag:'span', cls:'x-form-twin-triggers', cn:[
17192             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17193             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17194         ]};
17195     },
17196
17197     getTrigger : function(index){
17198         return this.triggers[index];
17199     },
17200
17201     initTrigger : function(){
17202         var ts = this.trigger.select('.x-form-trigger', true);
17203         this.wrap.setStyle('overflow', 'hidden');
17204         var triggerField = this;
17205         ts.each(function(t, all, index){
17206             t.hide = function(){
17207                 var w = triggerField.wrap.getWidth();
17208                 this.dom.style.display = 'none';
17209                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17210             };
17211             t.show = function(){
17212                 var w = triggerField.wrap.getWidth();
17213                 this.dom.style.display = '';
17214                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17215             };
17216             var triggerIndex = 'Trigger'+(index+1);
17217
17218             if(this['hide'+triggerIndex]){
17219                 t.dom.style.display = 'none';
17220             }
17221             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17222             t.addClassOnOver('x-form-trigger-over');
17223             t.addClassOnClick('x-form-trigger-click');
17224         }, this);
17225         this.triggers = ts.elements;
17226     },
17227
17228     onTrigger1Click : Roo.emptyFn,
17229     onTrigger2Click : Roo.emptyFn
17230 });/*
17231  * Based on:
17232  * Ext JS Library 1.1.1
17233  * Copyright(c) 2006-2007, Ext JS, LLC.
17234  *
17235  * Originally Released Under LGPL - original licence link has changed is not relivant.
17236  *
17237  * Fork - LGPL
17238  * <script type="text/javascript">
17239  */
17240  
17241 /**
17242  * @class Roo.form.TextArea
17243  * @extends Roo.form.TextField
17244  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17245  * support for auto-sizing.
17246  * @constructor
17247  * Creates a new TextArea
17248  * @param {Object} config Configuration options
17249  */
17250 Roo.form.TextArea = function(config){
17251     Roo.form.TextArea.superclass.constructor.call(this, config);
17252     // these are provided exchanges for backwards compat
17253     // minHeight/maxHeight were replaced by growMin/growMax to be
17254     // compatible with TextField growing config values
17255     if(this.minHeight !== undefined){
17256         this.growMin = this.minHeight;
17257     }
17258     if(this.maxHeight !== undefined){
17259         this.growMax = this.maxHeight;
17260     }
17261 };
17262
17263 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17264     /**
17265      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17266      */
17267     growMin : 60,
17268     /**
17269      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17270      */
17271     growMax: 1000,
17272     /**
17273      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17274      * in the field (equivalent to setting overflow: hidden, defaults to false)
17275      */
17276     preventScrollbars: false,
17277     /**
17278      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17279      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17280      */
17281
17282     // private
17283     onRender : function(ct, position){
17284         if(!this.el){
17285             this.defaultAutoCreate = {
17286                 tag: "textarea",
17287                 style:"width:300px;height:60px;",
17288                 autocomplete: "new-password"
17289             };
17290         }
17291         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17292         if(this.grow){
17293             this.textSizeEl = Roo.DomHelper.append(document.body, {
17294                 tag: "pre", cls: "x-form-grow-sizer"
17295             });
17296             if(this.preventScrollbars){
17297                 this.el.setStyle("overflow", "hidden");
17298             }
17299             this.el.setHeight(this.growMin);
17300         }
17301     },
17302
17303     onDestroy : function(){
17304         if(this.textSizeEl){
17305             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17306         }
17307         Roo.form.TextArea.superclass.onDestroy.call(this);
17308     },
17309
17310     // private
17311     onKeyUp : function(e){
17312         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17313             this.autoSize();
17314         }
17315     },
17316
17317     /**
17318      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17319      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17320      */
17321     autoSize : function(){
17322         if(!this.grow || !this.textSizeEl){
17323             return;
17324         }
17325         var el = this.el;
17326         var v = el.dom.value;
17327         var ts = this.textSizeEl;
17328
17329         ts.innerHTML = '';
17330         ts.appendChild(document.createTextNode(v));
17331         v = ts.innerHTML;
17332
17333         Roo.fly(ts).setWidth(this.el.getWidth());
17334         if(v.length < 1){
17335             v = "&#160;&#160;";
17336         }else{
17337             if(Roo.isIE){
17338                 v = v.replace(/\n/g, '<p>&#160;</p>');
17339             }
17340             v += "&#160;\n&#160;";
17341         }
17342         ts.innerHTML = v;
17343         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17344         if(h != this.lastHeight){
17345             this.lastHeight = h;
17346             this.el.setHeight(h);
17347             this.fireEvent("autosize", this, h);
17348         }
17349     }
17350 });/*
17351  * Based on:
17352  * Ext JS Library 1.1.1
17353  * Copyright(c) 2006-2007, Ext JS, LLC.
17354  *
17355  * Originally Released Under LGPL - original licence link has changed is not relivant.
17356  *
17357  * Fork - LGPL
17358  * <script type="text/javascript">
17359  */
17360  
17361
17362 /**
17363  * @class Roo.form.NumberField
17364  * @extends Roo.form.TextField
17365  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17366  * @constructor
17367  * Creates a new NumberField
17368  * @param {Object} config Configuration options
17369  */
17370 Roo.form.NumberField = function(config){
17371     Roo.form.NumberField.superclass.constructor.call(this, config);
17372 };
17373
17374 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17375     /**
17376      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17377      */
17378     fieldClass: "x-form-field x-form-num-field",
17379     /**
17380      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17381      */
17382     allowDecimals : true,
17383     /**
17384      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17385      */
17386     decimalSeparator : ".",
17387     /**
17388      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17389      */
17390     decimalPrecision : 2,
17391     /**
17392      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17393      */
17394     allowNegative : true,
17395     /**
17396      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17397      */
17398     minValue : Number.NEGATIVE_INFINITY,
17399     /**
17400      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17401      */
17402     maxValue : Number.MAX_VALUE,
17403     /**
17404      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17405      */
17406     minText : "The minimum value for this field is {0}",
17407     /**
17408      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17409      */
17410     maxText : "The maximum value for this field is {0}",
17411     /**
17412      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17413      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17414      */
17415     nanText : "{0} is not a valid number",
17416
17417     // private
17418     initEvents : function(){
17419         Roo.form.NumberField.superclass.initEvents.call(this);
17420         var allowed = "0123456789";
17421         if(this.allowDecimals){
17422             allowed += this.decimalSeparator;
17423         }
17424         if(this.allowNegative){
17425             allowed += "-";
17426         }
17427         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17428         var keyPress = function(e){
17429             var k = e.getKey();
17430             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17431                 return;
17432             }
17433             var c = e.getCharCode();
17434             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17435                 e.stopEvent();
17436             }
17437         };
17438         this.el.on("keypress", keyPress, this);
17439     },
17440
17441     // private
17442     validateValue : function(value){
17443         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17444             return false;
17445         }
17446         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17447              return true;
17448         }
17449         var num = this.parseValue(value);
17450         if(isNaN(num)){
17451             this.markInvalid(String.format(this.nanText, value));
17452             return false;
17453         }
17454         if(num < this.minValue){
17455             this.markInvalid(String.format(this.minText, this.minValue));
17456             return false;
17457         }
17458         if(num > this.maxValue){
17459             this.markInvalid(String.format(this.maxText, this.maxValue));
17460             return false;
17461         }
17462         return true;
17463     },
17464
17465     getValue : function(){
17466         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17467     },
17468
17469     // private
17470     parseValue : function(value){
17471         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17472         return isNaN(value) ? '' : value;
17473     },
17474
17475     // private
17476     fixPrecision : function(value){
17477         var nan = isNaN(value);
17478         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17479             return nan ? '' : value;
17480         }
17481         return parseFloat(value).toFixed(this.decimalPrecision);
17482     },
17483
17484     setValue : function(v){
17485         v = this.fixPrecision(v);
17486         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17487     },
17488
17489     // private
17490     decimalPrecisionFcn : function(v){
17491         return Math.floor(v);
17492     },
17493
17494     beforeBlur : function(){
17495         var v = this.parseValue(this.getRawValue());
17496         if(v){
17497             this.setValue(v);
17498         }
17499     }
17500 });/*
17501  * Based on:
17502  * Ext JS Library 1.1.1
17503  * Copyright(c) 2006-2007, Ext JS, LLC.
17504  *
17505  * Originally Released Under LGPL - original licence link has changed is not relivant.
17506  *
17507  * Fork - LGPL
17508  * <script type="text/javascript">
17509  */
17510  
17511 /**
17512  * @class Roo.form.DateField
17513  * @extends Roo.form.TriggerField
17514  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17515 * @constructor
17516 * Create a new DateField
17517 * @param {Object} config
17518  */
17519 Roo.form.DateField = function(config)
17520 {
17521     Roo.form.DateField.superclass.constructor.call(this, config);
17522     
17523       this.addEvents({
17524          
17525         /**
17526          * @event select
17527          * Fires when a date is selected
17528              * @param {Roo.form.DateField} combo This combo box
17529              * @param {Date} date The date selected
17530              */
17531         'select' : true
17532          
17533     });
17534     
17535     
17536     if(typeof this.minValue == "string") {
17537         this.minValue = this.parseDate(this.minValue);
17538     }
17539     if(typeof this.maxValue == "string") {
17540         this.maxValue = this.parseDate(this.maxValue);
17541     }
17542     this.ddMatch = null;
17543     if(this.disabledDates){
17544         var dd = this.disabledDates;
17545         var re = "(?:";
17546         for(var i = 0; i < dd.length; i++){
17547             re += dd[i];
17548             if(i != dd.length-1) {
17549                 re += "|";
17550             }
17551         }
17552         this.ddMatch = new RegExp(re + ")");
17553     }
17554 };
17555
17556 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17557     /**
17558      * @cfg {String} format
17559      * The default date format string which can be overriden for localization support.  The format must be
17560      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17561      */
17562     format : "m/d/y",
17563     /**
17564      * @cfg {String} altFormats
17565      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17566      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17567      */
17568     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17569     /**
17570      * @cfg {Array} disabledDays
17571      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17572      */
17573     disabledDays : null,
17574     /**
17575      * @cfg {String} disabledDaysText
17576      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17577      */
17578     disabledDaysText : "Disabled",
17579     /**
17580      * @cfg {Array} disabledDates
17581      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17582      * expression so they are very powerful. Some examples:
17583      * <ul>
17584      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17585      * <li>["03/08", "09/16"] would disable those days for every year</li>
17586      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17587      * <li>["03/../2006"] would disable every day in March 2006</li>
17588      * <li>["^03"] would disable every day in every March</li>
17589      * </ul>
17590      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17591      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17592      */
17593     disabledDates : null,
17594     /**
17595      * @cfg {String} disabledDatesText
17596      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17597      */
17598     disabledDatesText : "Disabled",
17599         
17600         
17601         /**
17602      * @cfg {Date/String} zeroValue
17603      * if the date is less that this number, then the field is rendered as empty
17604      * default is 1800
17605      */
17606         zeroValue : '1800-01-01',
17607         
17608         
17609     /**
17610      * @cfg {Date/String} minValue
17611      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17612      * valid format (defaults to null).
17613      */
17614     minValue : null,
17615     /**
17616      * @cfg {Date/String} maxValue
17617      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17618      * valid format (defaults to null).
17619      */
17620     maxValue : null,
17621     /**
17622      * @cfg {String} minText
17623      * The error text to display when the date in the cell is before minValue (defaults to
17624      * 'The date in this field must be after {minValue}').
17625      */
17626     minText : "The date in this field must be equal to or after {0}",
17627     /**
17628      * @cfg {String} maxText
17629      * The error text to display when the date in the cell is after maxValue (defaults to
17630      * 'The date in this field must be before {maxValue}').
17631      */
17632     maxText : "The date in this field must be equal to or before {0}",
17633     /**
17634      * @cfg {String} invalidText
17635      * The error text to display when the date in the field is invalid (defaults to
17636      * '{value} is not a valid date - it must be in the format {format}').
17637      */
17638     invalidText : "{0} is not a valid date - it must be in the format {1}",
17639     /**
17640      * @cfg {String} triggerClass
17641      * An additional CSS class used to style the trigger button.  The trigger will always get the
17642      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17643      * which displays a calendar icon).
17644      */
17645     triggerClass : 'x-form-date-trigger',
17646     
17647
17648     /**
17649      * @cfg {Boolean} useIso
17650      * if enabled, then the date field will use a hidden field to store the 
17651      * real value as iso formated date. default (false)
17652      */ 
17653     useIso : false,
17654     /**
17655      * @cfg {String/Object} autoCreate
17656      * A DomHelper element spec, or true for a default element spec (defaults to
17657      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17658      */ 
17659     // private
17660     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17661     
17662     // private
17663     hiddenField: false,
17664     
17665     onRender : function(ct, position)
17666     {
17667         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17668         if (this.useIso) {
17669             //this.el.dom.removeAttribute('name'); 
17670             Roo.log("Changing name?");
17671             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17672             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17673                     'before', true);
17674             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17675             // prevent input submission
17676             this.hiddenName = this.name;
17677         }
17678             
17679             
17680     },
17681     
17682     // private
17683     validateValue : function(value)
17684     {
17685         value = this.formatDate(value);
17686         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17687             Roo.log('super failed');
17688             return false;
17689         }
17690         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17691              return true;
17692         }
17693         var svalue = value;
17694         value = this.parseDate(value);
17695         if(!value){
17696             Roo.log('parse date failed' + svalue);
17697             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17698             return false;
17699         }
17700         var time = value.getTime();
17701         if(this.minValue && time < this.minValue.getTime()){
17702             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17703             return false;
17704         }
17705         if(this.maxValue && time > this.maxValue.getTime()){
17706             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17707             return false;
17708         }
17709         if(this.disabledDays){
17710             var day = value.getDay();
17711             for(var i = 0; i < this.disabledDays.length; i++) {
17712                 if(day === this.disabledDays[i]){
17713                     this.markInvalid(this.disabledDaysText);
17714                     return false;
17715                 }
17716             }
17717         }
17718         var fvalue = this.formatDate(value);
17719         if(this.ddMatch && this.ddMatch.test(fvalue)){
17720             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17721             return false;
17722         }
17723         return true;
17724     },
17725
17726     // private
17727     // Provides logic to override the default TriggerField.validateBlur which just returns true
17728     validateBlur : function(){
17729         return !this.menu || !this.menu.isVisible();
17730     },
17731     
17732     getName: function()
17733     {
17734         // returns hidden if it's set..
17735         if (!this.rendered) {return ''};
17736         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17737         
17738     },
17739
17740     /**
17741      * Returns the current date value of the date field.
17742      * @return {Date} The date value
17743      */
17744     getValue : function(){
17745         
17746         return  this.hiddenField ?
17747                 this.hiddenField.value :
17748                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17749     },
17750
17751     /**
17752      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17753      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17754      * (the default format used is "m/d/y").
17755      * <br />Usage:
17756      * <pre><code>
17757 //All of these calls set the same date value (May 4, 2006)
17758
17759 //Pass a date object:
17760 var dt = new Date('5/4/06');
17761 dateField.setValue(dt);
17762
17763 //Pass a date string (default format):
17764 dateField.setValue('5/4/06');
17765
17766 //Pass a date string (custom format):
17767 dateField.format = 'Y-m-d';
17768 dateField.setValue('2006-5-4');
17769 </code></pre>
17770      * @param {String/Date} date The date or valid date string
17771      */
17772     setValue : function(date){
17773         if (this.hiddenField) {
17774             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17775         }
17776         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17777         // make sure the value field is always stored as a date..
17778         this.value = this.parseDate(date);
17779         
17780         
17781     },
17782
17783     // private
17784     parseDate : function(value){
17785                 
17786                 if (value instanceof Date) {
17787                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17788                                 return  '';
17789                         }
17790                         return value;
17791                 }
17792                 
17793                 
17794         if(!value || value instanceof Date){
17795             return value;
17796         }
17797         var v = Date.parseDate(value, this.format);
17798          if (!v && this.useIso) {
17799             v = Date.parseDate(value, 'Y-m-d');
17800         }
17801         if(!v && this.altFormats){
17802             if(!this.altFormatsArray){
17803                 this.altFormatsArray = this.altFormats.split("|");
17804             }
17805             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17806                 v = Date.parseDate(value, this.altFormatsArray[i]);
17807             }
17808         }
17809                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17810                         v = '';
17811                 }
17812         return v;
17813     },
17814
17815     // private
17816     formatDate : function(date, fmt){
17817         return (!date || !(date instanceof Date)) ?
17818                date : date.dateFormat(fmt || this.format);
17819     },
17820
17821     // private
17822     menuListeners : {
17823         select: function(m, d){
17824             
17825             this.setValue(d);
17826             this.fireEvent('select', this, d);
17827         },
17828         show : function(){ // retain focus styling
17829             this.onFocus();
17830         },
17831         hide : function(){
17832             this.focus.defer(10, this);
17833             var ml = this.menuListeners;
17834             this.menu.un("select", ml.select,  this);
17835             this.menu.un("show", ml.show,  this);
17836             this.menu.un("hide", ml.hide,  this);
17837         }
17838     },
17839
17840     // private
17841     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17842     onTriggerClick : function(){
17843         if(this.disabled){
17844             return;
17845         }
17846         if(this.menu == null){
17847             this.menu = new Roo.menu.DateMenu();
17848         }
17849         Roo.apply(this.menu.picker,  {
17850             showClear: this.allowBlank,
17851             minDate : this.minValue,
17852             maxDate : this.maxValue,
17853             disabledDatesRE : this.ddMatch,
17854             disabledDatesText : this.disabledDatesText,
17855             disabledDays : this.disabledDays,
17856             disabledDaysText : this.disabledDaysText,
17857             format : this.useIso ? 'Y-m-d' : this.format,
17858             minText : String.format(this.minText, this.formatDate(this.minValue)),
17859             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17860         });
17861         this.menu.on(Roo.apply({}, this.menuListeners, {
17862             scope:this
17863         }));
17864         this.menu.picker.setValue(this.getValue() || new Date());
17865         this.menu.show(this.el, "tl-bl?");
17866     },
17867
17868     beforeBlur : function(){
17869         var v = this.parseDate(this.getRawValue());
17870         if(v){
17871             this.setValue(v);
17872         }
17873     },
17874
17875     /*@
17876      * overide
17877      * 
17878      */
17879     isDirty : function() {
17880         if(this.disabled) {
17881             return false;
17882         }
17883         
17884         if(typeof(this.startValue) === 'undefined'){
17885             return false;
17886         }
17887         
17888         return String(this.getValue()) !== String(this.startValue);
17889         
17890     },
17891     // @overide
17892     cleanLeadingSpace : function(e)
17893     {
17894        return;
17895     }
17896     
17897 });/*
17898  * Based on:
17899  * Ext JS Library 1.1.1
17900  * Copyright(c) 2006-2007, Ext JS, LLC.
17901  *
17902  * Originally Released Under LGPL - original licence link has changed is not relivant.
17903  *
17904  * Fork - LGPL
17905  * <script type="text/javascript">
17906  */
17907  
17908 /**
17909  * @class Roo.form.MonthField
17910  * @extends Roo.form.TriggerField
17911  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17912 * @constructor
17913 * Create a new MonthField
17914 * @param {Object} config
17915  */
17916 Roo.form.MonthField = function(config){
17917     
17918     Roo.form.MonthField.superclass.constructor.call(this, config);
17919     
17920       this.addEvents({
17921          
17922         /**
17923          * @event select
17924          * Fires when a date is selected
17925              * @param {Roo.form.MonthFieeld} combo This combo box
17926              * @param {Date} date The date selected
17927              */
17928         'select' : true
17929          
17930     });
17931     
17932     
17933     if(typeof this.minValue == "string") {
17934         this.minValue = this.parseDate(this.minValue);
17935     }
17936     if(typeof this.maxValue == "string") {
17937         this.maxValue = this.parseDate(this.maxValue);
17938     }
17939     this.ddMatch = null;
17940     if(this.disabledDates){
17941         var dd = this.disabledDates;
17942         var re = "(?:";
17943         for(var i = 0; i < dd.length; i++){
17944             re += dd[i];
17945             if(i != dd.length-1) {
17946                 re += "|";
17947             }
17948         }
17949         this.ddMatch = new RegExp(re + ")");
17950     }
17951 };
17952
17953 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17954     /**
17955      * @cfg {String} format
17956      * The default date format string which can be overriden for localization support.  The format must be
17957      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17958      */
17959     format : "M Y",
17960     /**
17961      * @cfg {String} altFormats
17962      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17963      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17964      */
17965     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17966     /**
17967      * @cfg {Array} disabledDays
17968      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17969      */
17970     disabledDays : [0,1,2,3,4,5,6],
17971     /**
17972      * @cfg {String} disabledDaysText
17973      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17974      */
17975     disabledDaysText : "Disabled",
17976     /**
17977      * @cfg {Array} disabledDates
17978      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17979      * expression so they are very powerful. Some examples:
17980      * <ul>
17981      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17982      * <li>["03/08", "09/16"] would disable those days for every year</li>
17983      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17984      * <li>["03/../2006"] would disable every day in March 2006</li>
17985      * <li>["^03"] would disable every day in every March</li>
17986      * </ul>
17987      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17988      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17989      */
17990     disabledDates : null,
17991     /**
17992      * @cfg {String} disabledDatesText
17993      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17994      */
17995     disabledDatesText : "Disabled",
17996     /**
17997      * @cfg {Date/String} minValue
17998      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17999      * valid format (defaults to null).
18000      */
18001     minValue : null,
18002     /**
18003      * @cfg {Date/String} maxValue
18004      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18005      * valid format (defaults to null).
18006      */
18007     maxValue : null,
18008     /**
18009      * @cfg {String} minText
18010      * The error text to display when the date in the cell is before minValue (defaults to
18011      * 'The date in this field must be after {minValue}').
18012      */
18013     minText : "The date in this field must be equal to or after {0}",
18014     /**
18015      * @cfg {String} maxTextf
18016      * The error text to display when the date in the cell is after maxValue (defaults to
18017      * 'The date in this field must be before {maxValue}').
18018      */
18019     maxText : "The date in this field must be equal to or before {0}",
18020     /**
18021      * @cfg {String} invalidText
18022      * The error text to display when the date in the field is invalid (defaults to
18023      * '{value} is not a valid date - it must be in the format {format}').
18024      */
18025     invalidText : "{0} is not a valid date - it must be in the format {1}",
18026     /**
18027      * @cfg {String} triggerClass
18028      * An additional CSS class used to style the trigger button.  The trigger will always get the
18029      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18030      * which displays a calendar icon).
18031      */
18032     triggerClass : 'x-form-date-trigger',
18033     
18034
18035     /**
18036      * @cfg {Boolean} useIso
18037      * if enabled, then the date field will use a hidden field to store the 
18038      * real value as iso formated date. default (true)
18039      */ 
18040     useIso : true,
18041     /**
18042      * @cfg {String/Object} autoCreate
18043      * A DomHelper element spec, or true for a default element spec (defaults to
18044      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18045      */ 
18046     // private
18047     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18048     
18049     // private
18050     hiddenField: false,
18051     
18052     hideMonthPicker : false,
18053     
18054     onRender : function(ct, position)
18055     {
18056         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18057         if (this.useIso) {
18058             this.el.dom.removeAttribute('name'); 
18059             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18060                     'before', true);
18061             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18062             // prevent input submission
18063             this.hiddenName = this.name;
18064         }
18065             
18066             
18067     },
18068     
18069     // private
18070     validateValue : function(value)
18071     {
18072         value = this.formatDate(value);
18073         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18074             return false;
18075         }
18076         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18077              return true;
18078         }
18079         var svalue = value;
18080         value = this.parseDate(value);
18081         if(!value){
18082             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18083             return false;
18084         }
18085         var time = value.getTime();
18086         if(this.minValue && time < this.minValue.getTime()){
18087             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18088             return false;
18089         }
18090         if(this.maxValue && time > this.maxValue.getTime()){
18091             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18092             return false;
18093         }
18094         /*if(this.disabledDays){
18095             var day = value.getDay();
18096             for(var i = 0; i < this.disabledDays.length; i++) {
18097                 if(day === this.disabledDays[i]){
18098                     this.markInvalid(this.disabledDaysText);
18099                     return false;
18100                 }
18101             }
18102         }
18103         */
18104         var fvalue = this.formatDate(value);
18105         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18106             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18107             return false;
18108         }
18109         */
18110         return true;
18111     },
18112
18113     // private
18114     // Provides logic to override the default TriggerField.validateBlur which just returns true
18115     validateBlur : function(){
18116         return !this.menu || !this.menu.isVisible();
18117     },
18118
18119     /**
18120      * Returns the current date value of the date field.
18121      * @return {Date} The date value
18122      */
18123     getValue : function(){
18124         
18125         
18126         
18127         return  this.hiddenField ?
18128                 this.hiddenField.value :
18129                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18130     },
18131
18132     /**
18133      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18134      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18135      * (the default format used is "m/d/y").
18136      * <br />Usage:
18137      * <pre><code>
18138 //All of these calls set the same date value (May 4, 2006)
18139
18140 //Pass a date object:
18141 var dt = new Date('5/4/06');
18142 monthField.setValue(dt);
18143
18144 //Pass a date string (default format):
18145 monthField.setValue('5/4/06');
18146
18147 //Pass a date string (custom format):
18148 monthField.format = 'Y-m-d';
18149 monthField.setValue('2006-5-4');
18150 </code></pre>
18151      * @param {String/Date} date The date or valid date string
18152      */
18153     setValue : function(date){
18154         Roo.log('month setValue' + date);
18155         // can only be first of month..
18156         
18157         var val = this.parseDate(date);
18158         
18159         if (this.hiddenField) {
18160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18161         }
18162         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18163         this.value = this.parseDate(date);
18164     },
18165
18166     // private
18167     parseDate : function(value){
18168         if(!value || value instanceof Date){
18169             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18170             return value;
18171         }
18172         var v = Date.parseDate(value, this.format);
18173         if (!v && this.useIso) {
18174             v = Date.parseDate(value, 'Y-m-d');
18175         }
18176         if (v) {
18177             // 
18178             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18179         }
18180         
18181         
18182         if(!v && this.altFormats){
18183             if(!this.altFormatsArray){
18184                 this.altFormatsArray = this.altFormats.split("|");
18185             }
18186             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18187                 v = Date.parseDate(value, this.altFormatsArray[i]);
18188             }
18189         }
18190         return v;
18191     },
18192
18193     // private
18194     formatDate : function(date, fmt){
18195         return (!date || !(date instanceof Date)) ?
18196                date : date.dateFormat(fmt || this.format);
18197     },
18198
18199     // private
18200     menuListeners : {
18201         select: function(m, d){
18202             this.setValue(d);
18203             this.fireEvent('select', this, d);
18204         },
18205         show : function(){ // retain focus styling
18206             this.onFocus();
18207         },
18208         hide : function(){
18209             this.focus.defer(10, this);
18210             var ml = this.menuListeners;
18211             this.menu.un("select", ml.select,  this);
18212             this.menu.un("show", ml.show,  this);
18213             this.menu.un("hide", ml.hide,  this);
18214         }
18215     },
18216     // private
18217     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18218     onTriggerClick : function(){
18219         if(this.disabled){
18220             return;
18221         }
18222         if(this.menu == null){
18223             this.menu = new Roo.menu.DateMenu();
18224            
18225         }
18226         
18227         Roo.apply(this.menu.picker,  {
18228             
18229             showClear: this.allowBlank,
18230             minDate : this.minValue,
18231             maxDate : this.maxValue,
18232             disabledDatesRE : this.ddMatch,
18233             disabledDatesText : this.disabledDatesText,
18234             
18235             format : this.useIso ? 'Y-m-d' : this.format,
18236             minText : String.format(this.minText, this.formatDate(this.minValue)),
18237             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18238             
18239         });
18240          this.menu.on(Roo.apply({}, this.menuListeners, {
18241             scope:this
18242         }));
18243        
18244         
18245         var m = this.menu;
18246         var p = m.picker;
18247         
18248         // hide month picker get's called when we called by 'before hide';
18249         
18250         var ignorehide = true;
18251         p.hideMonthPicker  = function(disableAnim){
18252             if (ignorehide) {
18253                 return;
18254             }
18255              if(this.monthPicker){
18256                 Roo.log("hideMonthPicker called");
18257                 if(disableAnim === true){
18258                     this.monthPicker.hide();
18259                 }else{
18260                     this.monthPicker.slideOut('t', {duration:.2});
18261                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18262                     p.fireEvent("select", this, this.value);
18263                     m.hide();
18264                 }
18265             }
18266         }
18267         
18268         Roo.log('picker set value');
18269         Roo.log(this.getValue());
18270         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18271         m.show(this.el, 'tl-bl?');
18272         ignorehide  = false;
18273         // this will trigger hideMonthPicker..
18274         
18275         
18276         // hidden the day picker
18277         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18278         
18279         
18280         
18281       
18282         
18283         p.showMonthPicker.defer(100, p);
18284     
18285         
18286        
18287     },
18288
18289     beforeBlur : function(){
18290         var v = this.parseDate(this.getRawValue());
18291         if(v){
18292             this.setValue(v);
18293         }
18294     }
18295
18296     /** @cfg {Boolean} grow @hide */
18297     /** @cfg {Number} growMin @hide */
18298     /** @cfg {Number} growMax @hide */
18299     /**
18300      * @hide
18301      * @method autoSize
18302      */
18303 });/*
18304  * Based on:
18305  * Ext JS Library 1.1.1
18306  * Copyright(c) 2006-2007, Ext JS, LLC.
18307  *
18308  * Originally Released Under LGPL - original licence link has changed is not relivant.
18309  *
18310  * Fork - LGPL
18311  * <script type="text/javascript">
18312  */
18313  
18314
18315 /**
18316  * @class Roo.form.ComboBox
18317  * @extends Roo.form.TriggerField
18318  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18319  * @constructor
18320  * Create a new ComboBox.
18321  * @param {Object} config Configuration options
18322  */
18323 Roo.form.ComboBox = function(config){
18324     Roo.form.ComboBox.superclass.constructor.call(this, config);
18325     this.addEvents({
18326         /**
18327          * @event expand
18328          * Fires when the dropdown list is expanded
18329              * @param {Roo.form.ComboBox} combo This combo box
18330              */
18331         'expand' : true,
18332         /**
18333          * @event collapse
18334          * Fires when the dropdown list is collapsed
18335              * @param {Roo.form.ComboBox} combo This combo box
18336              */
18337         'collapse' : true,
18338         /**
18339          * @event beforeselect
18340          * Fires before a list item is selected. Return false to cancel the selection.
18341              * @param {Roo.form.ComboBox} combo This combo box
18342              * @param {Roo.data.Record} record The data record returned from the underlying store
18343              * @param {Number} index The index of the selected item in the dropdown list
18344              */
18345         'beforeselect' : true,
18346         /**
18347          * @event select
18348          * Fires when a list item is selected
18349              * @param {Roo.form.ComboBox} combo This combo box
18350              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18351              * @param {Number} index The index of the selected item in the dropdown list
18352              */
18353         'select' : true,
18354         /**
18355          * @event beforequery
18356          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18357          * The event object passed has these properties:
18358              * @param {Roo.form.ComboBox} combo This combo box
18359              * @param {String} query The query
18360              * @param {Boolean} forceAll true to force "all" query
18361              * @param {Boolean} cancel true to cancel the query
18362              * @param {Object} e The query event object
18363              */
18364         'beforequery': true,
18365          /**
18366          * @event add
18367          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18368              * @param {Roo.form.ComboBox} combo This combo box
18369              */
18370         'add' : true,
18371         /**
18372          * @event edit
18373          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18374              * @param {Roo.form.ComboBox} combo This combo box
18375              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18376              */
18377         'edit' : true
18378         
18379         
18380     });
18381     if(this.transform){
18382         this.allowDomMove = false;
18383         var s = Roo.getDom(this.transform);
18384         if(!this.hiddenName){
18385             this.hiddenName = s.name;
18386         }
18387         if(!this.store){
18388             this.mode = 'local';
18389             var d = [], opts = s.options;
18390             for(var i = 0, len = opts.length;i < len; i++){
18391                 var o = opts[i];
18392                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18393                 if(o.selected) {
18394                     this.value = value;
18395                 }
18396                 d.push([value, o.text]);
18397             }
18398             this.store = new Roo.data.SimpleStore({
18399                 'id': 0,
18400                 fields: ['value', 'text'],
18401                 data : d
18402             });
18403             this.valueField = 'value';
18404             this.displayField = 'text';
18405         }
18406         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18407         if(!this.lazyRender){
18408             this.target = true;
18409             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18410             s.parentNode.removeChild(s); // remove it
18411             this.render(this.el.parentNode);
18412         }else{
18413             s.parentNode.removeChild(s); // remove it
18414         }
18415
18416     }
18417     if (this.store) {
18418         this.store = Roo.factory(this.store, Roo.data);
18419     }
18420     
18421     this.selectedIndex = -1;
18422     if(this.mode == 'local'){
18423         if(config.queryDelay === undefined){
18424             this.queryDelay = 10;
18425         }
18426         if(config.minChars === undefined){
18427             this.minChars = 0;
18428         }
18429     }
18430 };
18431
18432 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18433     /**
18434      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18435      */
18436     /**
18437      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18438      * rendering into an Roo.Editor, defaults to false)
18439      */
18440     /**
18441      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18442      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18443      */
18444     /**
18445      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18446      */
18447     /**
18448      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18449      * the dropdown list (defaults to undefined, with no header element)
18450      */
18451
18452      /**
18453      * @cfg {String/Roo.Template} tpl The template to use to render the output
18454      */
18455      
18456     // private
18457     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18458     /**
18459      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18460      */
18461     listWidth: undefined,
18462     /**
18463      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18464      * mode = 'remote' or 'text' if mode = 'local')
18465      */
18466     displayField: undefined,
18467     /**
18468      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18469      * mode = 'remote' or 'value' if mode = 'local'). 
18470      * Note: use of a valueField requires the user make a selection
18471      * in order for a value to be mapped.
18472      */
18473     valueField: undefined,
18474     
18475     
18476     /**
18477      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18478      * field's data value (defaults to the underlying DOM element's name)
18479      */
18480     hiddenName: undefined,
18481     /**
18482      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18483      */
18484     listClass: '',
18485     /**
18486      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18487      */
18488     selectedClass: 'x-combo-selected',
18489     /**
18490      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18491      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18492      * which displays a downward arrow icon).
18493      */
18494     triggerClass : 'x-form-arrow-trigger',
18495     /**
18496      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18497      */
18498     shadow:'sides',
18499     /**
18500      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18501      * anchor positions (defaults to 'tl-bl')
18502      */
18503     listAlign: 'tl-bl?',
18504     /**
18505      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18506      */
18507     maxHeight: 300,
18508     /**
18509      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18510      * query specified by the allQuery config option (defaults to 'query')
18511      */
18512     triggerAction: 'query',
18513     /**
18514      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18515      * (defaults to 4, does not apply if editable = false)
18516      */
18517     minChars : 4,
18518     /**
18519      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18520      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18521      */
18522     typeAhead: false,
18523     /**
18524      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18525      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18526      */
18527     queryDelay: 500,
18528     /**
18529      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18530      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18531      */
18532     pageSize: 0,
18533     /**
18534      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18535      * when editable = true (defaults to false)
18536      */
18537     selectOnFocus:false,
18538     /**
18539      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18540      */
18541     queryParam: 'query',
18542     /**
18543      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18544      * when mode = 'remote' (defaults to 'Loading...')
18545      */
18546     loadingText: 'Loading...',
18547     /**
18548      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18549      */
18550     resizable: false,
18551     /**
18552      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18553      */
18554     handleHeight : 8,
18555     /**
18556      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18557      * traditional select (defaults to true)
18558      */
18559     editable: true,
18560     /**
18561      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18562      */
18563     allQuery: '',
18564     /**
18565      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18566      */
18567     mode: 'remote',
18568     /**
18569      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18570      * listWidth has a higher value)
18571      */
18572     minListWidth : 70,
18573     /**
18574      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18575      * allow the user to set arbitrary text into the field (defaults to false)
18576      */
18577     forceSelection:false,
18578     /**
18579      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18580      * if typeAhead = true (defaults to 250)
18581      */
18582     typeAheadDelay : 250,
18583     /**
18584      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18585      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18586      */
18587     valueNotFoundText : undefined,
18588     /**
18589      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18590      */
18591     blockFocus : false,
18592     
18593     /**
18594      * @cfg {Boolean} disableClear Disable showing of clear button.
18595      */
18596     disableClear : false,
18597     /**
18598      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18599      */
18600     alwaysQuery : false,
18601     
18602     //private
18603     addicon : false,
18604     editicon: false,
18605     
18606     // element that contains real text value.. (when hidden is used..)
18607      
18608     // private
18609     onRender : function(ct, position)
18610     {
18611         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18612         
18613         if(this.hiddenName){
18614             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18615                     'before', true);
18616             this.hiddenField.value =
18617                 this.hiddenValue !== undefined ? this.hiddenValue :
18618                 this.value !== undefined ? this.value : '';
18619
18620             // prevent input submission
18621             this.el.dom.removeAttribute('name');
18622              
18623              
18624         }
18625         
18626         if(Roo.isGecko){
18627             this.el.dom.setAttribute('autocomplete', 'off');
18628         }
18629
18630         var cls = 'x-combo-list';
18631
18632         this.list = new Roo.Layer({
18633             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18634         });
18635
18636         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18637         this.list.setWidth(lw);
18638         this.list.swallowEvent('mousewheel');
18639         this.assetHeight = 0;
18640
18641         if(this.title){
18642             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18643             this.assetHeight += this.header.getHeight();
18644         }
18645
18646         this.innerList = this.list.createChild({cls:cls+'-inner'});
18647         this.innerList.on('mouseover', this.onViewOver, this);
18648         this.innerList.on('mousemove', this.onViewMove, this);
18649         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18650         
18651         if(this.allowBlank && !this.pageSize && !this.disableClear){
18652             this.footer = this.list.createChild({cls:cls+'-ft'});
18653             this.pageTb = new Roo.Toolbar(this.footer);
18654            
18655         }
18656         if(this.pageSize){
18657             this.footer = this.list.createChild({cls:cls+'-ft'});
18658             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18659                     {pageSize: this.pageSize});
18660             
18661         }
18662         
18663         if (this.pageTb && this.allowBlank && !this.disableClear) {
18664             var _this = this;
18665             this.pageTb.add(new Roo.Toolbar.Fill(), {
18666                 cls: 'x-btn-icon x-btn-clear',
18667                 text: '&#160;',
18668                 handler: function()
18669                 {
18670                     _this.collapse();
18671                     _this.clearValue();
18672                     _this.onSelect(false, -1);
18673                 }
18674             });
18675         }
18676         if (this.footer) {
18677             this.assetHeight += this.footer.getHeight();
18678         }
18679         
18680
18681         if(!this.tpl){
18682             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18683         }
18684
18685         this.view = new Roo.View(this.innerList, this.tpl, {
18686             singleSelect:true,
18687             store: this.store,
18688             selectedClass: this.selectedClass
18689         });
18690
18691         this.view.on('click', this.onViewClick, this);
18692
18693         this.store.on('beforeload', this.onBeforeLoad, this);
18694         this.store.on('load', this.onLoad, this);
18695         this.store.on('loadexception', this.onLoadException, this);
18696
18697         if(this.resizable){
18698             this.resizer = new Roo.Resizable(this.list,  {
18699                pinned:true, handles:'se'
18700             });
18701             this.resizer.on('resize', function(r, w, h){
18702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18703                 this.listWidth = w;
18704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18705                 this.restrictHeight();
18706             }, this);
18707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18708         }
18709         if(!this.editable){
18710             this.editable = true;
18711             this.setEditable(false);
18712         }  
18713         
18714         
18715         if (typeof(this.events.add.listeners) != 'undefined') {
18716             
18717             this.addicon = this.wrap.createChild(
18718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18719        
18720             this.addicon.on('click', function(e) {
18721                 this.fireEvent('add', this);
18722             }, this);
18723         }
18724         if (typeof(this.events.edit.listeners) != 'undefined') {
18725             
18726             this.editicon = this.wrap.createChild(
18727                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18728             if (this.addicon) {
18729                 this.editicon.setStyle('margin-left', '40px');
18730             }
18731             this.editicon.on('click', function(e) {
18732                 
18733                 // we fire even  if inothing is selected..
18734                 this.fireEvent('edit', this, this.lastData );
18735                 
18736             }, this);
18737         }
18738         
18739         
18740         
18741     },
18742
18743     // private
18744     initEvents : function(){
18745         Roo.form.ComboBox.superclass.initEvents.call(this);
18746
18747         this.keyNav = new Roo.KeyNav(this.el, {
18748             "up" : function(e){
18749                 this.inKeyMode = true;
18750                 this.selectPrev();
18751             },
18752
18753             "down" : function(e){
18754                 if(!this.isExpanded()){
18755                     this.onTriggerClick();
18756                 }else{
18757                     this.inKeyMode = true;
18758                     this.selectNext();
18759                 }
18760             },
18761
18762             "enter" : function(e){
18763                 this.onViewClick();
18764                 //return true;
18765             },
18766
18767             "esc" : function(e){
18768                 this.collapse();
18769             },
18770
18771             "tab" : function(e){
18772                 this.onViewClick(false);
18773                 this.fireEvent("specialkey", this, e);
18774                 return true;
18775             },
18776
18777             scope : this,
18778
18779             doRelay : function(foo, bar, hname){
18780                 if(hname == 'down' || this.scope.isExpanded()){
18781                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18782                 }
18783                 return true;
18784             },
18785
18786             forceKeyDown: true
18787         });
18788         this.queryDelay = Math.max(this.queryDelay || 10,
18789                 this.mode == 'local' ? 10 : 250);
18790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18791         if(this.typeAhead){
18792             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18793         }
18794         if(this.editable !== false){
18795             this.el.on("keyup", this.onKeyUp, this);
18796         }
18797         if(this.forceSelection){
18798             this.on('blur', this.doForce, this);
18799         }
18800     },
18801
18802     onDestroy : function(){
18803         if(this.view){
18804             this.view.setStore(null);
18805             this.view.el.removeAllListeners();
18806             this.view.el.remove();
18807             this.view.purgeListeners();
18808         }
18809         if(this.list){
18810             this.list.destroy();
18811         }
18812         if(this.store){
18813             this.store.un('beforeload', this.onBeforeLoad, this);
18814             this.store.un('load', this.onLoad, this);
18815             this.store.un('loadexception', this.onLoadException, this);
18816         }
18817         Roo.form.ComboBox.superclass.onDestroy.call(this);
18818     },
18819
18820     // private
18821     fireKey : function(e){
18822         if(e.isNavKeyPress() && !this.list.isVisible()){
18823             this.fireEvent("specialkey", this, e);
18824         }
18825     },
18826
18827     // private
18828     onResize: function(w, h){
18829         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18830         
18831         if(typeof w != 'number'){
18832             // we do not handle it!?!?
18833             return;
18834         }
18835         var tw = this.trigger.getWidth();
18836         tw += this.addicon ? this.addicon.getWidth() : 0;
18837         tw += this.editicon ? this.editicon.getWidth() : 0;
18838         var x = w - tw;
18839         this.el.setWidth( this.adjustWidth('input', x));
18840             
18841         this.trigger.setStyle('left', x+'px');
18842         
18843         if(this.list && this.listWidth === undefined){
18844             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18845             this.list.setWidth(lw);
18846             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18847         }
18848         
18849     
18850         
18851     },
18852
18853     /**
18854      * Allow or prevent the user from directly editing the field text.  If false is passed,
18855      * the user will only be able to select from the items defined in the dropdown list.  This method
18856      * is the runtime equivalent of setting the 'editable' config option at config time.
18857      * @param {Boolean} value True to allow the user to directly edit the field text
18858      */
18859     setEditable : function(value){
18860         if(value == this.editable){
18861             return;
18862         }
18863         this.editable = value;
18864         if(!value){
18865             this.el.dom.setAttribute('readOnly', true);
18866             this.el.on('mousedown', this.onTriggerClick,  this);
18867             this.el.addClass('x-combo-noedit');
18868         }else{
18869             this.el.dom.setAttribute('readOnly', false);
18870             this.el.un('mousedown', this.onTriggerClick,  this);
18871             this.el.removeClass('x-combo-noedit');
18872         }
18873     },
18874
18875     // private
18876     onBeforeLoad : function(){
18877         if(!this.hasFocus){
18878             return;
18879         }
18880         this.innerList.update(this.loadingText ?
18881                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18882         this.restrictHeight();
18883         this.selectedIndex = -1;
18884     },
18885
18886     // private
18887     onLoad : function(){
18888         if(!this.hasFocus){
18889             return;
18890         }
18891         if(this.store.getCount() > 0){
18892             this.expand();
18893             this.restrictHeight();
18894             if(this.lastQuery == this.allQuery){
18895                 if(this.editable){
18896                     this.el.dom.select();
18897                 }
18898                 if(!this.selectByValue(this.value, true)){
18899                     this.select(0, true);
18900                 }
18901             }else{
18902                 this.selectNext();
18903                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18904                     this.taTask.delay(this.typeAheadDelay);
18905                 }
18906             }
18907         }else{
18908             this.onEmptyResults();
18909         }
18910         //this.el.focus();
18911     },
18912     // private
18913     onLoadException : function()
18914     {
18915         this.collapse();
18916         Roo.log(this.store.reader.jsonData);
18917         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18918             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18919         }
18920         
18921         
18922     },
18923     // private
18924     onTypeAhead : function(){
18925         if(this.store.getCount() > 0){
18926             var r = this.store.getAt(0);
18927             var newValue = r.data[this.displayField];
18928             var len = newValue.length;
18929             var selStart = this.getRawValue().length;
18930             if(selStart != len){
18931                 this.setRawValue(newValue);
18932                 this.selectText(selStart, newValue.length);
18933             }
18934         }
18935     },
18936
18937     // private
18938     onSelect : function(record, index){
18939         if(this.fireEvent('beforeselect', this, record, index) !== false){
18940             this.setFromData(index > -1 ? record.data : false);
18941             this.collapse();
18942             this.fireEvent('select', this, record, index);
18943         }
18944     },
18945
18946     /**
18947      * Returns the currently selected field value or empty string if no value is set.
18948      * @return {String} value The selected value
18949      */
18950     getValue : function(){
18951         if(this.valueField){
18952             return typeof this.value != 'undefined' ? this.value : '';
18953         }
18954         return Roo.form.ComboBox.superclass.getValue.call(this);
18955     },
18956
18957     /**
18958      * Clears any text/value currently set in the field
18959      */
18960     clearValue : function(){
18961         if(this.hiddenField){
18962             this.hiddenField.value = '';
18963         }
18964         this.value = '';
18965         this.setRawValue('');
18966         this.lastSelectionText = '';
18967         
18968     },
18969
18970     /**
18971      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18972      * will be displayed in the field.  If the value does not match the data value of an existing item,
18973      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18974      * Otherwise the field will be blank (although the value will still be set).
18975      * @param {String} value The value to match
18976      */
18977     setValue : function(v){
18978         var text = v;
18979         if(this.valueField){
18980             var r = this.findRecord(this.valueField, v);
18981             if(r){
18982                 text = r.data[this.displayField];
18983             }else if(this.valueNotFoundText !== undefined){
18984                 text = this.valueNotFoundText;
18985             }
18986         }
18987         this.lastSelectionText = text;
18988         if(this.hiddenField){
18989             this.hiddenField.value = v;
18990         }
18991         Roo.form.ComboBox.superclass.setValue.call(this, text);
18992         this.value = v;
18993     },
18994     /**
18995      * @property {Object} the last set data for the element
18996      */
18997     
18998     lastData : false,
18999     /**
19000      * Sets the value of the field based on a object which is related to the record format for the store.
19001      * @param {Object} value the value to set as. or false on reset?
19002      */
19003     setFromData : function(o){
19004         var dv = ''; // display value
19005         var vv = ''; // value value..
19006         this.lastData = o;
19007         if (this.displayField) {
19008             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19009         } else {
19010             // this is an error condition!!!
19011             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19012         }
19013         
19014         if(this.valueField){
19015             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19016         }
19017         if(this.hiddenField){
19018             this.hiddenField.value = vv;
19019             
19020             this.lastSelectionText = dv;
19021             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19022             this.value = vv;
19023             return;
19024         }
19025         // no hidden field.. - we store the value in 'value', but still display
19026         // display field!!!!
19027         this.lastSelectionText = dv;
19028         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19029         this.value = vv;
19030         
19031         
19032     },
19033     // private
19034     reset : function(){
19035         // overridden so that last data is reset..
19036         this.setValue(this.resetValue);
19037         this.originalValue = this.getValue();
19038         this.clearInvalid();
19039         this.lastData = false;
19040         if (this.view) {
19041             this.view.clearSelections();
19042         }
19043     },
19044     // private
19045     findRecord : function(prop, value){
19046         var record;
19047         if(this.store.getCount() > 0){
19048             this.store.each(function(r){
19049                 if(r.data[prop] == value){
19050                     record = r;
19051                     return false;
19052                 }
19053                 return true;
19054             });
19055         }
19056         return record;
19057     },
19058     
19059     getName: function()
19060     {
19061         // returns hidden if it's set..
19062         if (!this.rendered) {return ''};
19063         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19064         
19065     },
19066     // private
19067     onViewMove : function(e, t){
19068         this.inKeyMode = false;
19069     },
19070
19071     // private
19072     onViewOver : function(e, t){
19073         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19074             return;
19075         }
19076         var item = this.view.findItemFromChild(t);
19077         if(item){
19078             var index = this.view.indexOf(item);
19079             this.select(index, false);
19080         }
19081     },
19082
19083     // private
19084     onViewClick : function(doFocus)
19085     {
19086         var index = this.view.getSelectedIndexes()[0];
19087         var r = this.store.getAt(index);
19088         if(r){
19089             this.onSelect(r, index);
19090         }
19091         if(doFocus !== false && !this.blockFocus){
19092             this.el.focus();
19093         }
19094     },
19095
19096     // private
19097     restrictHeight : function(){
19098         this.innerList.dom.style.height = '';
19099         var inner = this.innerList.dom;
19100         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19101         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19102         this.list.beginUpdate();
19103         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19104         this.list.alignTo(this.el, this.listAlign);
19105         this.list.endUpdate();
19106     },
19107
19108     // private
19109     onEmptyResults : function(){
19110         this.collapse();
19111     },
19112
19113     /**
19114      * Returns true if the dropdown list is expanded, else false.
19115      */
19116     isExpanded : function(){
19117         return this.list.isVisible();
19118     },
19119
19120     /**
19121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19123      * @param {String} value The data value of the item to select
19124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19125      * selected item if it is not currently in view (defaults to true)
19126      * @return {Boolean} True if the value matched an item in the list, else false
19127      */
19128     selectByValue : function(v, scrollIntoView){
19129         if(v !== undefined && v !== null){
19130             var r = this.findRecord(this.valueField || this.displayField, v);
19131             if(r){
19132                 this.select(this.store.indexOf(r), scrollIntoView);
19133                 return true;
19134             }
19135         }
19136         return false;
19137     },
19138
19139     /**
19140      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19142      * @param {Number} index The zero-based index of the list item to select
19143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19144      * selected item if it is not currently in view (defaults to true)
19145      */
19146     select : function(index, scrollIntoView){
19147         this.selectedIndex = index;
19148         this.view.select(index);
19149         if(scrollIntoView !== false){
19150             var el = this.view.getNode(index);
19151             if(el){
19152                 this.innerList.scrollChildIntoView(el, false);
19153             }
19154         }
19155     },
19156
19157     // private
19158     selectNext : function(){
19159         var ct = this.store.getCount();
19160         if(ct > 0){
19161             if(this.selectedIndex == -1){
19162                 this.select(0);
19163             }else if(this.selectedIndex < ct-1){
19164                 this.select(this.selectedIndex+1);
19165             }
19166         }
19167     },
19168
19169     // private
19170     selectPrev : function(){
19171         var ct = this.store.getCount();
19172         if(ct > 0){
19173             if(this.selectedIndex == -1){
19174                 this.select(0);
19175             }else if(this.selectedIndex != 0){
19176                 this.select(this.selectedIndex-1);
19177             }
19178         }
19179     },
19180
19181     // private
19182     onKeyUp : function(e){
19183         if(this.editable !== false && !e.isSpecialKey()){
19184             this.lastKey = e.getKey();
19185             this.dqTask.delay(this.queryDelay);
19186         }
19187     },
19188
19189     // private
19190     validateBlur : function(){
19191         return !this.list || !this.list.isVisible();   
19192     },
19193
19194     // private
19195     initQuery : function(){
19196         this.doQuery(this.getRawValue());
19197     },
19198
19199     // private
19200     doForce : function(){
19201         if(this.el.dom.value.length > 0){
19202             this.el.dom.value =
19203                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19204              
19205         }
19206     },
19207
19208     /**
19209      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19210      * query allowing the query action to be canceled if needed.
19211      * @param {String} query The SQL query to execute
19212      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19213      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19214      * saved in the current store (defaults to false)
19215      */
19216     doQuery : function(q, forceAll){
19217         if(q === undefined || q === null){
19218             q = '';
19219         }
19220         var qe = {
19221             query: q,
19222             forceAll: forceAll,
19223             combo: this,
19224             cancel:false
19225         };
19226         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19227             return false;
19228         }
19229         q = qe.query;
19230         forceAll = qe.forceAll;
19231         if(forceAll === true || (q.length >= this.minChars)){
19232             if(this.lastQuery != q || this.alwaysQuery){
19233                 this.lastQuery = q;
19234                 if(this.mode == 'local'){
19235                     this.selectedIndex = -1;
19236                     if(forceAll){
19237                         this.store.clearFilter();
19238                     }else{
19239                         this.store.filter(this.displayField, q);
19240                     }
19241                     this.onLoad();
19242                 }else{
19243                     this.store.baseParams[this.queryParam] = q;
19244                     this.store.load({
19245                         params: this.getParams(q)
19246                     });
19247                     this.expand();
19248                 }
19249             }else{
19250                 this.selectedIndex = -1;
19251                 this.onLoad();   
19252             }
19253         }
19254     },
19255
19256     // private
19257     getParams : function(q){
19258         var p = {};
19259         //p[this.queryParam] = q;
19260         if(this.pageSize){
19261             p.start = 0;
19262             p.limit = this.pageSize;
19263         }
19264         return p;
19265     },
19266
19267     /**
19268      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19269      */
19270     collapse : function(){
19271         if(!this.isExpanded()){
19272             return;
19273         }
19274         this.list.hide();
19275         Roo.get(document).un('mousedown', this.collapseIf, this);
19276         Roo.get(document).un('mousewheel', this.collapseIf, this);
19277         if (!this.editable) {
19278             Roo.get(document).un('keydown', this.listKeyPress, this);
19279         }
19280         this.fireEvent('collapse', this);
19281     },
19282
19283     // private
19284     collapseIf : function(e){
19285         if(!e.within(this.wrap) && !e.within(this.list)){
19286             this.collapse();
19287         }
19288     },
19289
19290     /**
19291      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19292      */
19293     expand : function(){
19294         if(this.isExpanded() || !this.hasFocus){
19295             return;
19296         }
19297         this.list.alignTo(this.el, this.listAlign);
19298         this.list.show();
19299         Roo.get(document).on('mousedown', this.collapseIf, this);
19300         Roo.get(document).on('mousewheel', this.collapseIf, this);
19301         if (!this.editable) {
19302             Roo.get(document).on('keydown', this.listKeyPress, this);
19303         }
19304         
19305         this.fireEvent('expand', this);
19306     },
19307
19308     // private
19309     // Implements the default empty TriggerField.onTriggerClick function
19310     onTriggerClick : function(){
19311         if(this.disabled){
19312             return;
19313         }
19314         if(this.isExpanded()){
19315             this.collapse();
19316             if (!this.blockFocus) {
19317                 this.el.focus();
19318             }
19319             
19320         }else {
19321             this.hasFocus = true;
19322             if(this.triggerAction == 'all') {
19323                 this.doQuery(this.allQuery, true);
19324             } else {
19325                 this.doQuery(this.getRawValue());
19326             }
19327             if (!this.blockFocus) {
19328                 this.el.focus();
19329             }
19330         }
19331     },
19332     listKeyPress : function(e)
19333     {
19334         //Roo.log('listkeypress');
19335         // scroll to first matching element based on key pres..
19336         if (e.isSpecialKey()) {
19337             return false;
19338         }
19339         var k = String.fromCharCode(e.getKey()).toUpperCase();
19340         //Roo.log(k);
19341         var match  = false;
19342         var csel = this.view.getSelectedNodes();
19343         var cselitem = false;
19344         if (csel.length) {
19345             var ix = this.view.indexOf(csel[0]);
19346             cselitem  = this.store.getAt(ix);
19347             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19348                 cselitem = false;
19349             }
19350             
19351         }
19352         
19353         this.store.each(function(v) { 
19354             if (cselitem) {
19355                 // start at existing selection.
19356                 if (cselitem.id == v.id) {
19357                     cselitem = false;
19358                 }
19359                 return;
19360             }
19361                 
19362             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19363                 match = this.store.indexOf(v);
19364                 return false;
19365             }
19366         }, this);
19367         
19368         if (match === false) {
19369             return true; // no more action?
19370         }
19371         // scroll to?
19372         this.view.select(match);
19373         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19374         sn.scrollIntoView(sn.dom.parentNode, false);
19375     } 
19376
19377     /** 
19378     * @cfg {Boolean} grow 
19379     * @hide 
19380     */
19381     /** 
19382     * @cfg {Number} growMin 
19383     * @hide 
19384     */
19385     /** 
19386     * @cfg {Number} growMax 
19387     * @hide 
19388     */
19389     /**
19390      * @hide
19391      * @method autoSize
19392      */
19393 });/*
19394  * Copyright(c) 2010-2012, Roo J Solutions Limited
19395  *
19396  * Licence LGPL
19397  *
19398  */
19399
19400 /**
19401  * @class Roo.form.ComboBoxArray
19402  * @extends Roo.form.TextField
19403  * A facebook style adder... for lists of email / people / countries  etc...
19404  * pick multiple items from a combo box, and shows each one.
19405  *
19406  *  Fred [x]  Brian [x]  [Pick another |v]
19407  *
19408  *
19409  *  For this to work: it needs various extra information
19410  *    - normal combo problay has
19411  *      name, hiddenName
19412  *    + displayField, valueField
19413  *
19414  *    For our purpose...
19415  *
19416  *
19417  *   If we change from 'extends' to wrapping...
19418  *   
19419  *  
19420  *
19421  
19422  
19423  * @constructor
19424  * Create a new ComboBoxArray.
19425  * @param {Object} config Configuration options
19426  */
19427  
19428
19429 Roo.form.ComboBoxArray = function(config)
19430 {
19431     this.addEvents({
19432         /**
19433          * @event beforeremove
19434          * Fires before remove the value from the list
19435              * @param {Roo.form.ComboBoxArray} _self This combo box array
19436              * @param {Roo.form.ComboBoxArray.Item} item removed item
19437              */
19438         'beforeremove' : true,
19439         /**
19440          * @event remove
19441          * Fires when remove the value from the list
19442              * @param {Roo.form.ComboBoxArray} _self This combo box array
19443              * @param {Roo.form.ComboBoxArray.Item} item removed item
19444              */
19445         'remove' : true
19446         
19447         
19448     });
19449     
19450     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19451     
19452     this.items = new Roo.util.MixedCollection(false);
19453     
19454     // construct the child combo...
19455     
19456     
19457     
19458     
19459    
19460     
19461 }
19462
19463  
19464 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19465
19466     /**
19467      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19468      */
19469     
19470     lastData : false,
19471     
19472     // behavies liek a hiddne field
19473     inputType:      'hidden',
19474     /**
19475      * @cfg {Number} width The width of the box that displays the selected element
19476      */ 
19477     width:          300,
19478
19479     
19480     
19481     /**
19482      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19483      */
19484     name : false,
19485     /**
19486      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19487      */
19488     hiddenName : false,
19489       /**
19490      * @cfg {String} seperator    The value seperator normally ',' 
19491      */
19492     seperator : ',',
19493     
19494     // private the array of items that are displayed..
19495     items  : false,
19496     // private - the hidden field el.
19497     hiddenEl : false,
19498     // private - the filed el..
19499     el : false,
19500     
19501     //validateValue : function() { return true; }, // all values are ok!
19502     //onAddClick: function() { },
19503     
19504     onRender : function(ct, position) 
19505     {
19506         
19507         // create the standard hidden element
19508         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19509         
19510         
19511         // give fake names to child combo;
19512         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19513         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19514         
19515         this.combo = Roo.factory(this.combo, Roo.form);
19516         this.combo.onRender(ct, position);
19517         if (typeof(this.combo.width) != 'undefined') {
19518             this.combo.onResize(this.combo.width,0);
19519         }
19520         
19521         this.combo.initEvents();
19522         
19523         // assigned so form know we need to do this..
19524         this.store          = this.combo.store;
19525         this.valueField     = this.combo.valueField;
19526         this.displayField   = this.combo.displayField ;
19527         
19528         
19529         this.combo.wrap.addClass('x-cbarray-grp');
19530         
19531         var cbwrap = this.combo.wrap.createChild(
19532             {tag: 'div', cls: 'x-cbarray-cb'},
19533             this.combo.el.dom
19534         );
19535         
19536              
19537         this.hiddenEl = this.combo.wrap.createChild({
19538             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19539         });
19540         this.el = this.combo.wrap.createChild({
19541             tag: 'input',  type:'hidden' , name: this.name, value : ''
19542         });
19543          //   this.el.dom.removeAttribute("name");
19544         
19545         
19546         this.outerWrap = this.combo.wrap;
19547         this.wrap = cbwrap;
19548         
19549         this.outerWrap.setWidth(this.width);
19550         this.outerWrap.dom.removeChild(this.el.dom);
19551         
19552         this.wrap.dom.appendChild(this.el.dom);
19553         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19554         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19555         
19556         this.combo.trigger.setStyle('position','relative');
19557         this.combo.trigger.setStyle('left', '0px');
19558         this.combo.trigger.setStyle('top', '2px');
19559         
19560         this.combo.el.setStyle('vertical-align', 'text-bottom');
19561         
19562         //this.trigger.setStyle('vertical-align', 'top');
19563         
19564         // this should use the code from combo really... on('add' ....)
19565         if (this.adder) {
19566             
19567         
19568             this.adder = this.outerWrap.createChild(
19569                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19570             var _t = this;
19571             this.adder.on('click', function(e) {
19572                 _t.fireEvent('adderclick', this, e);
19573             }, _t);
19574         }
19575         //var _t = this;
19576         //this.adder.on('click', this.onAddClick, _t);
19577         
19578         
19579         this.combo.on('select', function(cb, rec, ix) {
19580             this.addItem(rec.data);
19581             
19582             cb.setValue('');
19583             cb.el.dom.value = '';
19584             //cb.lastData = rec.data;
19585             // add to list
19586             
19587         }, this);
19588         
19589         
19590     },
19591     
19592     
19593     getName: function()
19594     {
19595         // returns hidden if it's set..
19596         if (!this.rendered) {return ''};
19597         return  this.hiddenName ? this.hiddenName : this.name;
19598         
19599     },
19600     
19601     
19602     onResize: function(w, h){
19603         
19604         return;
19605         // not sure if this is needed..
19606         //this.combo.onResize(w,h);
19607         
19608         if(typeof w != 'number'){
19609             // we do not handle it!?!?
19610             return;
19611         }
19612         var tw = this.combo.trigger.getWidth();
19613         tw += this.addicon ? this.addicon.getWidth() : 0;
19614         tw += this.editicon ? this.editicon.getWidth() : 0;
19615         var x = w - tw;
19616         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19617             
19618         this.combo.trigger.setStyle('left', '0px');
19619         
19620         if(this.list && this.listWidth === undefined){
19621             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19622             this.list.setWidth(lw);
19623             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19624         }
19625         
19626     
19627         
19628     },
19629     
19630     addItem: function(rec)
19631     {
19632         var valueField = this.combo.valueField;
19633         var displayField = this.combo.displayField;
19634         
19635         if (this.items.indexOfKey(rec[valueField]) > -1) {
19636             //console.log("GOT " + rec.data.id);
19637             return;
19638         }
19639         
19640         var x = new Roo.form.ComboBoxArray.Item({
19641             //id : rec[this.idField],
19642             data : rec,
19643             displayField : displayField ,
19644             tipField : displayField ,
19645             cb : this
19646         });
19647         // use the 
19648         this.items.add(rec[valueField],x);
19649         // add it before the element..
19650         this.updateHiddenEl();
19651         x.render(this.outerWrap, this.wrap.dom);
19652         // add the image handler..
19653     },
19654     
19655     updateHiddenEl : function()
19656     {
19657         this.validate();
19658         if (!this.hiddenEl) {
19659             return;
19660         }
19661         var ar = [];
19662         var idField = this.combo.valueField;
19663         
19664         this.items.each(function(f) {
19665             ar.push(f.data[idField]);
19666         });
19667         this.hiddenEl.dom.value = ar.join(this.seperator);
19668         this.validate();
19669     },
19670     
19671     reset : function()
19672     {
19673         this.items.clear();
19674         
19675         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19676            el.remove();
19677         });
19678         
19679         this.el.dom.value = '';
19680         if (this.hiddenEl) {
19681             this.hiddenEl.dom.value = '';
19682         }
19683         
19684     },
19685     getValue: function()
19686     {
19687         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19688     },
19689     setValue: function(v) // not a valid action - must use addItems..
19690     {
19691         
19692         this.reset();
19693          
19694         if (this.store.isLocal && (typeof(v) == 'string')) {
19695             // then we can use the store to find the values..
19696             // comma seperated at present.. this needs to allow JSON based encoding..
19697             this.hiddenEl.value  = v;
19698             var v_ar = [];
19699             Roo.each(v.split(this.seperator), function(k) {
19700                 Roo.log("CHECK " + this.valueField + ',' + k);
19701                 var li = this.store.query(this.valueField, k);
19702                 if (!li.length) {
19703                     return;
19704                 }
19705                 var add = {};
19706                 add[this.valueField] = k;
19707                 add[this.displayField] = li.item(0).data[this.displayField];
19708                 
19709                 this.addItem(add);
19710             }, this) 
19711              
19712         }
19713         if (typeof(v) == 'object' ) {
19714             // then let's assume it's an array of objects..
19715             Roo.each(v, function(l) {
19716                 var add = l;
19717                 if (typeof(l) == 'string') {
19718                     add = {};
19719                     add[this.valueField] = l;
19720                     add[this.displayField] = l
19721                 }
19722                 this.addItem(add);
19723             }, this);
19724              
19725         }
19726         
19727         
19728     },
19729     setFromData: function(v)
19730     {
19731         // this recieves an object, if setValues is called.
19732         this.reset();
19733         this.el.dom.value = v[this.displayField];
19734         this.hiddenEl.dom.value = v[this.valueField];
19735         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19736             return;
19737         }
19738         var kv = v[this.valueField];
19739         var dv = v[this.displayField];
19740         kv = typeof(kv) != 'string' ? '' : kv;
19741         dv = typeof(dv) != 'string' ? '' : dv;
19742         
19743         
19744         var keys = kv.split(this.seperator);
19745         var display = dv.split(this.seperator);
19746         for (var i = 0 ; i < keys.length; i++) {
19747             add = {};
19748             add[this.valueField] = keys[i];
19749             add[this.displayField] = display[i];
19750             this.addItem(add);
19751         }
19752       
19753         
19754     },
19755     
19756     /**
19757      * Validates the combox array value
19758      * @return {Boolean} True if the value is valid, else false
19759      */
19760     validate : function(){
19761         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19762             this.clearInvalid();
19763             return true;
19764         }
19765         return false;
19766     },
19767     
19768     validateValue : function(value){
19769         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19770         
19771     },
19772     
19773     /*@
19774      * overide
19775      * 
19776      */
19777     isDirty : function() {
19778         if(this.disabled) {
19779             return false;
19780         }
19781         
19782         try {
19783             var d = Roo.decode(String(this.originalValue));
19784         } catch (e) {
19785             return String(this.getValue()) !== String(this.originalValue);
19786         }
19787         
19788         var originalValue = [];
19789         
19790         for (var i = 0; i < d.length; i++){
19791             originalValue.push(d[i][this.valueField]);
19792         }
19793         
19794         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19795         
19796     }
19797     
19798 });
19799
19800
19801
19802 /**
19803  * @class Roo.form.ComboBoxArray.Item
19804  * @extends Roo.BoxComponent
19805  * A selected item in the list
19806  *  Fred [x]  Brian [x]  [Pick another |v]
19807  * 
19808  * @constructor
19809  * Create a new item.
19810  * @param {Object} config Configuration options
19811  */
19812  
19813 Roo.form.ComboBoxArray.Item = function(config) {
19814     config.id = Roo.id();
19815     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19816 }
19817
19818 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19819     data : {},
19820     cb: false,
19821     displayField : false,
19822     tipField : false,
19823     
19824     
19825     defaultAutoCreate : {
19826         tag: 'div',
19827         cls: 'x-cbarray-item',
19828         cn : [ 
19829             { tag: 'div' },
19830             {
19831                 tag: 'img',
19832                 width:16,
19833                 height : 16,
19834                 src : Roo.BLANK_IMAGE_URL ,
19835                 align: 'center'
19836             }
19837         ]
19838         
19839     },
19840     
19841  
19842     onRender : function(ct, position)
19843     {
19844         Roo.form.Field.superclass.onRender.call(this, ct, position);
19845         
19846         if(!this.el){
19847             var cfg = this.getAutoCreate();
19848             this.el = ct.createChild(cfg, position);
19849         }
19850         
19851         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19852         
19853         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19854             this.cb.renderer(this.data) :
19855             String.format('{0}',this.data[this.displayField]);
19856         
19857             
19858         this.el.child('div').dom.setAttribute('qtip',
19859                         String.format('{0}',this.data[this.tipField])
19860         );
19861         
19862         this.el.child('img').on('click', this.remove, this);
19863         
19864     },
19865    
19866     remove : function()
19867     {
19868         if(this.cb.disabled){
19869             return;
19870         }
19871         
19872         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19873             this.cb.items.remove(this);
19874             this.el.child('img').un('click', this.remove, this);
19875             this.el.remove();
19876             this.cb.updateHiddenEl();
19877
19878             this.cb.fireEvent('remove', this.cb, this);
19879         }
19880         
19881     }
19882 });/*
19883  * RooJS Library 1.1.1
19884  * Copyright(c) 2008-2011  Alan Knowles
19885  *
19886  * License - LGPL
19887  */
19888  
19889
19890 /**
19891  * @class Roo.form.ComboNested
19892  * @extends Roo.form.ComboBox
19893  * A combobox for that allows selection of nested items in a list,
19894  * eg.
19895  *
19896  *  Book
19897  *    -> red
19898  *    -> green
19899  *  Table
19900  *    -> square
19901  *      ->red
19902  *      ->green
19903  *    -> rectangle
19904  *      ->green
19905  *      
19906  * 
19907  * @constructor
19908  * Create a new ComboNested
19909  * @param {Object} config Configuration options
19910  */
19911 Roo.form.ComboNested = function(config){
19912     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19913     // should verify some data...
19914     // like
19915     // hiddenName = required..
19916     // displayField = required
19917     // valudField == required
19918     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19919     var _t = this;
19920     Roo.each(req, function(e) {
19921         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19922             throw "Roo.form.ComboNested : missing value for: " + e;
19923         }
19924     });
19925      
19926     
19927 };
19928
19929 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19930    
19931     /*
19932      * @config {Number} max Number of columns to show
19933      */
19934     
19935     maxColumns : 3,
19936    
19937     list : null, // the outermost div..
19938     innerLists : null, // the
19939     views : null,
19940     stores : null,
19941     // private
19942     loadingChildren : false,
19943     
19944     onRender : function(ct, position)
19945     {
19946         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19947         
19948         if(this.hiddenName){
19949             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19950                     'before', true);
19951             this.hiddenField.value =
19952                 this.hiddenValue !== undefined ? this.hiddenValue :
19953                 this.value !== undefined ? this.value : '';
19954
19955             // prevent input submission
19956             this.el.dom.removeAttribute('name');
19957              
19958              
19959         }
19960         
19961         if(Roo.isGecko){
19962             this.el.dom.setAttribute('autocomplete', 'off');
19963         }
19964
19965         var cls = 'x-combo-list';
19966
19967         this.list = new Roo.Layer({
19968             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19969         });
19970
19971         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19972         this.list.setWidth(lw);
19973         this.list.swallowEvent('mousewheel');
19974         this.assetHeight = 0;
19975
19976         if(this.title){
19977             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19978             this.assetHeight += this.header.getHeight();
19979         }
19980         this.innerLists = [];
19981         this.views = [];
19982         this.stores = [];
19983         for (var i =0 ; i < this.maxColumns; i++) {
19984             this.onRenderList( cls, i);
19985         }
19986         
19987         // always needs footer, as we are going to have an 'OK' button.
19988         this.footer = this.list.createChild({cls:cls+'-ft'});
19989         this.pageTb = new Roo.Toolbar(this.footer);  
19990         var _this = this;
19991         this.pageTb.add(  {
19992             
19993             text: 'Done',
19994             handler: function()
19995             {
19996                 _this.collapse();
19997             }
19998         });
19999         
20000         if ( this.allowBlank && !this.disableClear) {
20001             
20002             this.pageTb.add(new Roo.Toolbar.Fill(), {
20003                 cls: 'x-btn-icon x-btn-clear',
20004                 text: '&#160;',
20005                 handler: function()
20006                 {
20007                     _this.collapse();
20008                     _this.clearValue();
20009                     _this.onSelect(false, -1);
20010                 }
20011             });
20012         }
20013         if (this.footer) {
20014             this.assetHeight += this.footer.getHeight();
20015         }
20016         
20017     },
20018     onRenderList : function (  cls, i)
20019     {
20020         
20021         var lw = Math.floor(
20022                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20023         );
20024         
20025         this.list.setWidth(lw); // default to '1'
20026
20027         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20028         //il.on('mouseover', this.onViewOver, this, { list:  i });
20029         //il.on('mousemove', this.onViewMove, this, { list:  i });
20030         il.setWidth(lw);
20031         il.setStyle({ 'overflow-x' : 'hidden'});
20032
20033         if(!this.tpl){
20034             this.tpl = new Roo.Template({
20035                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20036                 isEmpty: function (value, allValues) {
20037                     //Roo.log(value);
20038                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20039                     return dl ? 'has-children' : 'no-children'
20040                 }
20041             });
20042         }
20043         
20044         var store  = this.store;
20045         if (i > 0) {
20046             store  = new Roo.data.SimpleStore({
20047                 //fields : this.store.reader.meta.fields,
20048                 reader : this.store.reader,
20049                 data : [ ]
20050             });
20051         }
20052         this.stores[i]  = store;
20053                   
20054         var view = this.views[i] = new Roo.View(
20055             il,
20056             this.tpl,
20057             {
20058                 singleSelect:true,
20059                 store: store,
20060                 selectedClass: this.selectedClass
20061             }
20062         );
20063         view.getEl().setWidth(lw);
20064         view.getEl().setStyle({
20065             position: i < 1 ? 'relative' : 'absolute',
20066             top: 0,
20067             left: (i * lw ) + 'px',
20068             display : i > 0 ? 'none' : 'block'
20069         });
20070         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20071         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20072         //view.on('click', this.onViewClick, this, { list : i });
20073
20074         store.on('beforeload', this.onBeforeLoad, this);
20075         store.on('load',  this.onLoad, this, { list  : i});
20076         store.on('loadexception', this.onLoadException, this);
20077
20078         // hide the other vies..
20079         
20080         
20081         
20082     },
20083       
20084     restrictHeight : function()
20085     {
20086         var mh = 0;
20087         Roo.each(this.innerLists, function(il,i) {
20088             var el = this.views[i].getEl();
20089             el.dom.style.height = '';
20090             var inner = el.dom;
20091             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20092             // only adjust heights on other ones..
20093             mh = Math.max(h, mh);
20094             if (i < 1) {
20095                 
20096                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20097                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20098                
20099             }
20100             
20101             
20102         }, this);
20103         
20104         this.list.beginUpdate();
20105         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20106         this.list.alignTo(this.el, this.listAlign);
20107         this.list.endUpdate();
20108         
20109     },
20110      
20111     
20112     // -- store handlers..
20113     // private
20114     onBeforeLoad : function()
20115     {
20116         if(!this.hasFocus){
20117             return;
20118         }
20119         this.innerLists[0].update(this.loadingText ?
20120                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20121         this.restrictHeight();
20122         this.selectedIndex = -1;
20123     },
20124     // private
20125     onLoad : function(a,b,c,d)
20126     {
20127         if (!this.loadingChildren) {
20128             // then we are loading the top level. - hide the children
20129             for (var i = 1;i < this.views.length; i++) {
20130                 this.views[i].getEl().setStyle({ display : 'none' });
20131             }
20132             var lw = Math.floor(
20133                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20134             );
20135         
20136              this.list.setWidth(lw); // default to '1'
20137
20138             
20139         }
20140         if(!this.hasFocus){
20141             return;
20142         }
20143         
20144         if(this.store.getCount() > 0) {
20145             this.expand();
20146             this.restrictHeight();   
20147         } else {
20148             this.onEmptyResults();
20149         }
20150         
20151         if (!this.loadingChildren) {
20152             this.selectActive();
20153         }
20154         /*
20155         this.stores[1].loadData([]);
20156         this.stores[2].loadData([]);
20157         this.views
20158         */    
20159     
20160         //this.el.focus();
20161     },
20162     
20163     
20164     // private
20165     onLoadException : function()
20166     {
20167         this.collapse();
20168         Roo.log(this.store.reader.jsonData);
20169         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20170             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20171         }
20172         
20173         
20174     },
20175     // no cleaning of leading spaces on blur here.
20176     cleanLeadingSpace : function(e) { },
20177     
20178
20179     onSelectChange : function (view, sels, opts )
20180     {
20181         var ix = view.getSelectedIndexes();
20182          
20183         if (opts.list > this.maxColumns - 2) {
20184             if (view.store.getCount()<  1) {
20185                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20186
20187             } else  {
20188                 if (ix.length) {
20189                     // used to clear ?? but if we are loading unselected 
20190                     this.setFromData(view.store.getAt(ix[0]).data);
20191                 }
20192                 
20193             }
20194             
20195             return;
20196         }
20197         
20198         if (!ix.length) {
20199             // this get's fired when trigger opens..
20200            // this.setFromData({});
20201             var str = this.stores[opts.list+1];
20202             str.data.clear(); // removeall wihtout the fire events..
20203             return;
20204         }
20205         
20206         var rec = view.store.getAt(ix[0]);
20207          
20208         this.setFromData(rec.data);
20209         this.fireEvent('select', this, rec, ix[0]);
20210         
20211         var lw = Math.floor(
20212              (
20213                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20214              ) / this.maxColumns
20215         );
20216         this.loadingChildren = true;
20217         this.stores[opts.list+1].loadDataFromChildren( rec );
20218         this.loadingChildren = false;
20219         var dl = this.stores[opts.list+1]. getTotalCount();
20220         
20221         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20222         
20223         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20224         for (var i = opts.list+2; i < this.views.length;i++) {
20225             this.views[i].getEl().setStyle({ display : 'none' });
20226         }
20227         
20228         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20229         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20230         
20231         if (this.isLoading) {
20232            // this.selectActive(opts.list);
20233         }
20234          
20235     },
20236     
20237     
20238     
20239     
20240     onDoubleClick : function()
20241     {
20242         this.collapse(); //??
20243     },
20244     
20245      
20246     
20247     
20248     
20249     // private
20250     recordToStack : function(store, prop, value, stack)
20251     {
20252         var cstore = new Roo.data.SimpleStore({
20253             //fields : this.store.reader.meta.fields, // we need array reader.. for
20254             reader : this.store.reader,
20255             data : [ ]
20256         });
20257         var _this = this;
20258         var record  = false;
20259         var srec = false;
20260         if(store.getCount() < 1){
20261             return false;
20262         }
20263         store.each(function(r){
20264             if(r.data[prop] == value){
20265                 record = r;
20266             srec = r;
20267                 return false;
20268             }
20269             if (r.data.cn && r.data.cn.length) {
20270                 cstore.loadDataFromChildren( r);
20271                 var cret = _this.recordToStack(cstore, prop, value, stack);
20272                 if (cret !== false) {
20273                     record = cret;
20274                     srec = r;
20275                     return false;
20276                 }
20277             }
20278              
20279             return true;
20280         });
20281         if (record == false) {
20282             return false
20283         }
20284         stack.unshift(srec);
20285         return record;
20286     },
20287     
20288     /*
20289      * find the stack of stores that match our value.
20290      *
20291      * 
20292      */
20293     
20294     selectActive : function ()
20295     {
20296         // if store is not loaded, then we will need to wait for that to happen first.
20297         var stack = [];
20298         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20299         for (var i = 0; i < stack.length; i++ ) {
20300             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20301         }
20302         
20303     }
20304         
20305          
20306     
20307     
20308     
20309     
20310 });/*
20311  * Based on:
20312  * Ext JS Library 1.1.1
20313  * Copyright(c) 2006-2007, Ext JS, LLC.
20314  *
20315  * Originally Released Under LGPL - original licence link has changed is not relivant.
20316  *
20317  * Fork - LGPL
20318  * <script type="text/javascript">
20319  */
20320 /**
20321  * @class Roo.form.Checkbox
20322  * @extends Roo.form.Field
20323  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20324  * @constructor
20325  * Creates a new Checkbox
20326  * @param {Object} config Configuration options
20327  */
20328 Roo.form.Checkbox = function(config){
20329     Roo.form.Checkbox.superclass.constructor.call(this, config);
20330     this.addEvents({
20331         /**
20332          * @event check
20333          * Fires when the checkbox is checked or unchecked.
20334              * @param {Roo.form.Checkbox} this This checkbox
20335              * @param {Boolean} checked The new checked value
20336              */
20337         check : true
20338     });
20339 };
20340
20341 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20342     /**
20343      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20344      */
20345     focusClass : undefined,
20346     /**
20347      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20348      */
20349     fieldClass: "x-form-field",
20350     /**
20351      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20352      */
20353     checked: false,
20354     /**
20355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20356      * {tag: "input", type: "checkbox", autocomplete: "off"})
20357      */
20358     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20359     /**
20360      * @cfg {String} boxLabel The text that appears beside the checkbox
20361      */
20362     boxLabel : "",
20363     /**
20364      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20365      */  
20366     inputValue : '1',
20367     /**
20368      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20369      */
20370      valueOff: '0', // value when not checked..
20371
20372     actionMode : 'viewEl', 
20373     //
20374     // private
20375     itemCls : 'x-menu-check-item x-form-item',
20376     groupClass : 'x-menu-group-item',
20377     inputType : 'hidden',
20378     
20379     
20380     inSetChecked: false, // check that we are not calling self...
20381     
20382     inputElement: false, // real input element?
20383     basedOn: false, // ????
20384     
20385     isFormField: true, // not sure where this is needed!!!!
20386
20387     onResize : function(){
20388         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20389         if(!this.boxLabel){
20390             this.el.alignTo(this.wrap, 'c-c');
20391         }
20392     },
20393
20394     initEvents : function(){
20395         Roo.form.Checkbox.superclass.initEvents.call(this);
20396         this.el.on("click", this.onClick,  this);
20397         this.el.on("change", this.onClick,  this);
20398     },
20399
20400
20401     getResizeEl : function(){
20402         return this.wrap;
20403     },
20404
20405     getPositionEl : function(){
20406         return this.wrap;
20407     },
20408
20409     // private
20410     onRender : function(ct, position){
20411         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20412         /*
20413         if(this.inputValue !== undefined){
20414             this.el.dom.value = this.inputValue;
20415         }
20416         */
20417         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20418         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20419         var viewEl = this.wrap.createChild({ 
20420             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20421         this.viewEl = viewEl;   
20422         this.wrap.on('click', this.onClick,  this); 
20423         
20424         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20425         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20426         
20427         
20428         
20429         if(this.boxLabel){
20430             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20431         //    viewEl.on('click', this.onClick,  this); 
20432         }
20433         //if(this.checked){
20434             this.setChecked(this.checked);
20435         //}else{
20436             //this.checked = this.el.dom;
20437         //}
20438
20439     },
20440
20441     // private
20442     initValue : Roo.emptyFn,
20443
20444     /**
20445      * Returns the checked state of the checkbox.
20446      * @return {Boolean} True if checked, else false
20447      */
20448     getValue : function(){
20449         if(this.el){
20450             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20451         }
20452         return this.valueOff;
20453         
20454     },
20455
20456         // private
20457     onClick : function(){ 
20458         if (this.disabled) {
20459             return;
20460         }
20461         this.setChecked(!this.checked);
20462
20463         //if(this.el.dom.checked != this.checked){
20464         //    this.setValue(this.el.dom.checked);
20465        // }
20466     },
20467
20468     /**
20469      * Sets the checked state of the checkbox.
20470      * On is always based on a string comparison between inputValue and the param.
20471      * @param {Boolean/String} value - the value to set 
20472      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20473      */
20474     setValue : function(v,suppressEvent){
20475         
20476         
20477         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20478         //if(this.el && this.el.dom){
20479         //    this.el.dom.checked = this.checked;
20480         //    this.el.dom.defaultChecked = this.checked;
20481         //}
20482         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20483         //this.fireEvent("check", this, this.checked);
20484     },
20485     // private..
20486     setChecked : function(state,suppressEvent)
20487     {
20488         if (this.inSetChecked) {
20489             this.checked = state;
20490             return;
20491         }
20492         
20493     
20494         if(this.wrap){
20495             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20496         }
20497         this.checked = state;
20498         if(suppressEvent !== true){
20499             this.fireEvent('check', this, state);
20500         }
20501         this.inSetChecked = true;
20502         this.el.dom.value = state ? this.inputValue : this.valueOff;
20503         this.inSetChecked = false;
20504         
20505     },
20506     // handle setting of hidden value by some other method!!?!?
20507     setFromHidden: function()
20508     {
20509         if(!this.el){
20510             return;
20511         }
20512         //console.log("SET FROM HIDDEN");
20513         //alert('setFrom hidden');
20514         this.setValue(this.el.dom.value);
20515     },
20516     
20517     onDestroy : function()
20518     {
20519         if(this.viewEl){
20520             Roo.get(this.viewEl).remove();
20521         }
20522          
20523         Roo.form.Checkbox.superclass.onDestroy.call(this);
20524     },
20525     
20526     setBoxLabel : function(str)
20527     {
20528         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20529     }
20530
20531 });/*
20532  * Based on:
20533  * Ext JS Library 1.1.1
20534  * Copyright(c) 2006-2007, Ext JS, LLC.
20535  *
20536  * Originally Released Under LGPL - original licence link has changed is not relivant.
20537  *
20538  * Fork - LGPL
20539  * <script type="text/javascript">
20540  */
20541  
20542 /**
20543  * @class Roo.form.Radio
20544  * @extends Roo.form.Checkbox
20545  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20546  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20547  * @constructor
20548  * Creates a new Radio
20549  * @param {Object} config Configuration options
20550  */
20551 Roo.form.Radio = function(){
20552     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20553 };
20554 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20555     inputType: 'radio',
20556
20557     /**
20558      * If this radio is part of a group, it will return the selected value
20559      * @return {String}
20560      */
20561     getGroupValue : function(){
20562         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20563     },
20564     
20565     
20566     onRender : function(ct, position){
20567         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20568         
20569         if(this.inputValue !== undefined){
20570             this.el.dom.value = this.inputValue;
20571         }
20572          
20573         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20574         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20575         //var viewEl = this.wrap.createChild({ 
20576         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20577         //this.viewEl = viewEl;   
20578         //this.wrap.on('click', this.onClick,  this); 
20579         
20580         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20581         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20582         
20583         
20584         
20585         if(this.boxLabel){
20586             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20587         //    viewEl.on('click', this.onClick,  this); 
20588         }
20589          if(this.checked){
20590             this.el.dom.checked =   'checked' ;
20591         }
20592          
20593     } 
20594     
20595     
20596 });Roo.rtf = {}; // namespace
20597 Roo.rtf.Hex = function(hex)
20598 {
20599     this.hexstr = hex;
20600 };
20601 Roo.rtf.Paragraph = function(opts)
20602 {
20603     this.content = []; ///??? is that used?
20604 };Roo.rtf.Span = function(opts)
20605 {
20606     this.value = opts.value;
20607 };
20608
20609 Roo.rtf.Group = function(parent)
20610 {
20611     // we dont want to acutally store parent - it will make debug a nightmare..
20612     this.content = [];
20613     this.cn  = [];
20614      
20615        
20616     
20617 };
20618
20619 Roo.rtf.Group.prototype = {
20620     ignorable : false,
20621     content: false,
20622     cn: false,
20623     addContent : function(node) {
20624         // could set styles...
20625         this.content.push(node);
20626     },
20627     addChild : function(cn)
20628     {
20629         this.cn.push(cn);
20630     },
20631     // only for images really...
20632     toDataURL : function()
20633     {
20634         var mimetype = false;
20635         switch(true) {
20636             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20637                 mimetype = "image/png";
20638                 break;
20639              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20640                 mimetype = "image/jpeg";
20641                 break;
20642             default :
20643                 return 'about:blank'; // ?? error?
20644         }
20645         
20646         
20647         var hexstring = this.content[this.content.length-1].value;
20648         
20649         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20650             return String.fromCharCode(parseInt(a, 16));
20651         }).join(""));
20652     }
20653     
20654 };
20655 // this looks like it's normally the {rtf{ .... }}
20656 Roo.rtf.Document = function()
20657 {
20658     // we dont want to acutally store parent - it will make debug a nightmare..
20659     this.rtlch  = [];
20660     this.content = [];
20661     this.cn = [];
20662     
20663 };
20664 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20665     addChild : function(cn)
20666     {
20667         this.cn.push(cn);
20668         switch(cn.type) {
20669             case 'rtlch': // most content seems to be inside this??
20670             case 'listtext':
20671             case 'shpinst':
20672                 this.rtlch.push(cn);
20673                 return;
20674             default:
20675                 this[cn.type] = cn;
20676         }
20677         
20678     },
20679     
20680     getElementsByType : function(type)
20681     {
20682         var ret =  [];
20683         this._getElementsByType(type, ret, this.cn, 'rtf');
20684         return ret;
20685     },
20686     _getElementsByType : function (type, ret, search_array, path)
20687     {
20688         search_array.forEach(function(n,i) {
20689             if (n.type == type) {
20690                 n.path = path + '/' + n.type + ':' + i;
20691                 ret.push(n);
20692             }
20693             if (n.cn.length > 0) {
20694                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20695             }
20696         },this);
20697     }
20698     
20699 });
20700  
20701 Roo.rtf.Ctrl = function(opts)
20702 {
20703     this.value = opts.value;
20704     this.param = opts.param;
20705 };
20706 /**
20707  *
20708  *
20709  * based on this https://github.com/iarna/rtf-parser
20710  * it's really only designed to extract pict from pasted RTF 
20711  *
20712  * usage:
20713  *
20714  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20715  *  
20716  *
20717  */
20718
20719  
20720
20721
20722
20723 Roo.rtf.Parser = function(text) {
20724     //super({objectMode: true})
20725     this.text = '';
20726     this.parserState = this.parseText;
20727     
20728     // these are for interpeter...
20729     this.doc = {};
20730     ///this.parserState = this.parseTop
20731     this.groupStack = [];
20732     this.hexStore = [];
20733     this.doc = false;
20734     
20735     this.groups = []; // where we put the return.
20736     
20737     for (var ii = 0; ii < text.length; ++ii) {
20738         ++this.cpos;
20739         
20740         if (text[ii] === '\n') {
20741             ++this.row;
20742             this.col = 1;
20743         } else {
20744             ++this.col;
20745         }
20746         this.parserState(text[ii]);
20747     }
20748     
20749     
20750     
20751 };
20752 Roo.rtf.Parser.prototype = {
20753     text : '', // string being parsed..
20754     controlWord : '',
20755     controlWordParam :  '',
20756     hexChar : '',
20757     doc : false,
20758     group: false,
20759     groupStack : false,
20760     hexStore : false,
20761     
20762     
20763     cpos : 0, 
20764     row : 1, // reportin?
20765     col : 1, //
20766
20767      
20768     push : function (el)
20769     {
20770         var m = 'cmd'+ el.type;
20771         if (typeof(this[m]) == 'undefined') {
20772             Roo.log('invalid cmd:' + el.type);
20773             return;
20774         }
20775         this[m](el);
20776         //Roo.log(el);
20777     },
20778     flushHexStore : function()
20779     {
20780         if (this.hexStore.length < 1) {
20781             return;
20782         }
20783         var hexstr = this.hexStore.map(
20784             function(cmd) {
20785                 return cmd.value;
20786         }).join('');
20787         
20788         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20789               
20790             
20791         this.hexStore.splice(0)
20792         
20793     },
20794     
20795     cmdgroupstart : function()
20796     {
20797         this.flushHexStore();
20798         if (this.group) {
20799             this.groupStack.push(this.group);
20800         }
20801          // parent..
20802         if (this.doc === false) {
20803             this.group = this.doc = new Roo.rtf.Document();
20804             return;
20805             
20806         }
20807         this.group = new Roo.rtf.Group(this.group);
20808     },
20809     cmdignorable : function()
20810     {
20811         this.flushHexStore();
20812         this.group.ignorable = true;
20813     },
20814     cmdendparagraph : function()
20815     {
20816         this.flushHexStore();
20817         this.group.addContent(new Roo.rtf.Paragraph());
20818     },
20819     cmdgroupend : function ()
20820     {
20821         this.flushHexStore();
20822         var endingGroup = this.group;
20823         
20824         
20825         this.group = this.groupStack.pop();
20826         if (this.group) {
20827             this.group.addChild(endingGroup);
20828         }
20829         
20830         
20831         
20832         var doc = this.group || this.doc;
20833         //if (endingGroup instanceof FontTable) {
20834         //  doc.fonts = endingGroup.table
20835         //} else if (endingGroup instanceof ColorTable) {
20836         //  doc.colors = endingGroup.table
20837         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20838         if (endingGroup.ignorable === false) {
20839             //code
20840             this.groups.push(endingGroup);
20841            // Roo.log( endingGroup );
20842         }
20843             //Roo.each(endingGroup.content, function(item)) {
20844             //    doc.addContent(item);
20845             //}
20846             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20847         //}
20848     },
20849     cmdtext : function (cmd)
20850     {
20851         this.flushHexStore();
20852         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20853             //this.group = this.doc
20854         }
20855         this.group.addContent(new Roo.rtf.Span(cmd));
20856     },
20857     cmdcontrolword : function (cmd)
20858     {
20859         this.flushHexStore();
20860         if (!this.group.type) {
20861             this.group.type = cmd.value;
20862             return;
20863         }
20864         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20865         // we actually don't care about ctrl words...
20866         return ;
20867         /*
20868         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20869         if (this[method]) {
20870             this[method](cmd.param)
20871         } else {
20872             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20873         }
20874         */
20875     },
20876     cmdhexchar : function(cmd) {
20877         this.hexStore.push(cmd);
20878     },
20879     cmderror : function(cmd) {
20880         throw new Exception (cmd.value);
20881     },
20882     
20883     /*
20884       _flush (done) {
20885         if (this.text !== '\u0000') this.emitText()
20886         done()
20887       }
20888       */
20889       
20890       
20891     parseText : function(c)
20892     {
20893         if (c === '\\') {
20894             this.parserState = this.parseEscapes;
20895         } else if (c === '{') {
20896             this.emitStartGroup();
20897         } else if (c === '}') {
20898             this.emitEndGroup();
20899         } else if (c === '\x0A' || c === '\x0D') {
20900             // cr/lf are noise chars
20901         } else {
20902             this.text += c;
20903         }
20904     },
20905     
20906     parseEscapes: function (c)
20907     {
20908         if (c === '\\' || c === '{' || c === '}') {
20909             this.text += c;
20910             this.parserState = this.parseText;
20911         } else {
20912             this.parserState = this.parseControlSymbol;
20913             this.parseControlSymbol(c);
20914         }
20915     },
20916     parseControlSymbol: function(c)
20917     {
20918         if (c === '~') {
20919             this.text += '\u00a0'; // nbsp
20920             this.parserState = this.parseText
20921         } else if (c === '-') {
20922              this.text += '\u00ad'; // soft hyphen
20923         } else if (c === '_') {
20924             this.text += '\u2011'; // non-breaking hyphen
20925         } else if (c === '*') {
20926             this.emitIgnorable();
20927             this.parserState = this.parseText;
20928         } else if (c === "'") {
20929             this.parserState = this.parseHexChar;
20930         } else if (c === '|') { // formula cacter
20931             this.emitFormula();
20932             this.parserState = this.parseText;
20933         } else if (c === ':') { // subentry in an index entry
20934             this.emitIndexSubEntry();
20935             this.parserState = this.parseText;
20936         } else if (c === '\x0a') {
20937             this.emitEndParagraph();
20938             this.parserState = this.parseText;
20939         } else if (c === '\x0d') {
20940             this.emitEndParagraph();
20941             this.parserState = this.parseText;
20942         } else {
20943             this.parserState = this.parseControlWord;
20944             this.parseControlWord(c);
20945         }
20946     },
20947     parseHexChar: function (c)
20948     {
20949         if (/^[A-Fa-f0-9]$/.test(c)) {
20950             this.hexChar += c;
20951             if (this.hexChar.length >= 2) {
20952               this.emitHexChar();
20953               this.parserState = this.parseText;
20954             }
20955             return;
20956         }
20957         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20958         this.parserState = this.parseText;
20959         
20960     },
20961     parseControlWord : function(c)
20962     {
20963         if (c === ' ') {
20964             this.emitControlWord();
20965             this.parserState = this.parseText;
20966         } else if (/^[-\d]$/.test(c)) {
20967             this.parserState = this.parseControlWordParam;
20968             this.controlWordParam += c;
20969         } else if (/^[A-Za-z]$/.test(c)) {
20970           this.controlWord += c;
20971         } else {
20972           this.emitControlWord();
20973           this.parserState = this.parseText;
20974           this.parseText(c);
20975         }
20976     },
20977     parseControlWordParam : function (c) {
20978         if (/^\d$/.test(c)) {
20979           this.controlWordParam += c;
20980         } else if (c === ' ') {
20981           this.emitControlWord();
20982           this.parserState = this.parseText;
20983         } else {
20984           this.emitControlWord();
20985           this.parserState = this.parseText;
20986           this.parseText(c);
20987         }
20988     },
20989     
20990     
20991     
20992     
20993     emitText : function () {
20994         if (this.text === '') {
20995             return;
20996         }
20997         this.push({
20998             type: 'text',
20999             value: this.text,
21000             pos: this.cpos,
21001             row: this.row,
21002             col: this.col
21003         });
21004         this.text = ''
21005     },
21006     emitControlWord : function ()
21007     {
21008         this.emitText();
21009         if (this.controlWord === '') {
21010             this.emitError('empty control word');
21011         } else {
21012             this.push({
21013                   type: 'controlword',
21014                   value: this.controlWord,
21015                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21016                   pos: this.cpos,
21017                   row: this.row,
21018                   col: this.col
21019             });
21020         }
21021         this.controlWord = '';
21022         this.controlWordParam = '';
21023     },
21024     emitStartGroup : function ()
21025     {
21026         this.emitText();
21027         this.push({
21028             type: 'groupstart',
21029             pos: this.cpos,
21030             row: this.row,
21031             col: this.col
21032         });
21033     },
21034     emitEndGroup : function ()
21035     {
21036         this.emitText();
21037         this.push({
21038             type: 'groupend',
21039             pos: this.cpos,
21040             row: this.row,
21041             col: this.col
21042         });
21043     },
21044     emitIgnorable : function ()
21045     {
21046         this.emitText();
21047         this.push({
21048             type: 'ignorable',
21049             pos: this.cpos,
21050             row: this.row,
21051             col: this.col
21052         });
21053     },
21054     emitHexChar : function ()
21055     {
21056         this.emitText();
21057         this.push({
21058             type: 'hexchar',
21059             value: this.hexChar,
21060             pos: this.cpos,
21061             row: this.row,
21062             col: this.col
21063         });
21064         this.hexChar = ''
21065     },
21066     emitError : function (message)
21067     {
21068       this.emitText();
21069       this.push({
21070             type: 'error',
21071             value: message,
21072             row: this.row,
21073             col: this.col,
21074             char: this.cpos //,
21075             //stack: new Error().stack
21076         });
21077     },
21078     emitEndParagraph : function () {
21079         this.emitText();
21080         this.push({
21081             type: 'endparagraph',
21082             pos: this.cpos,
21083             row: this.row,
21084             col: this.col
21085         });
21086     }
21087      
21088 } ;
21089 Roo.htmleditor = {};
21090  
21091 /**
21092  * @class Roo.htmleditor.Filter
21093  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21094  * @cfg {DomElement} node The node to iterate and filter
21095  * @cfg {boolean|String|Array} tag Tags to replace 
21096  * @constructor
21097  * Create a new Filter.
21098  * @param {Object} config Configuration options
21099  */
21100
21101
21102
21103 Roo.htmleditor.Filter = function(cfg) {
21104     Roo.apply(this.cfg);
21105     // this does not actually call walk as it's really just a abstract class
21106 }
21107
21108
21109 Roo.htmleditor.Filter.prototype = {
21110     
21111     node: false,
21112     
21113     tag: false,
21114
21115     // overrride to do replace comments.
21116     replaceComment : false,
21117     
21118     // overrride to do replace or do stuff with tags..
21119     replaceTag : false,
21120     
21121     walk : function(dom)
21122     {
21123         Roo.each( Array.from(dom.childNodes), function( e ) {
21124             switch(true) {
21125                 
21126                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21127                     this.replaceComment(e);
21128                     return;
21129                 
21130                 case e.nodeType != 1: //not a node.
21131                     return;
21132                 
21133                 case this.tag === true: // everything
21134                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21135                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21136                     if (this.replaceTag && false === this.replaceTag(e)) {
21137                         return;
21138                     }
21139                     if (e.hasChildNodes()) {
21140                         this.walk(e);
21141                     }
21142                     return;
21143                 
21144                 default:    // tags .. that do not match.
21145                     if (e.hasChildNodes()) {
21146                         this.walk(e);
21147                     }
21148             }
21149             
21150         }, this);
21151         
21152     }
21153 }; 
21154
21155 /**
21156  * @class Roo.htmleditor.FilterAttributes
21157  * clean attributes and  styles including http:// etc.. in attribute
21158  * @constructor
21159 * Run a new Attribute Filter
21160 * @param {Object} config Configuration options
21161  */
21162 Roo.htmleditor.FilterAttributes = function(cfg)
21163 {
21164     Roo.apply(this, cfg);
21165     this.attrib_black = this.attrib_black || [];
21166     this.attrib_white = this.attrib_white || [];
21167
21168     this.attrib_clean = this.attrib_clean || [];
21169     this.style_white = this.style_white || [];
21170     this.style_black = this.style_black || [];
21171     this.walk(cfg.node);
21172 }
21173
21174 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21175 {
21176     tag: true, // all tags
21177     
21178     attrib_black : false, // array
21179     attrib_clean : false,
21180     attrib_white : false,
21181
21182     style_white : false,
21183     style_black : false,
21184      
21185      
21186     replaceTag : function(node)
21187     {
21188         if (!node.attributes || !node.attributes.length) {
21189             return true;
21190         }
21191         
21192         for (var i = node.attributes.length-1; i > -1 ; i--) {
21193             var a = node.attributes[i];
21194             //console.log(a);
21195             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21196                 node.removeAttribute(a.name);
21197                 continue;
21198             }
21199             
21200             
21201             
21202             if (a.name.toLowerCase().substr(0,2)=='on')  {
21203                 node.removeAttribute(a.name);
21204                 continue;
21205             }
21206             
21207             
21208             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21209                 node.removeAttribute(a.name);
21210                 continue;
21211             }
21212             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21213                 this.cleanAttr(node,a.name,a.value); // fixme..
21214                 continue;
21215             }
21216             if (a.name == 'style') {
21217                 this.cleanStyle(node,a.name,a.value);
21218                 continue;
21219             }
21220             /// clean up MS crap..
21221             // tecnically this should be a list of valid class'es..
21222             
21223             
21224             if (a.name == 'class') {
21225                 if (a.value.match(/^Mso/)) {
21226                     node.removeAttribute('class');
21227                 }
21228                 
21229                 if (a.value.match(/^body$/)) {
21230                     node.removeAttribute('class');
21231                 }
21232                 continue;
21233             }
21234             
21235             
21236             // style cleanup!?
21237             // class cleanup?
21238             
21239         }
21240         return true; // clean children
21241     },
21242         
21243     cleanAttr: function(node, n,v)
21244     {
21245         
21246         if (v.match(/^\./) || v.match(/^\//)) {
21247             return;
21248         }
21249         if (v.match(/^(http|https):\/\//)
21250             || v.match(/^mailto:/) 
21251             || v.match(/^ftp:/)
21252             || v.match(/^data:/)
21253             ) {
21254             return;
21255         }
21256         if (v.match(/^#/)) {
21257             return;
21258         }
21259         if (v.match(/^\{/)) { // allow template editing.
21260             return;
21261         }
21262 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21263         node.removeAttribute(n);
21264         
21265     },
21266     cleanStyle : function(node,  n,v)
21267     {
21268         if (v.match(/expression/)) { //XSS?? should we even bother..
21269             node.removeAttribute(n);
21270             return;
21271         }
21272         
21273         var parts = v.split(/;/);
21274         var clean = [];
21275         
21276         Roo.each(parts, function(p) {
21277             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21278             if (!p.length) {
21279                 return true;
21280             }
21281             var l = p.split(':').shift().replace(/\s+/g,'');
21282             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21283             
21284             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21285                 return true;
21286             }
21287             //Roo.log()
21288             // only allow 'c whitelisted system attributes'
21289             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21290                 return true;
21291             }
21292             
21293             
21294             clean.push(p);
21295             return true;
21296         },this);
21297         if (clean.length) { 
21298             node.setAttribute(n, clean.join(';'));
21299         } else {
21300             node.removeAttribute(n);
21301         }
21302         
21303     }
21304         
21305         
21306         
21307     
21308 });/**
21309  * @class Roo.htmleditor.FilterBlack
21310  * remove blacklisted elements.
21311  * @constructor
21312  * Run a new Blacklisted Filter
21313  * @param {Object} config Configuration options
21314  */
21315
21316 Roo.htmleditor.FilterBlack = function(cfg)
21317 {
21318     Roo.apply(this, cfg);
21319     this.walk(cfg.node);
21320 }
21321
21322 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21323 {
21324     tag : true, // all elements.
21325    
21326     replaceTag : function(n)
21327     {
21328         n.parentNode.removeChild(n);
21329     }
21330 });
21331 /**
21332  * @class Roo.htmleditor.FilterComment
21333  * remove comments.
21334  * @constructor
21335 * Run a new Comments Filter
21336 * @param {Object} config Configuration options
21337  */
21338 Roo.htmleditor.FilterComment = function(cfg)
21339 {
21340     this.walk(cfg.node);
21341 }
21342
21343 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21344 {
21345   
21346     replaceComment : function(n)
21347     {
21348         n.parentNode.removeChild(n);
21349     }
21350 });/**
21351  * @class Roo.htmleditor.FilterKeepChildren
21352  * remove tags but keep children
21353  * @constructor
21354  * Run a new Keep Children Filter
21355  * @param {Object} config Configuration options
21356  */
21357
21358 Roo.htmleditor.FilterKeepChildren = function(cfg)
21359 {
21360     Roo.apply(this, cfg);
21361     if (this.tag === false) {
21362         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21363     }
21364     this.walk(cfg.node);
21365 }
21366
21367 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21368 {
21369     
21370   
21371     replaceTag : function(node)
21372     {
21373         // walk children...
21374         //Roo.log(node);
21375         var ar = Array.from(node.childNodes);
21376         //remove first..
21377         for (var i = 0; i < ar.length; i++) {
21378             if (ar[i].nodeType == 1) {
21379                 if (
21380                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21381                     || // array and it matches
21382                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21383                 ) {
21384                     this.replaceTag(ar[i]); // child is blacklisted as well...
21385                     continue;
21386                 }
21387             }
21388         }  
21389         ar = Array.from(node.childNodes);
21390         for (var i = 0; i < ar.length; i++) {
21391          
21392             node.removeChild(ar[i]);
21393             // what if we need to walk these???
21394             node.parentNode.insertBefore(ar[i], node);
21395             if (this.tag !== false) {
21396                 this.walk(ar[i]);
21397                 
21398             }
21399         }
21400         node.parentNode.removeChild(node);
21401         return false; // don't walk children
21402         
21403         
21404     }
21405 });/**
21406  * @class Roo.htmleditor.FilterParagraph
21407  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21408  * like on 'push' to remove the <p> tags and replace them with line breaks.
21409  * @constructor
21410  * Run a new Paragraph Filter
21411  * @param {Object} config Configuration options
21412  */
21413
21414 Roo.htmleditor.FilterParagraph = function(cfg)
21415 {
21416     // no need to apply config.
21417     this.walk(cfg.node);
21418 }
21419
21420 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21421 {
21422     
21423      
21424     tag : 'P',
21425     
21426      
21427     replaceTag : function(node)
21428     {
21429         
21430         if (node.childNodes.length == 1 &&
21431             node.childNodes[0].nodeType == 3 &&
21432             node.childNodes[0].textContent.trim().length < 1
21433             ) {
21434             // remove and replace with '<BR>';
21435             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21436             return false; // no need to walk..
21437         }
21438         var ar = Array.from(node.childNodes);
21439         for (var i = 0; i < ar.length; i++) {
21440             node.removeChild(ar[i]);
21441             // what if we need to walk these???
21442             node.parentNode.insertBefore(ar[i], node);
21443         }
21444         // now what about this?
21445         // <p> &nbsp; </p>
21446         
21447         // double BR.
21448         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21449         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21450         node.parentNode.removeChild(node);
21451         
21452         return false;
21453
21454     }
21455     
21456 });/**
21457  * @class Roo.htmleditor.FilterSpan
21458  * filter span's with no attributes out..
21459  * @constructor
21460  * Run a new Span Filter
21461  * @param {Object} config Configuration options
21462  */
21463
21464 Roo.htmleditor.FilterSpan = function(cfg)
21465 {
21466     // no need to apply config.
21467     this.walk(cfg.node);
21468 }
21469
21470 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21471 {
21472      
21473     tag : 'SPAN',
21474      
21475  
21476     replaceTag : function(node)
21477     {
21478         if (node.attributes && node.attributes.length > 0) {
21479             return true; // walk if there are any.
21480         }
21481         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21482         return false;
21483      
21484     }
21485     
21486 });/**
21487  * @class Roo.htmleditor.FilterTableWidth
21488   try and remove table width data - as that frequently messes up other stuff.
21489  * 
21490  *      was cleanTableWidths.
21491  *
21492  * Quite often pasting from word etc.. results in tables with column and widths.
21493  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21494  *
21495  * @constructor
21496  * Run a new Table Filter
21497  * @param {Object} config Configuration options
21498  */
21499
21500 Roo.htmleditor.FilterTableWidth = function(cfg)
21501 {
21502     // no need to apply config.
21503     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21504     this.walk(cfg.node);
21505 }
21506
21507 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21508 {
21509      
21510      
21511     
21512     replaceTag: function(node) {
21513         
21514         
21515       
21516         if (node.hasAttribute('width')) {
21517             node.removeAttribute('width');
21518         }
21519         
21520          
21521         if (node.hasAttribute("style")) {
21522             // pretty basic...
21523             
21524             var styles = node.getAttribute("style").split(";");
21525             var nstyle = [];
21526             Roo.each(styles, function(s) {
21527                 if (!s.match(/:/)) {
21528                     return;
21529                 }
21530                 var kv = s.split(":");
21531                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21532                     return;
21533                 }
21534                 // what ever is left... we allow.
21535                 nstyle.push(s);
21536             });
21537             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21538             if (!nstyle.length) {
21539                 node.removeAttribute('style');
21540             }
21541         }
21542         
21543         return true; // continue doing children..
21544     }
21545 });/**
21546  * @class Roo.htmleditor.FilterWord
21547  * try and clean up all the mess that Word generates.
21548  * 
21549  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21550  
21551  * @constructor
21552  * Run a new Span Filter
21553  * @param {Object} config Configuration options
21554  */
21555
21556 Roo.htmleditor.FilterWord = function(cfg)
21557 {
21558     // no need to apply config.
21559     this.walk(cfg.node);
21560 }
21561
21562 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21563 {
21564     tag: true,
21565      
21566     
21567     /**
21568      * Clean up MS wordisms...
21569      */
21570     replaceTag : function(node)
21571     {
21572          
21573         // no idea what this does - span with text, replaceds with just text.
21574         if(
21575                 node.nodeName == 'SPAN' &&
21576                 !node.hasAttributes() &&
21577                 node.childNodes.length == 1 &&
21578                 node.firstChild.nodeName == "#text"  
21579         ) {
21580             var textNode = node.firstChild;
21581             node.removeChild(textNode);
21582             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21583                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21584             }
21585             node.parentNode.insertBefore(textNode, node);
21586             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21587                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21588             }
21589             
21590             node.parentNode.removeChild(node);
21591             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21592         }
21593         
21594    
21595         
21596         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21597             node.parentNode.removeChild(node);
21598             return false; // dont do chidlren
21599         }
21600         //Roo.log(node.tagName);
21601         // remove - but keep children..
21602         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21603             //Roo.log('-- removed');
21604             while (node.childNodes.length) {
21605                 var cn = node.childNodes[0];
21606                 node.removeChild(cn);
21607                 node.parentNode.insertBefore(cn, node);
21608                 // move node to parent - and clean it..
21609                 this.replaceTag(cn);
21610             }
21611             node.parentNode.removeChild(node);
21612             /// no need to iterate chidlren = it's got none..
21613             //this.iterateChildren(node, this.cleanWord);
21614             return false; // no need to iterate children.
21615         }
21616         // clean styles
21617         if (node.className.length) {
21618             
21619             var cn = node.className.split(/\W+/);
21620             var cna = [];
21621             Roo.each(cn, function(cls) {
21622                 if (cls.match(/Mso[a-zA-Z]+/)) {
21623                     return;
21624                 }
21625                 cna.push(cls);
21626             });
21627             node.className = cna.length ? cna.join(' ') : '';
21628             if (!cna.length) {
21629                 node.removeAttribute("class");
21630             }
21631         }
21632         
21633         if (node.hasAttribute("lang")) {
21634             node.removeAttribute("lang");
21635         }
21636         
21637         if (node.hasAttribute("style")) {
21638             
21639             var styles = node.getAttribute("style").split(";");
21640             var nstyle = [];
21641             Roo.each(styles, function(s) {
21642                 if (!s.match(/:/)) {
21643                     return;
21644                 }
21645                 var kv = s.split(":");
21646                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21647                     return;
21648                 }
21649                 // what ever is left... we allow.
21650                 nstyle.push(s);
21651             });
21652             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21653             if (!nstyle.length) {
21654                 node.removeAttribute('style');
21655             }
21656         }
21657         return true; // do children
21658         
21659         
21660         
21661     }
21662 });
21663 /**
21664  * @class Roo.htmleditor.FilterStyleToTag
21665  * part of the word stuff... - certain 'styles' should be converted to tags.
21666  * eg.
21667  *   font-weight: bold -> bold
21668  *   ?? super / subscrit etc..
21669  * 
21670  * @constructor
21671 * Run a new style to tag filter.
21672 * @param {Object} config Configuration options
21673  */
21674 Roo.htmleditor.FilterStyleToTag = function(cfg)
21675 {
21676     
21677     this.tags = {
21678         B  : [ 'fontWeight' , 'bold'],
21679         I :  [ 'fontStyle' , 'italic'],
21680         //pre :  [ 'font-style' , 'italic'],
21681         // h1.. h6 ?? font-size?
21682         SUP : [ 'verticalAlign' , 'super' ],
21683         SUB : [ 'verticalAlign' , 'sub' ]
21684         
21685         
21686     };
21687     
21688     Roo.apply(this, cfg);
21689      
21690     
21691     this.walk(cfg.node);
21692     
21693     
21694     
21695 }
21696
21697
21698 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21699 {
21700     tag: true, // all tags
21701     
21702     tags : false,
21703     
21704     
21705     replaceTag : function(node)
21706     {
21707         
21708         
21709         if (node.getAttribute("style") === null) {
21710             return true;
21711         }
21712         var inject = [];
21713         for (var k in this.tags) {
21714             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21715                 inject.push(k);
21716                 node.style.removeProperty(this.tags[k][0]);
21717             }
21718         }
21719         if (!inject.length) {
21720             return true; 
21721         }
21722         var cn = Array.from(node.childNodes);
21723         var nn = node;
21724         Roo.each(inject, function(t) {
21725             var nc = node.ownerDocument.createElement(t);
21726             nn.appendChild(nc);
21727             nn = nc;
21728         });
21729         for(var i = 0;i < cn.length;cn++) {
21730             node.removeChild(cn[i]);
21731             nn.appendChild(cn[i]);
21732         }
21733         return true /// iterate thru
21734     }
21735     
21736 })/**
21737  * @class Roo.htmleditor.FilterLongBr
21738  * BR/BR/BR - keep a maximum of 2...
21739  * @constructor
21740  * Run a new Long BR Filter
21741  * @param {Object} config Configuration options
21742  */
21743
21744 Roo.htmleditor.FilterLongBr = function(cfg)
21745 {
21746     // no need to apply config.
21747     this.walk(cfg.node);
21748 }
21749
21750 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21751 {
21752     
21753      
21754     tag : 'BR',
21755     
21756      
21757     replaceTag : function(node)
21758     {
21759         
21760         var ps = node.nextSibling;
21761         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21762             ps = ps.nextSibling;
21763         }
21764         
21765         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21766             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21767             return false;
21768         }
21769         
21770         if (!ps || ps.nodeType != 1) {
21771             return false;
21772         }
21773         
21774         if (!ps || ps.tagName != 'BR') {
21775            
21776             return false;
21777         }
21778         
21779         
21780         
21781         
21782         
21783         if (!node.previousSibling) {
21784             return false;
21785         }
21786         var ps = node.previousSibling;
21787         
21788         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21789             ps = ps.previousSibling;
21790         }
21791         if (!ps || ps.nodeType != 1) {
21792             return false;
21793         }
21794         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21795         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21796             return false;
21797         }
21798         
21799         node.parentNode.removeChild(node); // remove me...
21800         
21801         return false; // no need to do children
21802
21803     }
21804     
21805 }); 
21806
21807 /**
21808  * @class Roo.htmleditor.FilterBlock
21809  * removes id / data-block and contenteditable that are associated with blocks
21810  * usage should be done on a cloned copy of the dom
21811  * @constructor
21812 * Run a new Attribute Filter { node : xxxx }}
21813 * @param {Object} config Configuration options
21814  */
21815 Roo.htmleditor.FilterBlock = function(cfg)
21816 {
21817     Roo.apply(this, cfg);
21818     var qa = cfg.node.querySelectorAll;
21819     this.removeAttributes('data-block');
21820     this.removeAttributes('contenteditable');
21821     this.removeAttributes('id');
21822     
21823 }
21824
21825 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21826 {
21827     node: true, // all tags
21828      
21829      
21830     removeAttributes : function(attr)
21831     {
21832         var ar = this.node.querySelectorAll('*[' + attr + ']');
21833         for (var i =0;i<ar.length;i++) {
21834             ar[i].removeAttribute(attr);
21835         }
21836     }
21837         
21838         
21839         
21840     
21841 });
21842 /***
21843  * This is based loosely on tinymce 
21844  * @class Roo.htmleditor.TidySerializer
21845  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21846  * @constructor
21847  * @method Serializer
21848  * @param {Object} settings Name/value settings object.
21849  */
21850
21851
21852 Roo.htmleditor.TidySerializer = function(settings)
21853 {
21854     Roo.apply(this, settings);
21855     
21856     this.writer = new Roo.htmleditor.TidyWriter(settings);
21857     
21858     
21859
21860 };
21861 Roo.htmleditor.TidySerializer.prototype = {
21862     
21863     /**
21864      * @param {boolean} inner do the inner of the node.
21865      */
21866     inner : false,
21867     
21868     writer : false,
21869     
21870     /**
21871     * Serializes the specified node into a string.
21872     *
21873     * @example
21874     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
21875     * @method serialize
21876     * @param {DomElement} node Node instance to serialize.
21877     * @return {String} String with HTML based on DOM tree.
21878     */
21879     serialize : function(node) {
21880         
21881         // = settings.validate;
21882         var writer = this.writer;
21883         var self  = this;
21884         this.handlers = {
21885             // #text
21886             3: function(node) {
21887                 
21888                 writer.text(node.nodeValue, node);
21889             },
21890             // #comment
21891             8: function(node) {
21892                 writer.comment(node.nodeValue);
21893             },
21894             // Processing instruction
21895             7: function(node) {
21896                 writer.pi(node.name, node.nodeValue);
21897             },
21898             // Doctype
21899             10: function(node) {
21900                 writer.doctype(node.nodeValue);
21901             },
21902             // CDATA
21903             4: function(node) {
21904                 writer.cdata(node.nodeValue);
21905             },
21906             // Document fragment
21907             11: function(node) {
21908                 node = node.firstChild;
21909                 if (!node) {
21910                     return;
21911                 }
21912                 while(node) {
21913                     self.walk(node);
21914                     node = node.nextSibling
21915                 }
21916             }
21917         };
21918         writer.reset();
21919         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
21920         return writer.getContent();
21921     },
21922
21923     walk: function(node)
21924     {
21925         var attrName, attrValue, sortedAttrs, i, l, elementRule,
21926             handler = this.handlers[node.nodeType];
21927             
21928         if (handler) {
21929             handler(node);
21930             return;
21931         }
21932     
21933         var name = node.nodeName;
21934         var isEmpty = node.childNodes.length < 1;
21935       
21936         var writer = this.writer;
21937         var attrs = node.attributes;
21938         // Sort attributes
21939         
21940         writer.start(node.nodeName, attrs, isEmpty, node);
21941         if (isEmpty) {
21942             return;
21943         }
21944         node = node.firstChild;
21945         if (!node) {
21946             writer.end(name);
21947             return;
21948         }
21949         while (node) {
21950             this.walk(node);
21951             node = node.nextSibling;
21952         }
21953         writer.end(name);
21954         
21955     
21956     }
21957     // Serialize element and treat all non elements as fragments
21958    
21959 }; 
21960
21961 /***
21962  * This is based loosely on tinymce 
21963  * @class Roo.htmleditor.TidyWriter
21964  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21965  *
21966  * Known issues?
21967  * - not tested much with 'PRE' formated elements.
21968  * 
21969  *
21970  *
21971  */
21972
21973 Roo.htmleditor.TidyWriter = function(settings)
21974 {
21975     
21976     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
21977     Roo.apply(this, settings);
21978     this.html = [];
21979     this.state = [];
21980      
21981     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
21982   
21983 }
21984 Roo.htmleditor.TidyWriter.prototype = {
21985
21986  
21987     state : false,
21988     
21989     indent :  '  ',
21990     
21991     // part of state...
21992     indentstr : '',
21993     in_pre: false,
21994     in_inline : false,
21995     last_inline : false,
21996     encode : false,
21997      
21998     
21999             /**
22000     * Writes the a start element such as <p id="a">.
22001     *
22002     * @method start
22003     * @param {String} name Name of the element.
22004     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22005     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22006     */
22007     start: function(name, attrs, empty, node)
22008     {
22009         var i, l, attr, value;
22010         
22011         // there are some situations where adding line break && indentation will not work. will not work.
22012         // <span / b / i ... formating?
22013         
22014         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22015         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22016         
22017         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22018         
22019         var add_lb = name == 'BR' ? false : in_inline;
22020         
22021         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22022             i_inline = false;
22023         }
22024
22025         var indentstr =  this.indentstr;
22026         
22027         // e_inline = elements that can be inline, but still allow \n before and after?
22028         // only 'BR' ??? any others?
22029         
22030         // ADD LINE BEFORE tage
22031         if (!this.in_pre) {
22032             if (in_inline) {
22033                 //code
22034                 if (name == 'BR') {
22035                     this.addLine();
22036                 } else if (this.lastElementEndsWS()) {
22037                     this.addLine();
22038                 } else{
22039                     // otherwise - no new line. (and dont indent.)
22040                     indentstr = '';
22041                 }
22042                 
22043             } else {
22044                 this.addLine();
22045             }
22046         } else {
22047             indentstr = '';
22048         }
22049         
22050         this.html.push(indentstr + '<', name.toLowerCase());
22051         
22052         if (attrs) {
22053             for (i = 0, l = attrs.length; i < l; i++) {
22054                 attr = attrs[i];
22055                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22056             }
22057         }
22058      
22059         if (empty) {
22060             if (is_short) {
22061                 this.html[this.html.length] = '/>';
22062             } else {
22063                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22064             }
22065             var e_inline = name == 'BR' ? false : this.in_inline;
22066             
22067             if (!e_inline && !this.in_pre) {
22068                 this.addLine();
22069             }
22070             return;
22071         
22072         }
22073         // not empty..
22074         this.html[this.html.length] = '>';
22075         
22076         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22077         /*
22078         if (!in_inline && !in_pre) {
22079             var cn = node.firstChild;
22080             while(cn) {
22081                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22082                     in_inline = true
22083                     break;
22084                 }
22085                 cn = cn.nextSibling;
22086             }
22087              
22088         }
22089         */
22090         
22091         
22092         this.pushState({
22093             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22094             in_pre : in_pre,
22095             in_inline :  in_inline
22096         });
22097         // add a line after if we are not in a
22098         
22099         if (!in_inline && !in_pre) {
22100             this.addLine();
22101         }
22102         
22103             
22104          
22105         
22106     },
22107     
22108     lastElementEndsWS : function()
22109     {
22110         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22111         if (value === false) {
22112             return true;
22113         }
22114         return value.match(/\s+$/);
22115         
22116     },
22117     
22118     /**
22119      * Writes the a end element such as </p>.
22120      *
22121      * @method end
22122      * @param {String} name Name of the element.
22123      */
22124     end: function(name) {
22125         var value;
22126         this.popState();
22127         var indentstr = '';
22128         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22129         
22130         if (!this.in_pre && !in_inline) {
22131             this.addLine();
22132             indentstr  = this.indentstr;
22133         }
22134         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22135         this.last_inline = in_inline;
22136         
22137         // pop the indent state..
22138     },
22139     /**
22140      * Writes a text node.
22141      *
22142      * In pre - we should not mess with the contents.
22143      * 
22144      *
22145      * @method text
22146      * @param {String} text String to write out.
22147      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22148      */
22149     text: function(text, node)
22150     {
22151         // if not in whitespace critical
22152         if (text.length < 1) {
22153             return;
22154         }
22155         if (this.in_pre) {
22156             this.html[this.html.length] =  text;
22157             return;   
22158         }
22159         
22160         if (this.in_inline) {
22161             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22162             if (text != ' ') {
22163                 text = text.replace(/\s+/,' ');  // all white space to single white space
22164                 
22165                     
22166                 // if next tag is '<BR>', then we can trim right..
22167                 if (node.nextSibling &&
22168                     node.nextSibling.nodeType == 1 &&
22169                     node.nextSibling.nodeName == 'BR' )
22170                 {
22171                     text = text.replace(/\s+$/g,'');
22172                 }
22173                 // if previous tag was a BR, we can also trim..
22174                 if (node.previousSibling &&
22175                     node.previousSibling.nodeType == 1 &&
22176                     node.previousSibling.nodeName == 'BR' )
22177                 {
22178                     text = this.indentstr +  text.replace(/^\s+/g,'');
22179                 }
22180                 if (text.match(/\n/)) {
22181                     text = text.replace(
22182                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22183                     );
22184                     // remoeve the last whitespace / line break.
22185                     text = text.replace(/\n\s+$/,'');
22186                 }
22187                 // repace long lines
22188                 
22189             }
22190              
22191             this.html[this.html.length] =  text;
22192             return;   
22193         }
22194         // see if previous element was a inline element.
22195         var indentstr = this.indentstr;
22196    
22197         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22198         
22199         // should trim left?
22200         if (node.previousSibling &&
22201             node.previousSibling.nodeType == 1 &&
22202             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22203         {
22204             indentstr = '';
22205             
22206         } else {
22207             this.addLine();
22208             text = text.replace(/^\s+/,''); // trim left
22209           
22210         }
22211         // should trim right?
22212         if (node.nextSibling &&
22213             node.nextSibling.nodeType == 1 &&
22214             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22215         {
22216           // noop
22217             
22218         }  else {
22219             text = text.replace(/\s+$/,''); // trim right
22220         }
22221          
22222               
22223         
22224         
22225         
22226         if (text.length < 1) {
22227             return;
22228         }
22229         if (!text.match(/\n/)) {
22230             this.html.push(indentstr + text);
22231             return;
22232         }
22233         
22234         text = this.indentstr + text.replace(
22235             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22236         );
22237         // remoeve the last whitespace / line break.
22238         text = text.replace(/\s+$/,''); 
22239         
22240         this.html.push(text);
22241         
22242         // split and indent..
22243         
22244         
22245     },
22246     /**
22247      * Writes a cdata node such as <![CDATA[data]]>.
22248      *
22249      * @method cdata
22250      * @param {String} text String to write out inside the cdata.
22251      */
22252     cdata: function(text) {
22253         this.html.push('<![CDATA[', text, ']]>');
22254     },
22255     /**
22256     * Writes a comment node such as <!-- Comment -->.
22257     *
22258     * @method cdata
22259     * @param {String} text String to write out inside the comment.
22260     */
22261    comment: function(text) {
22262        this.html.push('<!--', text, '-->');
22263    },
22264     /**
22265      * Writes a PI node such as <?xml attr="value" ?>.
22266      *
22267      * @method pi
22268      * @param {String} name Name of the pi.
22269      * @param {String} text String to write out inside the pi.
22270      */
22271     pi: function(name, text) {
22272         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22273         this.indent != '' && this.html.push('\n');
22274     },
22275     /**
22276      * Writes a doctype node such as <!DOCTYPE data>.
22277      *
22278      * @method doctype
22279      * @param {String} text String to write out inside the doctype.
22280      */
22281     doctype: function(text) {
22282         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22283     },
22284     /**
22285      * Resets the internal buffer if one wants to reuse the writer.
22286      *
22287      * @method reset
22288      */
22289     reset: function() {
22290         this.html.length = 0;
22291         this.state = [];
22292         this.pushState({
22293             indentstr : '',
22294             in_pre : false, 
22295             in_inline : false
22296         })
22297     },
22298     /**
22299      * Returns the contents that got serialized.
22300      *
22301      * @method getContent
22302      * @return {String} HTML contents that got written down.
22303      */
22304     getContent: function() {
22305         return this.html.join('').replace(/\n$/, '');
22306     },
22307     
22308     pushState : function(cfg)
22309     {
22310         this.state.push(cfg);
22311         Roo.apply(this, cfg);
22312     },
22313     
22314     popState : function()
22315     {
22316         if (this.state.length < 1) {
22317             return; // nothing to push
22318         }
22319         var cfg = {
22320             in_pre: false,
22321             indentstr : ''
22322         };
22323         this.state.pop();
22324         if (this.state.length > 0) {
22325             cfg = this.state[this.state.length-1]; 
22326         }
22327         Roo.apply(this, cfg);
22328     },
22329     
22330     addLine: function()
22331     {
22332         if (this.html.length < 1) {
22333             return;
22334         }
22335         
22336         
22337         var value = this.html[this.html.length - 1];
22338         if (value.length > 0 && '\n' !== value) {
22339             this.html.push('\n');
22340         }
22341     }
22342     
22343     
22344 //'pre script noscript style textarea video audio iframe object code'
22345 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22346 // inline 
22347 };
22348
22349 Roo.htmleditor.TidyWriter.inline_elements = [
22350         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22351         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP'
22352 ];
22353 Roo.htmleditor.TidyWriter.shortend_elements = [
22354     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22355     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22356 ];
22357
22358 Roo.htmleditor.TidyWriter.whitespace_elements = [
22359     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22360 ];/***
22361  * This is based loosely on tinymce 
22362  * @class Roo.htmleditor.TidyEntities
22363  * @static
22364  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22365  *
22366  * Not 100% sure this is actually used or needed.
22367  */
22368
22369 Roo.htmleditor.TidyEntities = {
22370     
22371     /**
22372      * initialize data..
22373      */
22374     init : function (){
22375      
22376         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22377        
22378     },
22379
22380
22381     buildEntitiesLookup: function(items, radix) {
22382         var i, chr, entity, lookup = {};
22383         if (!items) {
22384             return {};
22385         }
22386         items = typeof(items) == 'string' ? items.split(',') : items;
22387         radix = radix || 10;
22388         // Build entities lookup table
22389         for (i = 0; i < items.length; i += 2) {
22390             chr = String.fromCharCode(parseInt(items[i], radix));
22391             // Only add non base entities
22392             if (!this.baseEntities[chr]) {
22393                 entity = '&' + items[i + 1] + ';';
22394                 lookup[chr] = entity;
22395                 lookup[entity] = chr;
22396             }
22397         }
22398         return lookup;
22399         
22400     },
22401     
22402     asciiMap : {
22403             128: '€',
22404             130: '‚',
22405             131: 'ƒ',
22406             132: '„',
22407             133: '…',
22408             134: '†',
22409             135: '‡',
22410             136: 'ˆ',
22411             137: '‰',
22412             138: 'Š',
22413             139: '‹',
22414             140: 'Œ',
22415             142: 'Ž',
22416             145: '‘',
22417             146: '’',
22418             147: '“',
22419             148: '”',
22420             149: '•',
22421             150: '–',
22422             151: '—',
22423             152: '˜',
22424             153: '™',
22425             154: 'š',
22426             155: '›',
22427             156: 'œ',
22428             158: 'ž',
22429             159: 'Ÿ'
22430     },
22431     // Raw entities
22432     baseEntities : {
22433         '"': '&quot;',
22434         // Needs to be escaped since the YUI compressor would otherwise break the code
22435         '\'': '&#39;',
22436         '<': '&lt;',
22437         '>': '&gt;',
22438         '&': '&amp;',
22439         '`': '&#96;'
22440     },
22441     // Reverse lookup table for raw entities
22442     reverseEntities : {
22443         '&lt;': '<',
22444         '&gt;': '>',
22445         '&amp;': '&',
22446         '&quot;': '"',
22447         '&apos;': '\''
22448     },
22449     
22450     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22451     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22452     rawCharsRegExp : /[<>&\"\']/g,
22453     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22454     namedEntities  : false,
22455     namedEntitiesData : [ 
22456         '50',
22457         'nbsp',
22458         '51',
22459         'iexcl',
22460         '52',
22461         'cent',
22462         '53',
22463         'pound',
22464         '54',
22465         'curren',
22466         '55',
22467         'yen',
22468         '56',
22469         'brvbar',
22470         '57',
22471         'sect',
22472         '58',
22473         'uml',
22474         '59',
22475         'copy',
22476         '5a',
22477         'ordf',
22478         '5b',
22479         'laquo',
22480         '5c',
22481         'not',
22482         '5d',
22483         'shy',
22484         '5e',
22485         'reg',
22486         '5f',
22487         'macr',
22488         '5g',
22489         'deg',
22490         '5h',
22491         'plusmn',
22492         '5i',
22493         'sup2',
22494         '5j',
22495         'sup3',
22496         '5k',
22497         'acute',
22498         '5l',
22499         'micro',
22500         '5m',
22501         'para',
22502         '5n',
22503         'middot',
22504         '5o',
22505         'cedil',
22506         '5p',
22507         'sup1',
22508         '5q',
22509         'ordm',
22510         '5r',
22511         'raquo',
22512         '5s',
22513         'frac14',
22514         '5t',
22515         'frac12',
22516         '5u',
22517         'frac34',
22518         '5v',
22519         'iquest',
22520         '60',
22521         'Agrave',
22522         '61',
22523         'Aacute',
22524         '62',
22525         'Acirc',
22526         '63',
22527         'Atilde',
22528         '64',
22529         'Auml',
22530         '65',
22531         'Aring',
22532         '66',
22533         'AElig',
22534         '67',
22535         'Ccedil',
22536         '68',
22537         'Egrave',
22538         '69',
22539         'Eacute',
22540         '6a',
22541         'Ecirc',
22542         '6b',
22543         'Euml',
22544         '6c',
22545         'Igrave',
22546         '6d',
22547         'Iacute',
22548         '6e',
22549         'Icirc',
22550         '6f',
22551         'Iuml',
22552         '6g',
22553         'ETH',
22554         '6h',
22555         'Ntilde',
22556         '6i',
22557         'Ograve',
22558         '6j',
22559         'Oacute',
22560         '6k',
22561         'Ocirc',
22562         '6l',
22563         'Otilde',
22564         '6m',
22565         'Ouml',
22566         '6n',
22567         'times',
22568         '6o',
22569         'Oslash',
22570         '6p',
22571         'Ugrave',
22572         '6q',
22573         'Uacute',
22574         '6r',
22575         'Ucirc',
22576         '6s',
22577         'Uuml',
22578         '6t',
22579         'Yacute',
22580         '6u',
22581         'THORN',
22582         '6v',
22583         'szlig',
22584         '70',
22585         'agrave',
22586         '71',
22587         'aacute',
22588         '72',
22589         'acirc',
22590         '73',
22591         'atilde',
22592         '74',
22593         'auml',
22594         '75',
22595         'aring',
22596         '76',
22597         'aelig',
22598         '77',
22599         'ccedil',
22600         '78',
22601         'egrave',
22602         '79',
22603         'eacute',
22604         '7a',
22605         'ecirc',
22606         '7b',
22607         'euml',
22608         '7c',
22609         'igrave',
22610         '7d',
22611         'iacute',
22612         '7e',
22613         'icirc',
22614         '7f',
22615         'iuml',
22616         '7g',
22617         'eth',
22618         '7h',
22619         'ntilde',
22620         '7i',
22621         'ograve',
22622         '7j',
22623         'oacute',
22624         '7k',
22625         'ocirc',
22626         '7l',
22627         'otilde',
22628         '7m',
22629         'ouml',
22630         '7n',
22631         'divide',
22632         '7o',
22633         'oslash',
22634         '7p',
22635         'ugrave',
22636         '7q',
22637         'uacute',
22638         '7r',
22639         'ucirc',
22640         '7s',
22641         'uuml',
22642         '7t',
22643         'yacute',
22644         '7u',
22645         'thorn',
22646         '7v',
22647         'yuml',
22648         'ci',
22649         'fnof',
22650         'sh',
22651         'Alpha',
22652         'si',
22653         'Beta',
22654         'sj',
22655         'Gamma',
22656         'sk',
22657         'Delta',
22658         'sl',
22659         'Epsilon',
22660         'sm',
22661         'Zeta',
22662         'sn',
22663         'Eta',
22664         'so',
22665         'Theta',
22666         'sp',
22667         'Iota',
22668         'sq',
22669         'Kappa',
22670         'sr',
22671         'Lambda',
22672         'ss',
22673         'Mu',
22674         'st',
22675         'Nu',
22676         'su',
22677         'Xi',
22678         'sv',
22679         'Omicron',
22680         't0',
22681         'Pi',
22682         't1',
22683         'Rho',
22684         't3',
22685         'Sigma',
22686         't4',
22687         'Tau',
22688         't5',
22689         'Upsilon',
22690         't6',
22691         'Phi',
22692         't7',
22693         'Chi',
22694         't8',
22695         'Psi',
22696         't9',
22697         'Omega',
22698         'th',
22699         'alpha',
22700         'ti',
22701         'beta',
22702         'tj',
22703         'gamma',
22704         'tk',
22705         'delta',
22706         'tl',
22707         'epsilon',
22708         'tm',
22709         'zeta',
22710         'tn',
22711         'eta',
22712         'to',
22713         'theta',
22714         'tp',
22715         'iota',
22716         'tq',
22717         'kappa',
22718         'tr',
22719         'lambda',
22720         'ts',
22721         'mu',
22722         'tt',
22723         'nu',
22724         'tu',
22725         'xi',
22726         'tv',
22727         'omicron',
22728         'u0',
22729         'pi',
22730         'u1',
22731         'rho',
22732         'u2',
22733         'sigmaf',
22734         'u3',
22735         'sigma',
22736         'u4',
22737         'tau',
22738         'u5',
22739         'upsilon',
22740         'u6',
22741         'phi',
22742         'u7',
22743         'chi',
22744         'u8',
22745         'psi',
22746         'u9',
22747         'omega',
22748         'uh',
22749         'thetasym',
22750         'ui',
22751         'upsih',
22752         'um',
22753         'piv',
22754         '812',
22755         'bull',
22756         '816',
22757         'hellip',
22758         '81i',
22759         'prime',
22760         '81j',
22761         'Prime',
22762         '81u',
22763         'oline',
22764         '824',
22765         'frasl',
22766         '88o',
22767         'weierp',
22768         '88h',
22769         'image',
22770         '88s',
22771         'real',
22772         '892',
22773         'trade',
22774         '89l',
22775         'alefsym',
22776         '8cg',
22777         'larr',
22778         '8ch',
22779         'uarr',
22780         '8ci',
22781         'rarr',
22782         '8cj',
22783         'darr',
22784         '8ck',
22785         'harr',
22786         '8dl',
22787         'crarr',
22788         '8eg',
22789         'lArr',
22790         '8eh',
22791         'uArr',
22792         '8ei',
22793         'rArr',
22794         '8ej',
22795         'dArr',
22796         '8ek',
22797         'hArr',
22798         '8g0',
22799         'forall',
22800         '8g2',
22801         'part',
22802         '8g3',
22803         'exist',
22804         '8g5',
22805         'empty',
22806         '8g7',
22807         'nabla',
22808         '8g8',
22809         'isin',
22810         '8g9',
22811         'notin',
22812         '8gb',
22813         'ni',
22814         '8gf',
22815         'prod',
22816         '8gh',
22817         'sum',
22818         '8gi',
22819         'minus',
22820         '8gn',
22821         'lowast',
22822         '8gq',
22823         'radic',
22824         '8gt',
22825         'prop',
22826         '8gu',
22827         'infin',
22828         '8h0',
22829         'ang',
22830         '8h7',
22831         'and',
22832         '8h8',
22833         'or',
22834         '8h9',
22835         'cap',
22836         '8ha',
22837         'cup',
22838         '8hb',
22839         'int',
22840         '8hk',
22841         'there4',
22842         '8hs',
22843         'sim',
22844         '8i5',
22845         'cong',
22846         '8i8',
22847         'asymp',
22848         '8j0',
22849         'ne',
22850         '8j1',
22851         'equiv',
22852         '8j4',
22853         'le',
22854         '8j5',
22855         'ge',
22856         '8k2',
22857         'sub',
22858         '8k3',
22859         'sup',
22860         '8k4',
22861         'nsub',
22862         '8k6',
22863         'sube',
22864         '8k7',
22865         'supe',
22866         '8kl',
22867         'oplus',
22868         '8kn',
22869         'otimes',
22870         '8l5',
22871         'perp',
22872         '8m5',
22873         'sdot',
22874         '8o8',
22875         'lceil',
22876         '8o9',
22877         'rceil',
22878         '8oa',
22879         'lfloor',
22880         '8ob',
22881         'rfloor',
22882         '8p9',
22883         'lang',
22884         '8pa',
22885         'rang',
22886         '9ea',
22887         'loz',
22888         '9j0',
22889         'spades',
22890         '9j3',
22891         'clubs',
22892         '9j5',
22893         'hearts',
22894         '9j6',
22895         'diams',
22896         'ai',
22897         'OElig',
22898         'aj',
22899         'oelig',
22900         'b0',
22901         'Scaron',
22902         'b1',
22903         'scaron',
22904         'bo',
22905         'Yuml',
22906         'm6',
22907         'circ',
22908         'ms',
22909         'tilde',
22910         '802',
22911         'ensp',
22912         '803',
22913         'emsp',
22914         '809',
22915         'thinsp',
22916         '80c',
22917         'zwnj',
22918         '80d',
22919         'zwj',
22920         '80e',
22921         'lrm',
22922         '80f',
22923         'rlm',
22924         '80j',
22925         'ndash',
22926         '80k',
22927         'mdash',
22928         '80o',
22929         'lsquo',
22930         '80p',
22931         'rsquo',
22932         '80q',
22933         'sbquo',
22934         '80s',
22935         'ldquo',
22936         '80t',
22937         'rdquo',
22938         '80u',
22939         'bdquo',
22940         '810',
22941         'dagger',
22942         '811',
22943         'Dagger',
22944         '81g',
22945         'permil',
22946         '81p',
22947         'lsaquo',
22948         '81q',
22949         'rsaquo',
22950         '85c',
22951         'euro'
22952     ],
22953
22954          
22955     /**
22956      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
22957      *
22958      * @method encodeRaw
22959      * @param {String} text Text to encode.
22960      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22961      * @return {String} Entity encoded text.
22962      */
22963     encodeRaw: function(text, attr)
22964     {
22965         var t = this;
22966         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22967             return t.baseEntities[chr] || chr;
22968         });
22969     },
22970     /**
22971      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
22972      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
22973      * and is exposed as the DOMUtils.encode function.
22974      *
22975      * @method encodeAllRaw
22976      * @param {String} text Text to encode.
22977      * @return {String} Entity encoded text.
22978      */
22979     encodeAllRaw: function(text) {
22980         var t = this;
22981         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
22982             return t.baseEntities[chr] || chr;
22983         });
22984     },
22985     /**
22986      * Encodes the specified string using numeric entities. The core entities will be
22987      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
22988      *
22989      * @method encodeNumeric
22990      * @param {String} text Text to encode.
22991      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22992      * @return {String} Entity encoded text.
22993      */
22994     encodeNumeric: function(text, attr) {
22995         var t = this;
22996         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22997             // Multi byte sequence convert it to a single entity
22998             if (chr.length > 1) {
22999                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23000             }
23001             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23002         });
23003     },
23004     /**
23005      * Encodes the specified string using named entities. The core entities will be encoded
23006      * as named ones but all non lower ascii characters will be encoded into named entities.
23007      *
23008      * @method encodeNamed
23009      * @param {String} text Text to encode.
23010      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23011      * @param {Object} entities Optional parameter with entities to use.
23012      * @return {String} Entity encoded text.
23013      */
23014     encodeNamed: function(text, attr, entities) {
23015         var t = this;
23016         entities = entities || this.namedEntities;
23017         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23018             return t.baseEntities[chr] || entities[chr] || chr;
23019         });
23020     },
23021     /**
23022      * Returns an encode function based on the name(s) and it's optional entities.
23023      *
23024      * @method getEncodeFunc
23025      * @param {String} name Comma separated list of encoders for example named,numeric.
23026      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23027      * @return {function} Encode function to be used.
23028      */
23029     getEncodeFunc: function(name, entities) {
23030         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23031         var t = this;
23032         function encodeNamedAndNumeric(text, attr) {
23033             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23034                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23035             });
23036         }
23037
23038         function encodeCustomNamed(text, attr) {
23039             return t.encodeNamed(text, attr, entities);
23040         }
23041         // Replace + with , to be compatible with previous TinyMCE versions
23042         name = this.makeMap(name.replace(/\+/g, ','));
23043         // Named and numeric encoder
23044         if (name.named && name.numeric) {
23045             return this.encodeNamedAndNumeric;
23046         }
23047         // Named encoder
23048         if (name.named) {
23049             // Custom names
23050             if (entities) {
23051                 return encodeCustomNamed;
23052             }
23053             return this.encodeNamed;
23054         }
23055         // Numeric
23056         if (name.numeric) {
23057             return this.encodeNumeric;
23058         }
23059         // Raw encoder
23060         return this.encodeRaw;
23061     },
23062     /**
23063      * Decodes the specified string, this will replace entities with raw UTF characters.
23064      *
23065      * @method decode
23066      * @param {String} text Text to entity decode.
23067      * @return {String} Entity decoded string.
23068      */
23069     decode: function(text)
23070     {
23071         var  t = this;
23072         return text.replace(this.entityRegExp, function(all, numeric) {
23073             if (numeric) {
23074                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23075                 // Support upper UTF
23076                 if (numeric > 65535) {
23077                     numeric -= 65536;
23078                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23079                 }
23080                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23081             }
23082             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23083         });
23084     },
23085     nativeDecode : function (text) {
23086         return text;
23087     },
23088     makeMap : function (items, delim, map) {
23089                 var i;
23090                 items = items || [];
23091                 delim = delim || ',';
23092                 if (typeof items == "string") {
23093                         items = items.split(delim);
23094                 }
23095                 map = map || {};
23096                 i = items.length;
23097                 while (i--) {
23098                         map[items[i]] = {};
23099                 }
23100                 return map;
23101         }
23102 };
23103     
23104     
23105     
23106 Roo.htmleditor.TidyEntities.init();
23107 /**
23108  * @class Roo.htmleditor.KeyEnter
23109  * Handle Enter press..
23110  * @cfg {Roo.HtmlEditorCore} core the editor.
23111  * @constructor
23112  * Create a new Filter.
23113  * @param {Object} config Configuration options
23114  */
23115
23116
23117
23118
23119
23120 Roo.htmleditor.KeyEnter = function(cfg) {
23121     Roo.apply(this, cfg);
23122     // this does not actually call walk as it's really just a abstract class
23123  
23124     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23125 }
23126
23127 //Roo.htmleditor.KeyEnter.i = 0;
23128
23129
23130 Roo.htmleditor.KeyEnter.prototype = {
23131     
23132     core : false,
23133     
23134     keypress : function(e)
23135     {
23136         if (e.charCode != 13 && e.charCode != 10) {
23137             Roo.log([e.charCode,e]);
23138             return true;
23139         }
23140         e.preventDefault();
23141         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23142         var doc = this.core.doc;
23143           //add a new line
23144        
23145     
23146         var sel = this.core.getSelection();
23147         var range = sel.getRangeAt(0);
23148         var n = range.commonAncestorContainer;
23149         var pc = range.closest([ 'ol', 'ul']);
23150         var pli = range.closest('li');
23151         if (!pc || e.ctrlKey) {
23152             sel.insertNode('br', 'after'); 
23153          
23154             this.core.undoManager.addEvent();
23155             this.core.fireEditorEvent(e);
23156             return false;
23157         }
23158         
23159         // deal with <li> insetion
23160         if (pli.innerText.trim() == '' &&
23161             pli.previousSibling &&
23162             pli.previousSibling.nodeName == 'LI' &&
23163             pli.previousSibling.innerText.trim() ==  '') {
23164             pli.parentNode.removeChild(pli.previousSibling);
23165             sel.cursorAfter(pc);
23166             this.core.undoManager.addEvent();
23167             this.core.fireEditorEvent(e);
23168             return false;
23169         }
23170     
23171         var li = doc.createElement('LI');
23172         li.innerHTML = '&nbsp;';
23173         if (!pli || !pli.firstSibling) {
23174             pc.appendChild(li);
23175         } else {
23176             pli.parentNode.insertBefore(li, pli.firstSibling);
23177         }
23178         sel.cursorText (li.firstChild);
23179       
23180         this.core.undoManager.addEvent();
23181         this.core.fireEditorEvent(e);
23182
23183         return false;
23184         
23185     
23186         
23187         
23188          
23189     }
23190 };
23191      
23192 /**
23193  * @class Roo.htmleditor.Block
23194  * Base class for html editor blocks - do not use it directly .. extend it..
23195  * @cfg {DomElement} node The node to apply stuff to.
23196  * @cfg {String} friendly_name the name that appears in the context bar about this block
23197  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23198  
23199  * @constructor
23200  * Create a new Filter.
23201  * @param {Object} config Configuration options
23202  */
23203
23204 Roo.htmleditor.Block  = function(cfg)
23205 {
23206     // do nothing .. should not be called really.
23207 }
23208 /**
23209  * factory method to get the block from an element (using cache if necessary)
23210  * @static
23211  * @param {HtmlElement} the dom element
23212  */
23213 Roo.htmleditor.Block.factory = function(node)
23214 {
23215     var cc = Roo.htmleditor.Block.cache;
23216     var id = Roo.get(node).id;
23217     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23218         Roo.htmleditor.Block.cache[id].readElement(node);
23219         return Roo.htmleditor.Block.cache[id];
23220     }
23221     var db  = node.getAttribute('data-block');
23222     if (!db) {
23223         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23224     }
23225     var cls = Roo.htmleditor['Block' + db];
23226     if (typeof(cls) == 'undefined') {
23227         //Roo.log(node.getAttribute('data-block'));
23228         Roo.log("OOps missing block : " + 'Block' + db);
23229         return false;
23230     }
23231     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23232     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23233 };
23234
23235 /**
23236  * initalize all Elements from content that are 'blockable'
23237  * @static
23238  * @param the body element
23239  */
23240 Roo.htmleditor.Block.initAll = function(body, type)
23241 {
23242     if (typeof(type) == 'undefined') {
23243         var ia = Roo.htmleditor.Block.initAll;
23244         ia(body,'table');
23245         ia(body,'td');
23246         ia(body,'figure');
23247         return;
23248     }
23249     Roo.each(Roo.get(body).query(type), function(e) {
23250         Roo.htmleditor.Block.factory(e);    
23251     },this);
23252 };
23253 // question goes here... do we need to clear out this cache sometimes?
23254 // or show we make it relivant to the htmleditor.
23255 Roo.htmleditor.Block.cache = {};
23256
23257 Roo.htmleditor.Block.prototype = {
23258     
23259     node : false,
23260     
23261      // used by context menu
23262     friendly_name : 'Based Block',
23263     
23264     // text for button to delete this element
23265     deleteTitle : false,
23266     
23267     context : false,
23268     /**
23269      * Update a node with values from this object
23270      * @param {DomElement} node
23271      */
23272     updateElement : function(node)
23273     {
23274         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23275     },
23276      /**
23277      * convert to plain HTML for calling insertAtCursor..
23278      */
23279     toHTML : function()
23280     {
23281         return Roo.DomHelper.markup(this.toObject());
23282     },
23283     /**
23284      * used by readEleemnt to extract data from a node
23285      * may need improving as it's pretty basic
23286      
23287      * @param {DomElement} node
23288      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23289      * @param {String} attribute (use html - for contents, or style for using next param as style)
23290      * @param {String} style the style property - eg. text-align
23291      */
23292     getVal : function(node, tag, attr, style)
23293     {
23294         var n = node;
23295         if (tag !== true && n.tagName != tag.toUpperCase()) {
23296             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23297             // but kiss for now.
23298             n = node.getElementsByTagName(tag).item(0);
23299         }
23300         if (!n) {
23301             return '';
23302         }
23303         if (attr == 'html') {
23304             return n.innerHTML;
23305         }
23306         if (attr == 'style') {
23307             return n.style[style]; 
23308         }
23309         
23310         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23311             
23312     },
23313     /**
23314      * create a DomHelper friendly object - for use with 
23315      * Roo.DomHelper.markup / overwrite / etc..
23316      * (override this)
23317      */
23318     toObject : function()
23319     {
23320         return {};
23321     },
23322       /**
23323      * Read a node that has a 'data-block' property - and extract the values from it.
23324      * @param {DomElement} node - the node
23325      */
23326     readElement : function(node)
23327     {
23328         
23329     } 
23330     
23331     
23332 };
23333
23334  
23335
23336 /**
23337  * @class Roo.htmleditor.BlockFigure
23338  * Block that has an image and a figcaption
23339  * @cfg {String} image_src the url for the image
23340  * @cfg {String} align (left|right) alignment for the block default left
23341  * @cfg {String} caption the text to appear below  (and in the alt tag)
23342  * @cfg {String} caption_display (block|none) display or not the caption
23343  * @cfg {String|number} image_width the width of the image number or %?
23344  * @cfg {String|number} image_height the height of the image number or %?
23345  * 
23346  * @constructor
23347  * Create a new Filter.
23348  * @param {Object} config Configuration options
23349  */
23350
23351 Roo.htmleditor.BlockFigure = function(cfg)
23352 {
23353     if (cfg.node) {
23354         this.readElement(cfg.node);
23355         this.updateElement(cfg.node);
23356     }
23357     Roo.apply(this, cfg);
23358 }
23359 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23360  
23361     
23362     // setable values.
23363     image_src: '',
23364     align: 'center',
23365     caption : '',
23366     caption_display : 'block',
23367     width : '100%',
23368     cls : '',
23369     href: '',
23370     video_url : '',
23371     
23372     // margin: '2%', not used
23373     
23374     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23375
23376     
23377     // used by context menu
23378     friendly_name : 'Image with caption',
23379     deleteTitle : "Delete Image and Caption",
23380     
23381     contextMenu : function(toolbar)
23382     {
23383         
23384         var block = function() {
23385             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23386         };
23387         
23388         
23389         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23390         
23391         var syncValue = toolbar.editorcore.syncValue;
23392         
23393         var fields = {};
23394         
23395         return [
23396              {
23397                 xtype : 'TextItem',
23398                 text : "Source: ",
23399                 xns : rooui.Toolbar  //Boostrap?
23400             },
23401             {
23402                 xtype : 'Button',
23403                 text: 'Change Image URL',
23404                  
23405                 listeners : {
23406                     click: function (btn, state)
23407                     {
23408                         var b = block();
23409                         
23410                         Roo.MessageBox.show({
23411                             title : "Image Source URL",
23412                             msg : "Enter the url for the image",
23413                             buttons: Roo.MessageBox.OKCANCEL,
23414                             fn: function(btn, val){
23415                                 if (btn != 'ok') {
23416                                     return;
23417                                 }
23418                                 b.image_src = val;
23419                                 b.updateElement();
23420                                 syncValue();
23421                                 toolbar.editorcore.onEditorEvent();
23422                             },
23423                             minWidth:250,
23424                             prompt:true,
23425                             //multiline: multiline,
23426                             modal : true,
23427                             value : b.image_src
23428                         });
23429                     }
23430                 },
23431                 xns : rooui.Toolbar
23432             },
23433          
23434             {
23435                 xtype : 'Button',
23436                 text: 'Change Link URL',
23437                  
23438                 listeners : {
23439                     click: function (btn, state)
23440                     {
23441                         var b = block();
23442                         
23443                         Roo.MessageBox.show({
23444                             title : "Link URL",
23445                             msg : "Enter the url for the link - leave blank to have no link",
23446                             buttons: Roo.MessageBox.OKCANCEL,
23447                             fn: function(btn, val){
23448                                 if (btn != 'ok') {
23449                                     return;
23450                                 }
23451                                 b.href = val;
23452                                 b.updateElement();
23453                                 syncValue();
23454                                 toolbar.editorcore.onEditorEvent();
23455                             },
23456                             minWidth:250,
23457                             prompt:true,
23458                             //multiline: multiline,
23459                             modal : true,
23460                             value : b.href
23461                         });
23462                     }
23463                 },
23464                 xns : rooui.Toolbar
23465             },
23466             {
23467                 xtype : 'Button',
23468                 text: 'Show Video URL',
23469                  
23470                 listeners : {
23471                     click: function (btn, state)
23472                     {
23473                         Roo.MessageBox.alert("Video URL",
23474                             block().video_url == '' ? 'This image is not linked ot a video' :
23475                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23476                     }
23477                 },
23478                 xns : rooui.Toolbar
23479             },
23480             
23481             
23482             {
23483                 xtype : 'TextItem',
23484                 text : "Width: ",
23485                 xns : rooui.Toolbar  //Boostrap?
23486             },
23487             {
23488                 xtype : 'ComboBox',
23489                 allowBlank : false,
23490                 displayField : 'val',
23491                 editable : true,
23492                 listWidth : 100,
23493                 triggerAction : 'all',
23494                 typeAhead : true,
23495                 valueField : 'val',
23496                 width : 70,
23497                 name : 'width',
23498                 listeners : {
23499                     select : function (combo, r, index)
23500                     {
23501                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23502                         var b = block();
23503                         b.width = r.get('val');
23504                         b.updateElement();
23505                         syncValue();
23506                         toolbar.editorcore.onEditorEvent();
23507                     }
23508                 },
23509                 xns : rooui.form,
23510                 store : {
23511                     xtype : 'SimpleStore',
23512                     data : [
23513                         ['auto'],
23514                         ['50%'],
23515                         ['100%']
23516                     ],
23517                     fields : [ 'val'],
23518                     xns : Roo.data
23519                 }
23520             },
23521             {
23522                 xtype : 'TextItem',
23523                 text : "Align: ",
23524                 xns : rooui.Toolbar  //Boostrap?
23525             },
23526             {
23527                 xtype : 'ComboBox',
23528                 allowBlank : false,
23529                 displayField : 'val',
23530                 editable : true,
23531                 listWidth : 100,
23532                 triggerAction : 'all',
23533                 typeAhead : true,
23534                 valueField : 'val',
23535                 width : 70,
23536                 name : 'align',
23537                 listeners : {
23538                     select : function (combo, r, index)
23539                     {
23540                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23541                         var b = block();
23542                         b.align = r.get('val');
23543                         b.updateElement();
23544                         syncValue();
23545                         toolbar.editorcore.onEditorEvent();
23546                     }
23547                 },
23548                 xns : rooui.form,
23549                 store : {
23550                     xtype : 'SimpleStore',
23551                     data : [
23552                         ['left'],
23553                         ['right'],
23554                         ['center']
23555                     ],
23556                     fields : [ 'val'],
23557                     xns : Roo.data
23558                 }
23559             },
23560             
23561             
23562             {
23563                 xtype : 'Button',
23564                 text: 'Hide Caption',
23565                 name : 'caption_display',
23566                 pressed : false,
23567                 enableToggle : true,
23568                 setValue : function(v) {
23569                     this.toggle(v == 'block' ? false : true);
23570                 },
23571                 listeners : {
23572                     toggle: function (btn, state)
23573                     {
23574                         var b  = block();
23575                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23576                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23577                         b.updateElement();
23578                         syncValue();
23579                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23580                         toolbar.editorcore.onEditorEvent();
23581                     }
23582                 },
23583                 xns : rooui.Toolbar
23584             }
23585         ];
23586         
23587     },
23588     /**
23589      * create a DomHelper friendly object - for use with
23590      * Roo.DomHelper.markup / overwrite / etc..
23591      */
23592     toObject : function()
23593     {
23594         var d = document.createElement('div');
23595         d.innerHTML = this.caption;
23596         
23597         var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
23598         
23599         var img =   {
23600             tag : 'img',
23601             contenteditable : 'false',
23602             src : this.image_src,
23603             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23604             style: {
23605                 width : 'auto',
23606                 'max-width': '100%',
23607                 margin : '0px' 
23608                 
23609                 
23610             }
23611         };
23612         /*
23613         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23614                     '<a href="{2}">' + 
23615                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23616                     '</a>' + 
23617                 '</div>',
23618         */
23619                 
23620         if (this.href.length > 0) {
23621             img = {
23622                 tag : 'a',
23623                 href: this.href,
23624                 contenteditable : 'true',
23625                 cn : [
23626                     img
23627                 ]
23628             };
23629         }
23630         
23631         
23632         if (this.video_url.length > 0) {
23633             img = {
23634                 tag : 'div',
23635                 cls : this.cls,
23636                 frameborder : 0,
23637                 allowfullscreen : true,
23638                 width : 420,  // these are for video tricks - that we replace the outer
23639                 height : 315,
23640                 src : this.video_url,
23641                 cn : [
23642                     img
23643                 ]
23644             };
23645         }
23646         
23647         return  {
23648             tag: 'figure',
23649             'data-block' : 'Figure',
23650             contenteditable : 'false',
23651             style : {
23652                 display: 'block',
23653                 float :  this.align ,
23654                 'max-width':  this.width,
23655                 width : 'auto',
23656                 margin:  m,
23657                 padding: '10px'
23658                 
23659             },
23660            
23661             
23662             align : this.align,
23663             cn : [
23664                 img,
23665               
23666                 {
23667                     tag: 'figcaption',
23668                     
23669                     style : {
23670                         'text-align': 'left',
23671                         'margin-top' : '16px',
23672                         'font-size' : '16px',
23673                         'line-height' : '24px',
23674                          display : this.caption_display
23675                     },
23676                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23677                     cn : [
23678                         {
23679                             // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23680                             tag : 'i',
23681                             contenteditable : true,
23682                             html : this.caption
23683                         }
23684                     ]
23685                     
23686                 }
23687             ]
23688         };
23689          
23690     },
23691     
23692     readElement : function(node)
23693     {
23694         // this should not really come from the link...
23695         this.video_url = this.getVal(node, 'div', 'src');
23696         this.cls = this.getVal(node, 'div', 'class');
23697         this.href = this.getVal(node, 'a', 'href');
23698         
23699         this.image_src = this.getVal(node, 'img', 'src');
23700          
23701         this.align = this.getVal(node, 'figure', 'align');
23702         this.caption = this.getVal(node, 'figcaption', 'html');
23703         // remove '<i>
23704         if (this.caption.trim().match(/^<i[^>]*>/i)) {
23705             this.caption = this.caption.trim().replace(/^<i[^>]*>/i, '').replace(/^<\/i>$/i, '');
23706         }
23707         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23708         this.width = this.getVal(node, 'figure', 'style', 'max-width');
23709         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23710         
23711     },
23712     removeNode : function()
23713     {
23714         return this.node;
23715     }
23716     
23717   
23718    
23719      
23720     
23721     
23722     
23723     
23724 })
23725
23726  
23727
23728 /**
23729  * @class Roo.htmleditor.BlockTable
23730  * Block that manages a table
23731  * 
23732  * @constructor
23733  * Create a new Filter.
23734  * @param {Object} config Configuration options
23735  */
23736
23737 Roo.htmleditor.BlockTable = function(cfg)
23738 {
23739     if (cfg.node) {
23740         this.readElement(cfg.node);
23741         this.updateElement(cfg.node);
23742     }
23743     Roo.apply(this, cfg);
23744     if (!cfg.node) {
23745         this.rows = [];
23746         for(var r = 0; r < this.no_row; r++) {
23747             this.rows[r] = [];
23748             for(var c = 0; c < this.no_col; c++) {
23749                 this.rows[r][c] = this.emptyCell();
23750             }
23751         }
23752     }
23753     
23754     
23755 }
23756 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23757  
23758     rows : false,
23759     no_col : 1,
23760     no_row : 1,
23761     
23762     
23763     width: '100%',
23764     
23765     // used by context menu
23766     friendly_name : 'Table',
23767     deleteTitle : 'Delete Table',
23768     // context menu is drawn once..
23769     
23770     contextMenu : function(toolbar)
23771     {
23772         
23773         var block = function() {
23774             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23775         };
23776         
23777         
23778         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23779         
23780         var syncValue = toolbar.editorcore.syncValue;
23781         
23782         var fields = {};
23783         
23784         return [
23785             {
23786                 xtype : 'TextItem',
23787                 text : "Width: ",
23788                 xns : rooui.Toolbar  //Boostrap?
23789             },
23790             {
23791                 xtype : 'ComboBox',
23792                 allowBlank : false,
23793                 displayField : 'val',
23794                 editable : true,
23795                 listWidth : 100,
23796                 triggerAction : 'all',
23797                 typeAhead : true,
23798                 valueField : 'val',
23799                 width : 100,
23800                 name : 'width',
23801                 listeners : {
23802                     select : function (combo, r, index)
23803                     {
23804                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23805                         var b = block();
23806                         b.width = r.get('val');
23807                         b.updateElement();
23808                         syncValue();
23809                         toolbar.editorcore.onEditorEvent();
23810                     }
23811                 },
23812                 xns : rooui.form,
23813                 store : {
23814                     xtype : 'SimpleStore',
23815                     data : [
23816                         ['100%'],
23817                         ['auto']
23818                     ],
23819                     fields : [ 'val'],
23820                     xns : Roo.data
23821                 }
23822             },
23823             // -------- Cols
23824             
23825             {
23826                 xtype : 'TextItem',
23827                 text : "Columns: ",
23828                 xns : rooui.Toolbar  //Boostrap?
23829             },
23830          
23831             {
23832                 xtype : 'Button',
23833                 text: '-',
23834                 listeners : {
23835                     click : function (_self, e)
23836                     {
23837                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23838                         block().removeColumn();
23839                         syncValue();
23840                         toolbar.editorcore.onEditorEvent();
23841                     }
23842                 },
23843                 xns : rooui.Toolbar
23844             },
23845             {
23846                 xtype : 'Button',
23847                 text: '+',
23848                 listeners : {
23849                     click : function (_self, e)
23850                     {
23851                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23852                         block().addColumn();
23853                         syncValue();
23854                         toolbar.editorcore.onEditorEvent();
23855                     }
23856                 },
23857                 xns : rooui.Toolbar
23858             },
23859             // -------- ROWS
23860             {
23861                 xtype : 'TextItem',
23862                 text : "Rows: ",
23863                 xns : rooui.Toolbar  //Boostrap?
23864             },
23865          
23866             {
23867                 xtype : 'Button',
23868                 text: '-',
23869                 listeners : {
23870                     click : function (_self, e)
23871                     {
23872                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23873                         block().removeRow();
23874                         syncValue();
23875                         toolbar.editorcore.onEditorEvent();
23876                     }
23877                 },
23878                 xns : rooui.Toolbar
23879             },
23880             {
23881                 xtype : 'Button',
23882                 text: '+',
23883                 listeners : {
23884                     click : function (_self, e)
23885                     {
23886                         block().addRow();
23887                         syncValue();
23888                         toolbar.editorcore.onEditorEvent();
23889                     }
23890                 },
23891                 xns : rooui.Toolbar
23892             },
23893             // -------- ROWS
23894             {
23895                 xtype : 'Button',
23896                 text: 'Reset Column Widths',
23897                 listeners : {
23898                     
23899                     click : function (_self, e)
23900                     {
23901                         block().resetWidths();
23902                         syncValue();
23903                         toolbar.editorcore.onEditorEvent();
23904                     }
23905                 },
23906                 xns : rooui.Toolbar
23907             } 
23908             
23909             
23910             
23911         ];
23912         
23913     },
23914     
23915     
23916   /**
23917      * create a DomHelper friendly object - for use with
23918      * Roo.DomHelper.markup / overwrite / etc..
23919      * ?? should it be called with option to hide all editing features?
23920      */
23921     toObject : function()
23922     {
23923         
23924         var ret = {
23925             tag : 'table',
23926             contenteditable : 'false', // this stops cell selection from picking the table.
23927             'data-block' : 'Table',
23928             style : {
23929                 width:  this.width,
23930                 border : 'solid 1px #000', // ??? hard coded?
23931                 'border-collapse' : 'collapse' 
23932             },
23933             cn : [
23934                 { tag : 'tbody' , cn : [] }
23935             ]
23936         };
23937         
23938         // do we have a head = not really 
23939         var ncols = 0;
23940         Roo.each(this.rows, function( row ) {
23941             var tr = {
23942                 tag: 'tr',
23943                 style : {
23944                     margin: '6px',
23945                     border : 'solid 1px #000',
23946                     textAlign : 'left' 
23947                 },
23948                 cn : [ ]
23949             };
23950             
23951             ret.cn[0].cn.push(tr);
23952             // does the row have any properties? ?? height?
23953             var nc = 0;
23954             Roo.each(row, function( cell ) {
23955                 
23956                 var td = {
23957                     tag : 'td',
23958                     contenteditable :  'true',
23959                     'data-block' : 'Td',
23960                     html : cell.html,
23961                     style : cell.style
23962                 };
23963                 if (cell.colspan > 1) {
23964                     td.colspan = cell.colspan ;
23965                     nc += cell.colspan;
23966                 } else {
23967                     nc++;
23968                 }
23969                 if (cell.rowspan > 1) {
23970                     td.rowspan = cell.rowspan ;
23971                 }
23972                 
23973                 
23974                 // widths ?
23975                 tr.cn.push(td);
23976                     
23977                 
23978             }, this);
23979             ncols = Math.max(nc, ncols);
23980             
23981             
23982         }, this);
23983         // add the header row..
23984         
23985         ncols++;
23986          
23987         
23988         return ret;
23989          
23990     },
23991     
23992     readElement : function(node)
23993     {
23994         node  = node ? node : this.node ;
23995         this.width = this.getVal(node, true, 'style', 'width') || '100%';
23996         
23997         this.rows = [];
23998         this.no_row = 0;
23999         var trs = Array.from(node.rows);
24000         trs.forEach(function(tr) {
24001             var row =  [];
24002             this.rows.push(row);
24003             
24004             this.no_row++;
24005             var no_column = 0;
24006             Array.from(tr.cells).forEach(function(td) {
24007                 
24008                 var add = {
24009                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24010                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24011                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24012                     html : td.innerHTML
24013                 };
24014                 no_column += add.colspan;
24015                      
24016                 
24017                 row.push(add);
24018                 
24019                 
24020             },this);
24021             this.no_col = Math.max(this.no_col, no_column);
24022             
24023             
24024         },this);
24025         
24026         
24027     },
24028     normalizeRows: function()
24029     {
24030         var ret= [];
24031         var rid = -1;
24032         this.rows.forEach(function(row) {
24033             rid++;
24034             ret[rid] = [];
24035             row = this.normalizeRow(row);
24036             var cid = 0;
24037             row.forEach(function(c) {
24038                 while (typeof(ret[rid][cid]) != 'undefined') {
24039                     cid++;
24040                 }
24041                 if (typeof(ret[rid]) == 'undefined') {
24042                     ret[rid] = [];
24043                 }
24044                 ret[rid][cid] = c;
24045                 c.row = rid;
24046                 c.col = cid;
24047                 if (c.rowspan < 2) {
24048                     return;
24049                 }
24050                 
24051                 for(var i = 1 ;i < c.rowspan; i++) {
24052                     if (typeof(ret[rid+i]) == 'undefined') {
24053                         ret[rid+i] = [];
24054                     }
24055                     ret[rid+i][cid] = c;
24056                 }
24057             });
24058         }, this);
24059         return ret;
24060     
24061     },
24062     
24063     normalizeRow: function(row)
24064     {
24065         var ret= [];
24066         row.forEach(function(c) {
24067             if (c.colspan < 2) {
24068                 ret.push(c);
24069                 return;
24070             }
24071             for(var i =0 ;i < c.colspan; i++) {
24072                 ret.push(c);
24073             }
24074         });
24075         return ret;
24076     
24077     },
24078     
24079     deleteColumn : function(sel)
24080     {
24081         if (!sel || sel.type != 'col') {
24082             return;
24083         }
24084         if (this.no_col < 2) {
24085             return;
24086         }
24087         
24088         this.rows.forEach(function(row) {
24089             var cols = this.normalizeRow(row);
24090             var col = cols[sel.col];
24091             if (col.colspan > 1) {
24092                 col.colspan --;
24093             } else {
24094                 row.remove(col);
24095             }
24096             
24097         }, this);
24098         this.no_col--;
24099         
24100     },
24101     removeColumn : function()
24102     {
24103         this.deleteColumn({
24104             type: 'col',
24105             col : this.no_col-1
24106         });
24107         this.updateElement();
24108     },
24109     
24110      
24111     addColumn : function()
24112     {
24113         
24114         this.rows.forEach(function(row) {
24115             row.push(this.emptyCell());
24116            
24117         }, this);
24118         this.updateElement();
24119     },
24120     
24121     deleteRow : function(sel)
24122     {
24123         if (!sel || sel.type != 'row') {
24124             return;
24125         }
24126         
24127         if (this.no_row < 2) {
24128             return;
24129         }
24130         
24131         var rows = this.normalizeRows();
24132         
24133         
24134         rows[sel.row].forEach(function(col) {
24135             if (col.rowspan > 1) {
24136                 col.rowspan--;
24137             } else {
24138                 col.remove = 1; // flage it as removed.
24139             }
24140             
24141         }, this);
24142         var newrows = [];
24143         this.rows.forEach(function(row) {
24144             newrow = [];
24145             row.forEach(function(c) {
24146                 if (typeof(c.remove) == 'undefined') {
24147                     newrow.push(c);
24148                 }
24149                 
24150             });
24151             if (newrow.length > 0) {
24152                 newrows.push(row);
24153             }
24154         });
24155         this.rows =  newrows;
24156         
24157         
24158         
24159         this.no_row--;
24160         this.updateElement();
24161         
24162     },
24163     removeRow : function()
24164     {
24165         this.deleteRow({
24166             type: 'row',
24167             row : this.no_row-1
24168         });
24169         
24170     },
24171     
24172      
24173     addRow : function()
24174     {
24175         
24176         var row = [];
24177         for (var i = 0; i < this.no_col; i++ ) {
24178             
24179             row.push(this.emptyCell());
24180            
24181         }
24182         this.rows.push(row);
24183         this.updateElement();
24184         
24185     },
24186      
24187     // the default cell object... at present...
24188     emptyCell : function() {
24189         return (new Roo.htmleditor.BlockTd({})).toObject();
24190         
24191      
24192     },
24193     
24194     removeNode : function()
24195     {
24196         return this.node;
24197     },
24198     
24199     
24200     
24201     resetWidths : function()
24202     {
24203         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24204             var nn = Roo.htmleditor.Block.factory(n);
24205             nn.width = '';
24206             nn.updateElement(n);
24207         });
24208     }
24209     
24210     
24211     
24212     
24213 })
24214
24215 /**
24216  *
24217  * editing a TD?
24218  *
24219  * since selections really work on the table cell, then editing really should work from there
24220  *
24221  * The original plan was to support merging etc... - but that may not be needed yet..
24222  *
24223  * So this simple version will support:
24224  *   add/remove cols
24225  *   adjust the width +/-
24226  *   reset the width...
24227  *   
24228  *
24229  */
24230
24231
24232  
24233
24234 /**
24235  * @class Roo.htmleditor.BlockTable
24236  * Block that manages a table
24237  * 
24238  * @constructor
24239  * Create a new Filter.
24240  * @param {Object} config Configuration options
24241  */
24242
24243 Roo.htmleditor.BlockTd = function(cfg)
24244 {
24245     if (cfg.node) {
24246         this.readElement(cfg.node);
24247         this.updateElement(cfg.node);
24248     }
24249     Roo.apply(this, cfg);
24250      
24251     
24252     
24253 }
24254 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24255  
24256     node : false,
24257     
24258     width: '',
24259     textAlign : 'left',
24260     valign : 'top',
24261     
24262     colspan : 1,
24263     rowspan : 1,
24264     
24265     
24266     // used by context menu
24267     friendly_name : 'Table Cell',
24268     deleteTitle : false, // use our customer delete
24269     
24270     // context menu is drawn once..
24271     
24272     contextMenu : function(toolbar)
24273     {
24274         
24275         var cell = function() {
24276             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24277         };
24278         
24279         var table = function() {
24280             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24281         };
24282         
24283         var lr = false;
24284         var saveSel = function()
24285         {
24286             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24287         }
24288         var restoreSel = function()
24289         {
24290             if (lr) {
24291                 (function() {
24292                     toolbar.editorcore.focus();
24293                     var cr = toolbar.editorcore.getSelection();
24294                     cr.removeAllRanges();
24295                     cr.addRange(lr);
24296                     toolbar.editorcore.onEditorEvent();
24297                 }).defer(10, this);
24298                 
24299                 
24300             }
24301         }
24302         
24303         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24304         
24305         var syncValue = toolbar.editorcore.syncValue;
24306         
24307         var fields = {};
24308         
24309         return [
24310             {
24311                 xtype : 'Button',
24312                 text : 'Edit Table',
24313                 listeners : {
24314                     click : function() {
24315                         var t = toolbar.tb.selectedNode.closest('table');
24316                         toolbar.editorcore.selectNode(t);
24317                         toolbar.editorcore.onEditorEvent();                        
24318                     }
24319                 }
24320                 
24321             },
24322               
24323            
24324              
24325             {
24326                 xtype : 'TextItem',
24327                 text : "Column Width: ",
24328                  xns : rooui.Toolbar 
24329                
24330             },
24331             {
24332                 xtype : 'Button',
24333                 text: '-',
24334                 listeners : {
24335                     click : function (_self, e)
24336                     {
24337                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24338                         cell().shrinkColumn();
24339                         syncValue();
24340                          toolbar.editorcore.onEditorEvent();
24341                     }
24342                 },
24343                 xns : rooui.Toolbar
24344             },
24345             {
24346                 xtype : 'Button',
24347                 text: '+',
24348                 listeners : {
24349                     click : function (_self, e)
24350                     {
24351                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24352                         cell().growColumn();
24353                         syncValue();
24354                         toolbar.editorcore.onEditorEvent();
24355                     }
24356                 },
24357                 xns : rooui.Toolbar
24358             },
24359             
24360             {
24361                 xtype : 'TextItem',
24362                 text : "Vertical Align: ",
24363                 xns : rooui.Toolbar  //Boostrap?
24364             },
24365             {
24366                 xtype : 'ComboBox',
24367                 allowBlank : false,
24368                 displayField : 'val',
24369                 editable : true,
24370                 listWidth : 100,
24371                 triggerAction : 'all',
24372                 typeAhead : true,
24373                 valueField : 'val',
24374                 width : 100,
24375                 name : 'valign',
24376                 listeners : {
24377                     select : function (combo, r, index)
24378                     {
24379                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24380                         var b = cell();
24381                         b.valign = r.get('val');
24382                         b.updateElement();
24383                         syncValue();
24384                         toolbar.editorcore.onEditorEvent();
24385                     }
24386                 },
24387                 xns : rooui.form,
24388                 store : {
24389                     xtype : 'SimpleStore',
24390                     data : [
24391                         ['top'],
24392                         ['middle'],
24393                         ['bottom'] // there are afew more... 
24394                     ],
24395                     fields : [ 'val'],
24396                     xns : Roo.data
24397                 }
24398             },
24399             
24400             {
24401                 xtype : 'TextItem',
24402                 text : "Merge Cells: ",
24403                  xns : rooui.Toolbar 
24404                
24405             },
24406             
24407             
24408             {
24409                 xtype : 'Button',
24410                 text: 'Right',
24411                 listeners : {
24412                     click : function (_self, e)
24413                     {
24414                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24415                         cell().mergeRight();
24416                         //block().growColumn();
24417                         syncValue();
24418                         toolbar.editorcore.onEditorEvent();
24419                     }
24420                 },
24421                 xns : rooui.Toolbar
24422             },
24423              
24424             {
24425                 xtype : 'Button',
24426                 text: 'Below',
24427                 listeners : {
24428                     click : function (_self, e)
24429                     {
24430                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24431                         cell().mergeBelow();
24432                         //block().growColumn();
24433                         syncValue();
24434                         toolbar.editorcore.onEditorEvent();
24435                     }
24436                 },
24437                 xns : rooui.Toolbar
24438             },
24439             {
24440                 xtype : 'TextItem',
24441                 text : "| ",
24442                  xns : rooui.Toolbar 
24443                
24444             },
24445             
24446             {
24447                 xtype : 'Button',
24448                 text: 'Split',
24449                 listeners : {
24450                     click : function (_self, e)
24451                     {
24452                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24453                         cell().split();
24454                         syncValue();
24455                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24456                         toolbar.editorcore.onEditorEvent();
24457                                              
24458                     }
24459                 },
24460                 xns : rooui.Toolbar
24461             },
24462             {
24463                 xtype : 'Fill',
24464                 xns : rooui.Toolbar 
24465                
24466             },
24467         
24468           
24469             {
24470                 xtype : 'Button',
24471                 text: 'Delete',
24472                  
24473                 xns : rooui.Toolbar,
24474                 menu : {
24475                     xtype : 'Menu',
24476                     xns : rooui.menu,
24477                     items : [
24478                         {
24479                             xtype : 'Item',
24480                             html: 'Column',
24481                             listeners : {
24482                                 click : function (_self, e)
24483                                 {
24484                                     var t = table();
24485                                     
24486                                     cell().deleteColumn();
24487                                     syncValue();
24488                                     toolbar.editorcore.selectNode(t.node);
24489                                     toolbar.editorcore.onEditorEvent();   
24490                                 }
24491                             },
24492                             xns : rooui.menu
24493                         },
24494                         {
24495                             xtype : 'Item',
24496                             html: 'Row',
24497                             listeners : {
24498                                 click : function (_self, e)
24499                                 {
24500                                     var t = table();
24501                                     cell().deleteRow();
24502                                     syncValue();
24503                                     
24504                                     toolbar.editorcore.selectNode(t.node);
24505                                     toolbar.editorcore.onEditorEvent();   
24506                                                          
24507                                 }
24508                             },
24509                             xns : rooui.menu
24510                         },
24511                        {
24512                             xtype : 'Separator',
24513                             xns : rooui.menu
24514                         },
24515                         {
24516                             xtype : 'Item',
24517                             html: 'Table',
24518                             listeners : {
24519                                 click : function (_self, e)
24520                                 {
24521                                     var t = table();
24522                                     var nn = t.node.nextSibling || t.node.previousSibling;
24523                                     t.node.parentNode.removeChild(t.node);
24524                                     if (nn) { 
24525                                         toolbar.editorcore.selectNode(nn, true);
24526                                     }
24527                                     toolbar.editorcore.onEditorEvent();   
24528                                                          
24529                                 }
24530                             },
24531                             xns : rooui.menu
24532                         }
24533                     ]
24534                 }
24535             }
24536             
24537             // align... << fixme
24538             
24539         ];
24540         
24541     },
24542     
24543     
24544   /**
24545      * create a DomHelper friendly object - for use with
24546      * Roo.DomHelper.markup / overwrite / etc..
24547      * ?? should it be called with option to hide all editing features?
24548      */
24549  /**
24550      * create a DomHelper friendly object - for use with
24551      * Roo.DomHelper.markup / overwrite / etc..
24552      * ?? should it be called with option to hide all editing features?
24553      */
24554     toObject : function()
24555     {
24556         
24557         var ret = {
24558             tag : 'td',
24559             contenteditable : 'true', // this stops cell selection from picking the table.
24560             'data-block' : 'Td',
24561             valign : this.valign,
24562             style : {  
24563                 'text-align' :  this.textAlign,
24564                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24565                 'border-collapse' : 'collapse',
24566                 padding : '6px', // 8 for desktop / 4 for mobile
24567                 'vertical-align': this.valign
24568             },
24569             html : this.html
24570         };
24571         if (this.width != '') {
24572             ret.width = this.width;
24573             ret.style.width = this.width;
24574         }
24575         
24576         
24577         if (this.colspan > 1) {
24578             ret.colspan = this.colspan ;
24579         } 
24580         if (this.rowspan > 1) {
24581             ret.rowspan = this.rowspan ;
24582         }
24583         
24584            
24585         
24586         return ret;
24587          
24588     },
24589     
24590     readElement : function(node)
24591     {
24592         node  = node ? node : this.node ;
24593         this.width = node.style.width;
24594         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24595         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24596         this.html = node.innerHTML;
24597         
24598         
24599     },
24600      
24601     // the default cell object... at present...
24602     emptyCell : function() {
24603         return {
24604             colspan :  1,
24605             rowspan :  1,
24606             textAlign : 'left',
24607             html : "&nbsp;" // is this going to be editable now?
24608         };
24609      
24610     },
24611     
24612     removeNode : function()
24613     {
24614         return this.node.closest('table');
24615          
24616     },
24617     
24618     cellData : false,
24619     
24620     colWidths : false,
24621     
24622     toTableArray  : function()
24623     {
24624         var ret = [];
24625         var tab = this.node.closest('tr').closest('table');
24626         Array.from(tab.rows).forEach(function(r, ri){
24627             ret[ri] = [];
24628         });
24629         var rn = 0;
24630         this.colWidths = [];
24631         var all_auto = true;
24632         Array.from(tab.rows).forEach(function(r, ri){
24633             
24634             var cn = 0;
24635             Array.from(r.cells).forEach(function(ce, ci){
24636                 var c =  {
24637                     cell : ce,
24638                     row : rn,
24639                     col: cn,
24640                     colspan : ce.colSpan,
24641                     rowspan : ce.rowSpan
24642                 };
24643                 if (ce.isEqualNode(this.node)) {
24644                     this.cellData = c;
24645                 }
24646                 // if we have been filled up by a row?
24647                 if (typeof(ret[rn][cn]) != 'undefined') {
24648                     while(typeof(ret[rn][cn]) != 'undefined') {
24649                         cn++;
24650                     }
24651                     c.col = cn;
24652                 }
24653                 
24654                 if (typeof(this.colWidths[cn]) == 'undefined') {
24655                     this.colWidths[cn] =   ce.style.width;
24656                     if (this.colWidths[cn] != '') {
24657                         all_auto = false;
24658                     }
24659                 }
24660                 
24661                 
24662                 if (c.colspan < 2 && c.rowspan < 2 ) {
24663                     ret[rn][cn] = c;
24664                     cn++;
24665                     return;
24666                 }
24667                 for(var j = 0; j < c.rowspan; j++) {
24668                     if (typeof(ret[rn+j]) == 'undefined') {
24669                         continue; // we have a problem..
24670                     }
24671                     ret[rn+j][cn] = c;
24672                     for(var i = 0; i < c.colspan; i++) {
24673                         ret[rn+j][cn+i] = c;
24674                     }
24675                 }
24676                 
24677                 cn += c.colspan;
24678             }, this);
24679             rn++;
24680         }, this);
24681         
24682         // initalize widths.?
24683         // either all widths or no widths..
24684         if (all_auto) {
24685             this.colWidths[0] = false; // no widths flag.
24686         }
24687         
24688         
24689         return ret;
24690         
24691     },
24692     
24693     
24694     
24695     
24696     mergeRight: function()
24697     {
24698          
24699         // get the contents of the next cell along..
24700         var tr = this.node.closest('tr');
24701         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24702         if (i >= tr.childNodes.length - 1) {
24703             return; // no cells on right to merge with.
24704         }
24705         var table = this.toTableArray();
24706         
24707         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24708             return; // nothing right?
24709         }
24710         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24711         // right cell - must be same rowspan and on the same row.
24712         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24713             return; // right hand side is not same rowspan.
24714         }
24715         
24716         
24717         
24718         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24719         tr.removeChild(rc.cell);
24720         this.colspan += rc.colspan;
24721         this.node.setAttribute('colspan', this.colspan);
24722
24723     },
24724     
24725     
24726     mergeBelow : function()
24727     {
24728         var table = this.toTableArray();
24729         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24730             return; // no row below
24731         }
24732         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24733             return; // nothing right?
24734         }
24735         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24736         
24737         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24738             return; // right hand side is not same rowspan.
24739         }
24740         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24741         rc.cell.parentNode.removeChild(rc.cell);
24742         this.rowspan += rc.rowspan;
24743         this.node.setAttribute('rowspan', this.rowspan);
24744     },
24745     
24746     split: function()
24747     {
24748         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24749             return;
24750         }
24751         var table = this.toTableArray();
24752         var cd = this.cellData;
24753         this.rowspan = 1;
24754         this.colspan = 1;
24755         
24756         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24757             
24758             
24759             
24760             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24761                 if (r == cd.row && c == cd.col) {
24762                     this.node.removeAttribute('rowspan');
24763                     this.node.removeAttribute('colspan');
24764                     continue;
24765                 }
24766                  
24767                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24768                 ntd.removeAttribute('id'); //
24769                 //ntd.style.width  = '';
24770                 ntd.innerHTML = '';
24771                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24772             }
24773             
24774         }
24775         this.redrawAllCells(table);
24776         
24777          
24778         
24779     },
24780     
24781     
24782     
24783     redrawAllCells: function(table)
24784     {
24785         
24786          
24787         var tab = this.node.closest('tr').closest('table');
24788         var ctr = tab.rows[0].parentNode;
24789         Array.from(tab.rows).forEach(function(r, ri){
24790             
24791             Array.from(r.cells).forEach(function(ce, ci){
24792                 ce.parentNode.removeChild(ce);
24793             });
24794             r.parentNode.removeChild(r);
24795         });
24796         for(var r = 0 ; r < table.length; r++) {
24797             var re = tab.rows[r];
24798             
24799             var re = tab.ownerDocument.createElement('tr');
24800             ctr.appendChild(re);
24801             for(var c = 0 ; c < table[r].length; c++) {
24802                 if (table[r][c].cell === false) {
24803                     continue;
24804                 }
24805                 
24806                 re.appendChild(table[r][c].cell);
24807                  
24808                 table[r][c].cell = false;
24809             }
24810         }
24811         
24812     },
24813     updateWidths : function(table)
24814     {
24815         for(var r = 0 ; r < table.length; r++) {
24816            
24817             for(var c = 0 ; c < table[r].length; c++) {
24818                 if (table[r][c].cell === false) {
24819                     continue;
24820                 }
24821                 
24822                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
24823                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
24824                     el.width = Math.floor(this.colWidths[c])  +'%';
24825                     el.updateElement(el.node);
24826                 }
24827                 table[r][c].cell = false; // done
24828             }
24829         }
24830     },
24831     normalizeWidths : function(table)
24832     {
24833     
24834         if (this.colWidths[0] === false) {
24835             var nw = 100.0 / this.colWidths.length;
24836             this.colWidths.forEach(function(w,i) {
24837                 this.colWidths[i] = nw;
24838             },this);
24839             return;
24840         }
24841     
24842         var t = 0, missing = [];
24843         
24844         this.colWidths.forEach(function(w,i) {
24845             //if you mix % and
24846             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
24847             var add =  this.colWidths[i];
24848             if (add > 0) {
24849                 t+=add;
24850                 return;
24851             }
24852             missing.push(i);
24853             
24854             
24855         },this);
24856         var nc = this.colWidths.length;
24857         if (missing.length) {
24858             var mult = (nc - missing.length) / (1.0 * nc);
24859             var t = mult * t;
24860             var ew = (100 -t) / (1.0 * missing.length);
24861             this.colWidths.forEach(function(w,i) {
24862                 if (w > 0) {
24863                     this.colWidths[i] = w * mult;
24864                     return;
24865                 }
24866                 
24867                 this.colWidths[i] = ew;
24868             }, this);
24869             // have to make up numbers..
24870              
24871         }
24872         // now we should have all the widths..
24873         
24874     
24875     },
24876     
24877     shrinkColumn : function()
24878     {
24879         var table = this.toTableArray();
24880         this.normalizeWidths(table);
24881         var col = this.cellData.col;
24882         var nw = this.colWidths[col] * 0.8;
24883         if (nw < 5) {
24884             return;
24885         }
24886         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24887         this.colWidths.forEach(function(w,i) {
24888             if (i == col) {
24889                  this.colWidths[i] = nw;
24890                 return;
24891             }
24892             this.colWidths[i] += otherAdd
24893         }, this);
24894         this.updateWidths(table);
24895          
24896     },
24897     growColumn : function()
24898     {
24899         var table = this.toTableArray();
24900         this.normalizeWidths(table);
24901         var col = this.cellData.col;
24902         var nw = this.colWidths[col] * 1.2;
24903         if (nw > 90) {
24904             return;
24905         }
24906         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24907         this.colWidths.forEach(function(w,i) {
24908             if (i == col) {
24909                 this.colWidths[i] = nw;
24910                 return;
24911             }
24912             this.colWidths[i] -= otherSub
24913         }, this);
24914         this.updateWidths(table);
24915          
24916     },
24917     deleteRow : function()
24918     {
24919         // delete this rows 'tr'
24920         // if any of the cells in this row have a rowspan > 1 && row!= this row..
24921         // then reduce the rowspan.
24922         var table = this.toTableArray();
24923         // this.cellData.row;
24924         for (var i =0;i< table[this.cellData.row].length ; i++) {
24925             var c = table[this.cellData.row][i];
24926             if (c.row != this.cellData.row) {
24927                 
24928                 c.rowspan--;
24929                 c.cell.setAttribute('rowspan', c.rowspan);
24930                 continue;
24931             }
24932             if (c.rowspan > 1) {
24933                 c.rowspan--;
24934                 c.cell.setAttribute('rowspan', c.rowspan);
24935             }
24936         }
24937         table.splice(this.cellData.row,1);
24938         this.redrawAllCells(table);
24939         
24940     },
24941     deleteColumn : function()
24942     {
24943         var table = this.toTableArray();
24944         
24945         for (var i =0;i< table.length ; i++) {
24946             var c = table[i][this.cellData.col];
24947             if (c.col != this.cellData.col) {
24948                 table[i][this.cellData.col].colspan--;
24949             } else if (c.colspan > 1) {
24950                 c.colspan--;
24951                 c.cell.setAttribute('colspan', c.colspan);
24952             }
24953             table[i].splice(this.cellData.col,1);
24954         }
24955         
24956         this.redrawAllCells(table);
24957     }
24958     
24959     
24960     
24961     
24962 })
24963
24964 //<script type="text/javascript">
24965
24966 /*
24967  * Based  Ext JS Library 1.1.1
24968  * Copyright(c) 2006-2007, Ext JS, LLC.
24969  * LGPL
24970  *
24971  */
24972  
24973 /**
24974  * @class Roo.HtmlEditorCore
24975  * @extends Roo.Component
24976  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24977  *
24978  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24979  */
24980
24981 Roo.HtmlEditorCore = function(config){
24982     
24983     
24984     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24985     
24986     
24987     this.addEvents({
24988         /**
24989          * @event initialize
24990          * Fires when the editor is fully initialized (including the iframe)
24991          * @param {Roo.HtmlEditorCore} this
24992          */
24993         initialize: true,
24994         /**
24995          * @event activate
24996          * Fires when the editor is first receives the focus. Any insertion must wait
24997          * until after this event.
24998          * @param {Roo.HtmlEditorCore} this
24999          */
25000         activate: true,
25001          /**
25002          * @event beforesync
25003          * Fires before the textarea is updated with content from the editor iframe. Return false
25004          * to cancel the sync.
25005          * @param {Roo.HtmlEditorCore} this
25006          * @param {String} html
25007          */
25008         beforesync: true,
25009          /**
25010          * @event beforepush
25011          * Fires before the iframe editor is updated with content from the textarea. Return false
25012          * to cancel the push.
25013          * @param {Roo.HtmlEditorCore} this
25014          * @param {String} html
25015          */
25016         beforepush: true,
25017          /**
25018          * @event sync
25019          * Fires when the textarea is updated with content from the editor iframe.
25020          * @param {Roo.HtmlEditorCore} this
25021          * @param {String} html
25022          */
25023         sync: true,
25024          /**
25025          * @event push
25026          * Fires when the iframe editor is updated with content from the textarea.
25027          * @param {Roo.HtmlEditorCore} this
25028          * @param {String} html
25029          */
25030         push: true,
25031         
25032         /**
25033          * @event editorevent
25034          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25035          * @param {Roo.HtmlEditorCore} this
25036          */
25037         editorevent: true 
25038          
25039         
25040     });
25041     
25042     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25043     
25044     // defaults : white / black...
25045     this.applyBlacklists();
25046     
25047     
25048     
25049 };
25050
25051
25052 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25053
25054
25055      /**
25056      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25057      */
25058     
25059     owner : false,
25060     
25061      /**
25062      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25063      *                        Roo.resizable.
25064      */
25065     resizable : false,
25066      /**
25067      * @cfg {Number} height (in pixels)
25068      */   
25069     height: 300,
25070    /**
25071      * @cfg {Number} width (in pixels)
25072      */   
25073     width: 500,
25074      /**
25075      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25076      *         if you are doing an email editor, this probably needs disabling, it's designed
25077      */
25078     autoClean: true,
25079     
25080     /**
25081      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25082      */
25083     enableBlocks : true,
25084     /**
25085      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25086      * 
25087      */
25088     stylesheets: false,
25089      /**
25090      * @cfg {String} language default en - language of text (usefull for rtl languages)
25091      * 
25092      */
25093     language: 'en',
25094     
25095     /**
25096      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25097      *          - by default they are stripped - if you are editing email you may need this.
25098      */
25099     allowComments: false,
25100     // id of frame..
25101     frameId: false,
25102     
25103     // private properties
25104     validationEvent : false,
25105     deferHeight: true,
25106     initialized : false,
25107     activated : false,
25108     sourceEditMode : false,
25109     onFocus : Roo.emptyFn,
25110     iframePad:3,
25111     hideMode:'offsets',
25112     
25113     clearUp: true,
25114     
25115     // blacklist + whitelisted elements..
25116     black: false,
25117     white: false,
25118      
25119     bodyCls : '',
25120
25121     
25122     undoManager : false,
25123     /**
25124      * Protected method that will not generally be called directly. It
25125      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25126      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25127      */
25128     getDocMarkup : function(){
25129         // body styles..
25130         var st = '';
25131         
25132         // inherit styels from page...?? 
25133         if (this.stylesheets === false) {
25134             
25135             Roo.get(document.head).select('style').each(function(node) {
25136                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25137             });
25138             
25139             Roo.get(document.head).select('link').each(function(node) { 
25140                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25141             });
25142             
25143         } else if (!this.stylesheets.length) {
25144                 // simple..
25145                 st = '<style type="text/css">' +
25146                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25147                    '</style>';
25148         } else {
25149             for (var i in this.stylesheets) {
25150                 if (typeof(this.stylesheets[i]) != 'string') {
25151                     continue;
25152                 }
25153                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25154             }
25155             
25156         }
25157         
25158         st +=  '<style type="text/css">' +
25159             'IMG { cursor: pointer } ' +
25160         '</style>';
25161         
25162         st += '<meta name="google" content="notranslate">';
25163         
25164         var cls = 'notranslate roo-htmleditor-body';
25165         
25166         if(this.bodyCls.length){
25167             cls += ' ' + this.bodyCls;
25168         }
25169         
25170         return '<html  class="notranslate" translate="no"><head>' + st  +
25171             //<style type="text/css">' +
25172             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25173             //'</style>' +
25174             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25175     },
25176
25177     // private
25178     onRender : function(ct, position)
25179     {
25180         var _t = this;
25181         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25182         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25183         
25184         
25185         this.el.dom.style.border = '0 none';
25186         this.el.dom.setAttribute('tabIndex', -1);
25187         this.el.addClass('x-hidden hide');
25188         
25189         
25190         
25191         if(Roo.isIE){ // fix IE 1px bogus margin
25192             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25193         }
25194        
25195         
25196         this.frameId = Roo.id();
25197         
25198          
25199         
25200         var iframe = this.owner.wrap.createChild({
25201             tag: 'iframe',
25202             cls: 'form-control', // bootstrap..
25203             id: this.frameId,
25204             name: this.frameId,
25205             frameBorder : 'no',
25206             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25207         }, this.el
25208         );
25209         
25210         
25211         this.iframe = iframe.dom;
25212
25213         this.assignDocWin();
25214         
25215         this.doc.designMode = 'on';
25216        
25217         this.doc.open();
25218         this.doc.write(this.getDocMarkup());
25219         this.doc.close();
25220
25221         
25222         var task = { // must defer to wait for browser to be ready
25223             run : function(){
25224                 //console.log("run task?" + this.doc.readyState);
25225                 this.assignDocWin();
25226                 if(this.doc.body || this.doc.readyState == 'complete'){
25227                     try {
25228                         this.doc.designMode="on";
25229                         
25230                     } catch (e) {
25231                         return;
25232                     }
25233                     Roo.TaskMgr.stop(task);
25234                     this.initEditor.defer(10, this);
25235                 }
25236             },
25237             interval : 10,
25238             duration: 10000,
25239             scope: this
25240         };
25241         Roo.TaskMgr.start(task);
25242
25243     },
25244
25245     // private
25246     onResize : function(w, h)
25247     {
25248          Roo.log('resize: ' +w + ',' + h );
25249         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25250         if(!this.iframe){
25251             return;
25252         }
25253         if(typeof w == 'number'){
25254             
25255             this.iframe.style.width = w + 'px';
25256         }
25257         if(typeof h == 'number'){
25258             
25259             this.iframe.style.height = h + 'px';
25260             if(this.doc){
25261                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25262             }
25263         }
25264         
25265     },
25266
25267     /**
25268      * Toggles the editor between standard and source edit mode.
25269      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25270      */
25271     toggleSourceEdit : function(sourceEditMode){
25272         
25273         this.sourceEditMode = sourceEditMode === true;
25274         
25275         if(this.sourceEditMode){
25276  
25277             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25278             
25279         }else{
25280             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25281             //this.iframe.className = '';
25282             this.deferFocus();
25283         }
25284         //this.setSize(this.owner.wrap.getSize());
25285         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25286     },
25287
25288     
25289   
25290
25291     /**
25292      * Protected method that will not generally be called directly. If you need/want
25293      * custom HTML cleanup, this is the method you should override.
25294      * @param {String} html The HTML to be cleaned
25295      * return {String} The cleaned HTML
25296      */
25297     cleanHtml : function(html)
25298     {
25299         html = String(html);
25300         if(html.length > 5){
25301             if(Roo.isSafari){ // strip safari nonsense
25302                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25303             }
25304         }
25305         if(html == '&nbsp;'){
25306             html = '';
25307         }
25308         return html;
25309     },
25310
25311     /**
25312      * HTML Editor -> Textarea
25313      * Protected method that will not generally be called directly. Syncs the contents
25314      * of the editor iframe with the textarea.
25315      */
25316     syncValue : function()
25317     {
25318         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25319         if(this.initialized){
25320             
25321             this.undoManager.addEvent();
25322
25323             
25324             var bd = (this.doc.body || this.doc.documentElement);
25325            
25326             
25327             var sel = this.win.getSelection();
25328             
25329             var div = document.createElement('div');
25330             div.innerHTML = bd.innerHTML;
25331             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25332             if (gtx.length > 0) {
25333                 var rm = gtx.item(0).parentNode;
25334                 rm.parentNode.removeChild(rm);
25335             }
25336             
25337            
25338             if (this.enableBlocks) {
25339                 new Roo.htmleditor.FilterBlock({ node : div });
25340             }
25341             //?? tidy?
25342             var tidy = new Roo.htmleditor.TidySerializer({
25343                 inner:  true
25344             });
25345             var html  = tidy.serialize(div);
25346             
25347             
25348             if(Roo.isSafari){
25349                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25350                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25351                 if(m && m[1]){
25352                     html = '<div style="'+m[0]+'">' + html + '</div>';
25353                 }
25354             }
25355             html = this.cleanHtml(html);
25356             // fix up the special chars.. normaly like back quotes in word...
25357             // however we do not want to do this with chinese..
25358             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25359                 
25360                 var cc = match.charCodeAt();
25361
25362                 // Get the character value, handling surrogate pairs
25363                 if (match.length == 2) {
25364                     // It's a surrogate pair, calculate the Unicode code point
25365                     var high = match.charCodeAt(0) - 0xD800;
25366                     var low  = match.charCodeAt(1) - 0xDC00;
25367                     cc = (high * 0x400) + low + 0x10000;
25368                 }  else if (
25369                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25370                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25371                     (cc >= 0xf900 && cc < 0xfb00 )
25372                 ) {
25373                         return match;
25374                 }  
25375          
25376                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25377                 return "&#" + cc + ";";
25378                 
25379                 
25380             });
25381             
25382             
25383              
25384             if(this.owner.fireEvent('beforesync', this, html) !== false){
25385                 this.el.dom.value = html;
25386                 this.owner.fireEvent('sync', this, html);
25387             }
25388         }
25389     },
25390
25391     /**
25392      * TEXTAREA -> EDITABLE
25393      * Protected method that will not generally be called directly. Pushes the value of the textarea
25394      * into the iframe editor.
25395      */
25396     pushValue : function()
25397     {
25398         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25399         if(this.initialized){
25400             var v = this.el.dom.value.trim();
25401             
25402             
25403             if(this.owner.fireEvent('beforepush', this, v) !== false){
25404                 var d = (this.doc.body || this.doc.documentElement);
25405                 d.innerHTML = v;
25406                  
25407                 this.el.dom.value = d.innerHTML;
25408                 this.owner.fireEvent('push', this, v);
25409             }
25410             if (this.autoClean) {
25411                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25412                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25413             }
25414             
25415             Roo.htmleditor.Block.initAll(this.doc.body);
25416             this.updateLanguage();
25417             
25418             var lc = this.doc.body.lastChild;
25419             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25420                 // add an extra line at the end.
25421                 this.doc.body.appendChild(this.doc.createElement('br'));
25422             }
25423             
25424             
25425         }
25426     },
25427
25428     // private
25429     deferFocus : function(){
25430         this.focus.defer(10, this);
25431     },
25432
25433     // doc'ed in Field
25434     focus : function(){
25435         if(this.win && !this.sourceEditMode){
25436             this.win.focus();
25437         }else{
25438             this.el.focus();
25439         }
25440     },
25441     
25442     assignDocWin: function()
25443     {
25444         var iframe = this.iframe;
25445         
25446          if(Roo.isIE){
25447             this.doc = iframe.contentWindow.document;
25448             this.win = iframe.contentWindow;
25449         } else {
25450 //            if (!Roo.get(this.frameId)) {
25451 //                return;
25452 //            }
25453 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25454 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25455             
25456             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25457                 return;
25458             }
25459             
25460             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25461             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25462         }
25463     },
25464     
25465     // private
25466     initEditor : function(){
25467         //console.log("INIT EDITOR");
25468         this.assignDocWin();
25469         
25470         
25471         
25472         this.doc.designMode="on";
25473         this.doc.open();
25474         this.doc.write(this.getDocMarkup());
25475         this.doc.close();
25476         
25477         var dbody = (this.doc.body || this.doc.documentElement);
25478         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25479         // this copies styles from the containing element into thsi one..
25480         // not sure why we need all of this..
25481         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25482         
25483         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25484         //ss['background-attachment'] = 'fixed'; // w3c
25485         dbody.bgProperties = 'fixed'; // ie
25486         dbody.setAttribute("translate", "no");
25487         
25488         //Roo.DomHelper.applyStyles(dbody, ss);
25489         Roo.EventManager.on(this.doc, {
25490              
25491             'mouseup': this.onEditorEvent,
25492             'dblclick': this.onEditorEvent,
25493             'click': this.onEditorEvent,
25494             'keyup': this.onEditorEvent,
25495             
25496             buffer:100,
25497             scope: this
25498         });
25499         Roo.EventManager.on(this.doc, {
25500             'paste': this.onPasteEvent,
25501             scope : this
25502         });
25503         if(Roo.isGecko){
25504             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25505         }
25506         //??? needed???
25507         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25508             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25509         }
25510         this.initialized = true;
25511
25512         
25513         // initialize special key events - enter
25514         new Roo.htmleditor.KeyEnter({core : this});
25515         
25516          
25517         
25518         this.owner.fireEvent('initialize', this);
25519         this.pushValue();
25520     },
25521     // this is to prevent a href clicks resulting in a redirect?
25522    
25523     onPasteEvent : function(e,v)
25524     {
25525         // I think we better assume paste is going to be a dirty load of rubish from word..
25526         
25527         // even pasting into a 'email version' of this widget will have to clean up that mess.
25528         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25529         
25530         // check what type of paste - if it's an image, then handle it differently.
25531         if (cd.files.length > 0) {
25532             // pasting images?
25533             var urlAPI = (window.createObjectURL && window) || 
25534                 (window.URL && URL.revokeObjectURL && URL) || 
25535                 (window.webkitURL && webkitURL);
25536     
25537             var url = urlAPI.createObjectURL( cd.files[0]);
25538             this.insertAtCursor('<img src=" + url + ">');
25539             return false;
25540         }
25541         
25542         var html = cd.getData('text/html'); // clipboard event
25543         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25544         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25545         Roo.log(images);
25546         //Roo.log(imgs);
25547         // fixme..
25548         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
25549                        .map(function(g) { return g.toDataURL(); });
25550         
25551         
25552         html = this.cleanWordChars(html);
25553         
25554         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25555         
25556         
25557         var sn = this.getParentElement();
25558         // check if d contains a table, and prevent nesting??
25559         //Roo.log(d.getElementsByTagName('table'));
25560         //Roo.log(sn);
25561         //Roo.log(sn.closest('table'));
25562         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25563             e.preventDefault();
25564             this.insertAtCursor("You can not nest tables");
25565             //Roo.log("prevent?"); // fixme - 
25566             return false;
25567         }
25568         
25569         if (images.length > 0) {
25570             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25571                 img.setAttribute('src', images[i]);
25572             });
25573         }
25574         if (this.autoClean) {
25575             new Roo.htmleditor.FilterStyleToTag({ node : d });
25576             new Roo.htmleditor.FilterAttributes({
25577                 node : d,
25578                 attrib_white : ['href', 'src', 'name', 'align'],
25579                 attrib_clean : ['href', 'src' ] 
25580             });
25581             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25582             // should be fonts..
25583             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25584             new Roo.htmleditor.FilterParagraph({ node : d });
25585             new Roo.htmleditor.FilterSpan({ node : d });
25586             new Roo.htmleditor.FilterLongBr({ node : d });
25587         }
25588         if (this.enableBlocks) {
25589                 
25590             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25591                 if (img.closest('figure')) { // assume!! that it's aready
25592                     return;
25593                 }
25594                 var fig  = new Roo.htmleditor.BlockFigure({
25595                     image_src  : img.src
25596                 });
25597                 fig.updateElement(img); // replace it..
25598                 
25599             });
25600         }
25601         
25602         
25603         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25604         if (this.enableBlocks) {
25605             Roo.htmleditor.Block.initAll(this.doc.body);
25606         }
25607         
25608         
25609         e.preventDefault();
25610         return false;
25611         // default behaveiour should be our local cleanup paste? (optional?)
25612         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25613         //this.owner.fireEvent('paste', e, v);
25614     },
25615     // private
25616     onDestroy : function(){
25617         
25618         
25619         
25620         if(this.rendered){
25621             
25622             //for (var i =0; i < this.toolbars.length;i++) {
25623             //    // fixme - ask toolbars for heights?
25624             //    this.toolbars[i].onDestroy();
25625            // }
25626             
25627             //this.wrap.dom.innerHTML = '';
25628             //this.wrap.remove();
25629         }
25630     },
25631
25632     // private
25633     onFirstFocus : function(){
25634         
25635         this.assignDocWin();
25636         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25637         
25638         this.activated = true;
25639          
25640     
25641         if(Roo.isGecko){ // prevent silly gecko errors
25642             this.win.focus();
25643             var s = this.win.getSelection();
25644             if(!s.focusNode || s.focusNode.nodeType != 3){
25645                 var r = s.getRangeAt(0);
25646                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25647                 r.collapse(true);
25648                 this.deferFocus();
25649             }
25650             try{
25651                 this.execCmd('useCSS', true);
25652                 this.execCmd('styleWithCSS', false);
25653             }catch(e){}
25654         }
25655         this.owner.fireEvent('activate', this);
25656     },
25657
25658     // private
25659     adjustFont: function(btn){
25660         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25661         //if(Roo.isSafari){ // safari
25662         //    adjust *= 2;
25663        // }
25664         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25665         if(Roo.isSafari){ // safari
25666             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25667             v =  (v < 10) ? 10 : v;
25668             v =  (v > 48) ? 48 : v;
25669             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25670             
25671         }
25672         
25673         
25674         v = Math.max(1, v+adjust);
25675         
25676         this.execCmd('FontSize', v  );
25677     },
25678
25679     onEditorEvent : function(e)
25680     {
25681          
25682         
25683         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25684             return; // we do not handle this.. (undo manager does..)
25685         }
25686         // in theory this detects if the last element is not a br, then we try and do that.
25687         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25688         if (e &&
25689             e.target.nodeName == 'BODY' &&
25690             e.type == "mouseup" &&
25691             this.doc.body.lastChild
25692            ) {
25693             var lc = this.doc.body.lastChild;
25694             // gtx-trans is google translate plugin adding crap.
25695             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25696                 lc = lc.previousSibling;
25697             }
25698             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25699             // if last element is <BR> - then dont do anything.
25700             
25701                 var ns = this.doc.createElement('br');
25702                 this.doc.body.appendChild(ns);
25703                 range = this.doc.createRange();
25704                 range.setStartAfter(ns);
25705                 range.collapse(true);
25706                 var sel = this.win.getSelection();
25707                 sel.removeAllRanges();
25708                 sel.addRange(range);
25709             }
25710         }
25711         
25712         
25713         
25714         this.fireEditorEvent(e);
25715       //  this.updateToolbar();
25716         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25717     },
25718     
25719     fireEditorEvent: function(e)
25720     {
25721         this.owner.fireEvent('editorevent', this, e);
25722     },
25723
25724     insertTag : function(tg)
25725     {
25726         // could be a bit smarter... -> wrap the current selected tRoo..
25727         if (tg.toLowerCase() == 'span' ||
25728             tg.toLowerCase() == 'code' ||
25729             tg.toLowerCase() == 'sup' ||
25730             tg.toLowerCase() == 'sub' 
25731             ) {
25732             
25733             range = this.createRange(this.getSelection());
25734             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25735             wrappingNode.appendChild(range.extractContents());
25736             range.insertNode(wrappingNode);
25737
25738             return;
25739             
25740             
25741             
25742         }
25743         this.execCmd("formatblock",   tg);
25744         this.undoManager.addEvent(); 
25745     },
25746     
25747     insertText : function(txt)
25748     {
25749         
25750         
25751         var range = this.createRange();
25752         range.deleteContents();
25753                //alert(Sender.getAttribute('label'));
25754                
25755         range.insertNode(this.doc.createTextNode(txt));
25756         this.undoManager.addEvent();
25757     } ,
25758     
25759      
25760
25761     /**
25762      * Executes a Midas editor command on the editor document and performs necessary focus and
25763      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25764      * @param {String} cmd The Midas command
25765      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25766      */
25767     relayCmd : function(cmd, value)
25768     {
25769         
25770         switch (cmd) {
25771             case 'justifyleft':
25772             case 'justifyright':
25773             case 'justifycenter':
25774                 // if we are in a cell, then we will adjust the
25775                 var n = this.getParentElement();
25776                 var td = n.closest('td');
25777                 if (td) {
25778                     var bl = Roo.htmleditor.Block.factory(td);
25779                     bl.textAlign = cmd.replace('justify','');
25780                     bl.updateElement();
25781                     this.owner.fireEvent('editorevent', this);
25782                     return;
25783                 }
25784                 this.execCmd('styleWithCSS', true); // 
25785                 break;
25786             case 'bold':
25787             case 'italic':
25788                 // if there is no selection, then we insert, and set the curson inside it..
25789                 this.execCmd('styleWithCSS', false); 
25790                 break;
25791                 
25792         
25793             default:
25794                 break;
25795         }
25796         
25797         
25798         this.win.focus();
25799         this.execCmd(cmd, value);
25800         this.owner.fireEvent('editorevent', this);
25801         //this.updateToolbar();
25802         this.owner.deferFocus();
25803     },
25804
25805     /**
25806      * Executes a Midas editor command directly on the editor document.
25807      * For visual commands, you should use {@link #relayCmd} instead.
25808      * <b>This should only be called after the editor is initialized.</b>
25809      * @param {String} cmd The Midas command
25810      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25811      */
25812     execCmd : function(cmd, value){
25813         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25814         this.syncValue();
25815     },
25816  
25817  
25818    
25819     /**
25820      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25821      * to insert tRoo.
25822      * @param {String} text | dom node.. 
25823      */
25824     insertAtCursor : function(text)
25825     {
25826         
25827         if(!this.activated){
25828             return;
25829         }
25830          
25831         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25832             this.win.focus();
25833             
25834             
25835             // from jquery ui (MIT licenced)
25836             var range, node;
25837             var win = this.win;
25838             
25839             if (win.getSelection && win.getSelection().getRangeAt) {
25840                 
25841                 // delete the existing?
25842                 
25843                 this.createRange(this.getSelection()).deleteContents();
25844                 range = win.getSelection().getRangeAt(0);
25845                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25846                 range.insertNode(node);
25847                 range = range.cloneRange();
25848                 range.collapse(false);
25849                  
25850                 win.getSelection().removeAllRanges();
25851                 win.getSelection().addRange(range);
25852                 
25853                 
25854                 
25855             } else if (win.document.selection && win.document.selection.createRange) {
25856                 // no firefox support
25857                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25858                 win.document.selection.createRange().pasteHTML(txt);
25859             
25860             } else {
25861                 // no firefox support
25862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25863                 this.execCmd('InsertHTML', txt);
25864             } 
25865             this.syncValue();
25866             
25867             this.deferFocus();
25868         }
25869     },
25870  // private
25871     mozKeyPress : function(e){
25872         if(e.ctrlKey){
25873             var c = e.getCharCode(), cmd;
25874           
25875             if(c > 0){
25876                 c = String.fromCharCode(c).toLowerCase();
25877                 switch(c){
25878                     case 'b':
25879                         cmd = 'bold';
25880                         break;
25881                     case 'i':
25882                         cmd = 'italic';
25883                         break;
25884                     
25885                     case 'u':
25886                         cmd = 'underline';
25887                         break;
25888                     
25889                     //case 'v':
25890                       //  this.cleanUpPaste.defer(100, this);
25891                       //  return;
25892                         
25893                 }
25894                 if(cmd){
25895                     
25896                     this.relayCmd(cmd);
25897                     //this.win.focus();
25898                     //this.execCmd(cmd);
25899                     //this.deferFocus();
25900                     e.preventDefault();
25901                 }
25902                 
25903             }
25904         }
25905     },
25906
25907     // private
25908     fixKeys : function(){ // load time branching for fastest keydown performance
25909         
25910         
25911         if(Roo.isIE){
25912             return function(e){
25913                 var k = e.getKey(), r;
25914                 if(k == e.TAB){
25915                     e.stopEvent();
25916                     r = this.doc.selection.createRange();
25917                     if(r){
25918                         r.collapse(true);
25919                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25920                         this.deferFocus();
25921                     }
25922                     return;
25923                 }
25924                 /// this is handled by Roo.htmleditor.KeyEnter
25925                  /*
25926                 if(k == e.ENTER){
25927                     r = this.doc.selection.createRange();
25928                     if(r){
25929                         var target = r.parentElement();
25930                         if(!target || target.tagName.toLowerCase() != 'li'){
25931                             e.stopEvent();
25932                             r.pasteHTML('<br/>');
25933                             r.collapse(false);
25934                             r.select();
25935                         }
25936                     }
25937                 }
25938                 */
25939                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25940                 //    this.cleanUpPaste.defer(100, this);
25941                 //    return;
25942                 //}
25943                 
25944                 
25945             };
25946         }else if(Roo.isOpera){
25947             return function(e){
25948                 var k = e.getKey();
25949                 if(k == e.TAB){
25950                     e.stopEvent();
25951                     this.win.focus();
25952                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25953                     this.deferFocus();
25954                 }
25955                
25956                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25957                 //    this.cleanUpPaste.defer(100, this);
25958                  //   return;
25959                 //}
25960                 
25961             };
25962         }else if(Roo.isSafari){
25963             return function(e){
25964                 var k = e.getKey();
25965                 
25966                 if(k == e.TAB){
25967                     e.stopEvent();
25968                     this.execCmd('InsertText','\t');
25969                     this.deferFocus();
25970                     return;
25971                 }
25972                  this.mozKeyPress(e);
25973                 
25974                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25975                  //   this.cleanUpPaste.defer(100, this);
25976                  //   return;
25977                // }
25978                 
25979              };
25980         }
25981     }(),
25982     
25983     getAllAncestors: function()
25984     {
25985         var p = this.getSelectedNode();
25986         var a = [];
25987         if (!p) {
25988             a.push(p); // push blank onto stack..
25989             p = this.getParentElement();
25990         }
25991         
25992         
25993         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25994             a.push(p);
25995             p = p.parentNode;
25996         }
25997         a.push(this.doc.body);
25998         return a;
25999     },
26000     lastSel : false,
26001     lastSelNode : false,
26002     
26003     
26004     getSelection : function() 
26005     {
26006         this.assignDocWin();
26007         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26008     },
26009     /**
26010      * Select a dom node
26011      * @param {DomElement} node the node to select
26012      */
26013     selectNode : function(node, collapse)
26014     {
26015         var nodeRange = node.ownerDocument.createRange();
26016         try {
26017             nodeRange.selectNode(node);
26018         } catch (e) {
26019             nodeRange.selectNodeContents(node);
26020         }
26021         if (collapse === true) {
26022             nodeRange.collapse(true);
26023         }
26024         //
26025         var s = this.win.getSelection();
26026         s.removeAllRanges();
26027         s.addRange(nodeRange);
26028     },
26029     
26030     getSelectedNode: function() 
26031     {
26032         // this may only work on Gecko!!!
26033         
26034         // should we cache this!!!!
26035         
26036          
26037          
26038         var range = this.createRange(this.getSelection()).cloneRange();
26039         
26040         if (Roo.isIE) {
26041             var parent = range.parentElement();
26042             while (true) {
26043                 var testRange = range.duplicate();
26044                 testRange.moveToElementText(parent);
26045                 if (testRange.inRange(range)) {
26046                     break;
26047                 }
26048                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26049                     break;
26050                 }
26051                 parent = parent.parentElement;
26052             }
26053             return parent;
26054         }
26055         
26056         // is ancestor a text element.
26057         var ac =  range.commonAncestorContainer;
26058         if (ac.nodeType == 3) {
26059             ac = ac.parentNode;
26060         }
26061         
26062         var ar = ac.childNodes;
26063          
26064         var nodes = [];
26065         var other_nodes = [];
26066         var has_other_nodes = false;
26067         for (var i=0;i<ar.length;i++) {
26068             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26069                 continue;
26070             }
26071             // fullly contained node.
26072             
26073             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26074                 nodes.push(ar[i]);
26075                 continue;
26076             }
26077             
26078             // probably selected..
26079             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26080                 other_nodes.push(ar[i]);
26081                 continue;
26082             }
26083             // outer..
26084             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26085                 continue;
26086             }
26087             
26088             
26089             has_other_nodes = true;
26090         }
26091         if (!nodes.length && other_nodes.length) {
26092             nodes= other_nodes;
26093         }
26094         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26095             return false;
26096         }
26097         
26098         return nodes[0];
26099     },
26100     
26101     
26102     createRange: function(sel)
26103     {
26104         // this has strange effects when using with 
26105         // top toolbar - not sure if it's a great idea.
26106         //this.editor.contentWindow.focus();
26107         if (typeof sel != "undefined") {
26108             try {
26109                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26110             } catch(e) {
26111                 return this.doc.createRange();
26112             }
26113         } else {
26114             return this.doc.createRange();
26115         }
26116     },
26117     getParentElement: function()
26118     {
26119         
26120         this.assignDocWin();
26121         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26122         
26123         var range = this.createRange(sel);
26124          
26125         try {
26126             var p = range.commonAncestorContainer;
26127             while (p.nodeType == 3) { // text node
26128                 p = p.parentNode;
26129             }
26130             return p;
26131         } catch (e) {
26132             return null;
26133         }
26134     
26135     },
26136     /***
26137      *
26138      * Range intersection.. the hard stuff...
26139      *  '-1' = before
26140      *  '0' = hits..
26141      *  '1' = after.
26142      *         [ -- selected range --- ]
26143      *   [fail]                        [fail]
26144      *
26145      *    basically..
26146      *      if end is before start or  hits it. fail.
26147      *      if start is after end or hits it fail.
26148      *
26149      *   if either hits (but other is outside. - then it's not 
26150      *   
26151      *    
26152      **/
26153     
26154     
26155     // @see http://www.thismuchiknow.co.uk/?p=64.
26156     rangeIntersectsNode : function(range, node)
26157     {
26158         var nodeRange = node.ownerDocument.createRange();
26159         try {
26160             nodeRange.selectNode(node);
26161         } catch (e) {
26162             nodeRange.selectNodeContents(node);
26163         }
26164     
26165         var rangeStartRange = range.cloneRange();
26166         rangeStartRange.collapse(true);
26167     
26168         var rangeEndRange = range.cloneRange();
26169         rangeEndRange.collapse(false);
26170     
26171         var nodeStartRange = nodeRange.cloneRange();
26172         nodeStartRange.collapse(true);
26173     
26174         var nodeEndRange = nodeRange.cloneRange();
26175         nodeEndRange.collapse(false);
26176     
26177         return rangeStartRange.compareBoundaryPoints(
26178                  Range.START_TO_START, nodeEndRange) == -1 &&
26179                rangeEndRange.compareBoundaryPoints(
26180                  Range.START_TO_START, nodeStartRange) == 1;
26181         
26182          
26183     },
26184     rangeCompareNode : function(range, node)
26185     {
26186         var nodeRange = node.ownerDocument.createRange();
26187         try {
26188             nodeRange.selectNode(node);
26189         } catch (e) {
26190             nodeRange.selectNodeContents(node);
26191         }
26192         
26193         
26194         range.collapse(true);
26195     
26196         nodeRange.collapse(true);
26197      
26198         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26199         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26200          
26201         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26202         
26203         var nodeIsBefore   =  ss == 1;
26204         var nodeIsAfter    = ee == -1;
26205         
26206         if (nodeIsBefore && nodeIsAfter) {
26207             return 0; // outer
26208         }
26209         if (!nodeIsBefore && nodeIsAfter) {
26210             return 1; //right trailed.
26211         }
26212         
26213         if (nodeIsBefore && !nodeIsAfter) {
26214             return 2;  // left trailed.
26215         }
26216         // fully contined.
26217         return 3;
26218     },
26219  
26220     cleanWordChars : function(input) {// change the chars to hex code
26221         
26222        var swapCodes  = [ 
26223             [    8211, "&#8211;" ], 
26224             [    8212, "&#8212;" ], 
26225             [    8216,  "'" ],  
26226             [    8217, "'" ],  
26227             [    8220, '"' ],  
26228             [    8221, '"' ],  
26229             [    8226, "*" ],  
26230             [    8230, "..." ]
26231         ]; 
26232         var output = input;
26233         Roo.each(swapCodes, function(sw) { 
26234             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26235             
26236             output = output.replace(swapper, sw[1]);
26237         });
26238         
26239         return output;
26240     },
26241     
26242      
26243     
26244         
26245     
26246     cleanUpChild : function (node)
26247     {
26248         
26249         new Roo.htmleditor.FilterComment({node : node});
26250         new Roo.htmleditor.FilterAttributes({
26251                 node : node,
26252                 attrib_black : this.ablack,
26253                 attrib_clean : this.aclean,
26254                 style_white : this.cwhite,
26255                 style_black : this.cblack
26256         });
26257         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26258         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26259          
26260         
26261     },
26262     
26263     /**
26264      * Clean up MS wordisms...
26265      * @deprecated - use filter directly
26266      */
26267     cleanWord : function(node)
26268     {
26269         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26270         
26271     },
26272    
26273     
26274     /**
26275
26276      * @deprecated - use filters
26277      */
26278     cleanTableWidths : function(node)
26279     {
26280         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26281         
26282  
26283     },
26284     
26285      
26286         
26287     applyBlacklists : function()
26288     {
26289         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26290         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26291         
26292         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26293         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26294         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26295         
26296         this.white = [];
26297         this.black = [];
26298         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26299             if (b.indexOf(tag) > -1) {
26300                 return;
26301             }
26302             this.white.push(tag);
26303             
26304         }, this);
26305         
26306         Roo.each(w, function(tag) {
26307             if (b.indexOf(tag) > -1) {
26308                 return;
26309             }
26310             if (this.white.indexOf(tag) > -1) {
26311                 return;
26312             }
26313             this.white.push(tag);
26314             
26315         }, this);
26316         
26317         
26318         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26319             if (w.indexOf(tag) > -1) {
26320                 return;
26321             }
26322             this.black.push(tag);
26323             
26324         }, this);
26325         
26326         Roo.each(b, function(tag) {
26327             if (w.indexOf(tag) > -1) {
26328                 return;
26329             }
26330             if (this.black.indexOf(tag) > -1) {
26331                 return;
26332             }
26333             this.black.push(tag);
26334             
26335         }, this);
26336         
26337         
26338         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26339         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26340         
26341         this.cwhite = [];
26342         this.cblack = [];
26343         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26344             if (b.indexOf(tag) > -1) {
26345                 return;
26346             }
26347             this.cwhite.push(tag);
26348             
26349         }, this);
26350         
26351         Roo.each(w, function(tag) {
26352             if (b.indexOf(tag) > -1) {
26353                 return;
26354             }
26355             if (this.cwhite.indexOf(tag) > -1) {
26356                 return;
26357             }
26358             this.cwhite.push(tag);
26359             
26360         }, this);
26361         
26362         
26363         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26364             if (w.indexOf(tag) > -1) {
26365                 return;
26366             }
26367             this.cblack.push(tag);
26368             
26369         }, this);
26370         
26371         Roo.each(b, function(tag) {
26372             if (w.indexOf(tag) > -1) {
26373                 return;
26374             }
26375             if (this.cblack.indexOf(tag) > -1) {
26376                 return;
26377             }
26378             this.cblack.push(tag);
26379             
26380         }, this);
26381     },
26382     
26383     setStylesheets : function(stylesheets)
26384     {
26385         if(typeof(stylesheets) == 'string'){
26386             Roo.get(this.iframe.contentDocument.head).createChild({
26387                 tag : 'link',
26388                 rel : 'stylesheet',
26389                 type : 'text/css',
26390                 href : stylesheets
26391             });
26392             
26393             return;
26394         }
26395         var _this = this;
26396      
26397         Roo.each(stylesheets, function(s) {
26398             if(!s.length){
26399                 return;
26400             }
26401             
26402             Roo.get(_this.iframe.contentDocument.head).createChild({
26403                 tag : 'link',
26404                 rel : 'stylesheet',
26405                 type : 'text/css',
26406                 href : s
26407             });
26408         });
26409
26410         
26411     },
26412     
26413     
26414     updateLanguage : function()
26415     {
26416         if (!this.iframe || !this.iframe.contentDocument) {
26417             return;
26418         }
26419         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26420     },
26421     
26422     
26423     removeStylesheets : function()
26424     {
26425         var _this = this;
26426         
26427         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26428             s.remove();
26429         });
26430     },
26431     
26432     setStyle : function(style)
26433     {
26434         Roo.get(this.iframe.contentDocument.head).createChild({
26435             tag : 'style',
26436             type : 'text/css',
26437             html : style
26438         });
26439
26440         return;
26441     }
26442     
26443     // hide stuff that is not compatible
26444     /**
26445      * @event blur
26446      * @hide
26447      */
26448     /**
26449      * @event change
26450      * @hide
26451      */
26452     /**
26453      * @event focus
26454      * @hide
26455      */
26456     /**
26457      * @event specialkey
26458      * @hide
26459      */
26460     /**
26461      * @cfg {String} fieldClass @hide
26462      */
26463     /**
26464      * @cfg {String} focusClass @hide
26465      */
26466     /**
26467      * @cfg {String} autoCreate @hide
26468      */
26469     /**
26470      * @cfg {String} inputType @hide
26471      */
26472     /**
26473      * @cfg {String} invalidClass @hide
26474      */
26475     /**
26476      * @cfg {String} invalidText @hide
26477      */
26478     /**
26479      * @cfg {String} msgFx @hide
26480      */
26481     /**
26482      * @cfg {String} validateOnBlur @hide
26483      */
26484 });
26485
26486 Roo.HtmlEditorCore.white = [
26487         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26488         
26489        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26490        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26491        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26492        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26493        'TABLE',   'UL',         'XMP', 
26494        
26495        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26496       'THEAD',   'TR', 
26497      
26498       'DIR', 'MENU', 'OL', 'UL', 'DL',
26499        
26500       'EMBED',  'OBJECT'
26501 ];
26502
26503
26504 Roo.HtmlEditorCore.black = [
26505     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26506         'APPLET', // 
26507         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26508         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26509         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26510         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26511         //'FONT' // CLEAN LATER..
26512         'COLGROUP', 'COL'   // messy tables.
26513         
26514         
26515 ];
26516 Roo.HtmlEditorCore.clean = [ // ?? needed???
26517      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26518 ];
26519 Roo.HtmlEditorCore.tag_remove = [
26520     'FONT', 'TBODY'  
26521 ];
26522 // attributes..
26523
26524 Roo.HtmlEditorCore.ablack = [
26525     'on'
26526 ];
26527     
26528 Roo.HtmlEditorCore.aclean = [ 
26529     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26530 ];
26531
26532 // protocols..
26533 Roo.HtmlEditorCore.pwhite= [
26534         'http',  'https',  'mailto'
26535 ];
26536
26537 // white listed style attributes.
26538 Roo.HtmlEditorCore.cwhite= [
26539       //  'text-align', /// default is to allow most things..
26540       
26541          
26542 //        'font-size'//??
26543 ];
26544
26545 // black listed style attributes.
26546 Roo.HtmlEditorCore.cblack= [
26547       //  'font-size' -- this can be set by the project 
26548 ];
26549
26550
26551
26552
26553     //<script type="text/javascript">
26554
26555 /*
26556  * Ext JS Library 1.1.1
26557  * Copyright(c) 2006-2007, Ext JS, LLC.
26558  * Licence LGPL
26559  * 
26560  */
26561  
26562  
26563 Roo.form.HtmlEditor = function(config){
26564     
26565     
26566     
26567     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26568     
26569     if (!this.toolbars) {
26570         this.toolbars = [];
26571     }
26572     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26573     
26574     
26575 };
26576
26577 /**
26578  * @class Roo.form.HtmlEditor
26579  * @extends Roo.form.Field
26580  * Provides a lightweight HTML Editor component.
26581  *
26582  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26583  * 
26584  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26585  * supported by this editor.</b><br/><br/>
26586  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26587  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26588  */
26589 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26590     /**
26591      * @cfg {Boolean} clearUp
26592      */
26593     clearUp : true,
26594       /**
26595      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26596      */
26597     toolbars : false,
26598    
26599      /**
26600      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26601      *                        Roo.resizable.
26602      */
26603     resizable : false,
26604      /**
26605      * @cfg {Number} height (in pixels)
26606      */   
26607     height: 300,
26608    /**
26609      * @cfg {Number} width (in pixels)
26610      */   
26611     width: 500,
26612     
26613     /**
26614      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26615      * 
26616      */
26617     stylesheets: false,
26618     
26619     
26620      /**
26621      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26622      * 
26623      */
26624     cblack: false,
26625     /**
26626      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26627      * 
26628      */
26629     cwhite: false,
26630     
26631      /**
26632      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26633      * 
26634      */
26635     black: false,
26636     /**
26637      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26638      * 
26639      */
26640     white: false,
26641     /**
26642      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26643      */
26644     allowComments: false,
26645     /**
26646      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26647      */
26648     enableBlocks : true,
26649     
26650     /**
26651      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26652      *         if you are doing an email editor, this probably needs disabling, it's designed
26653      */
26654     autoClean: true,
26655     /**
26656      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
26657      */
26658     bodyCls : '',
26659     /**
26660      * @cfg {String} language default en - language of text (usefull for rtl languages)
26661      * 
26662      */
26663     language: 'en',
26664     
26665      
26666     // id of frame..
26667     frameId: false,
26668     
26669     // private properties
26670     validationEvent : false,
26671     deferHeight: true,
26672     initialized : false,
26673     activated : false,
26674     
26675     onFocus : Roo.emptyFn,
26676     iframePad:3,
26677     hideMode:'offsets',
26678     
26679     actionMode : 'container', // defaults to hiding it...
26680     
26681     defaultAutoCreate : { // modified by initCompnoent..
26682         tag: "textarea",
26683         style:"width:500px;height:300px;",
26684         autocomplete: "new-password"
26685     },
26686
26687     // private
26688     initComponent : function(){
26689         this.addEvents({
26690             /**
26691              * @event initialize
26692              * Fires when the editor is fully initialized (including the iframe)
26693              * @param {HtmlEditor} this
26694              */
26695             initialize: true,
26696             /**
26697              * @event activate
26698              * Fires when the editor is first receives the focus. Any insertion must wait
26699              * until after this event.
26700              * @param {HtmlEditor} this
26701              */
26702             activate: true,
26703              /**
26704              * @event beforesync
26705              * Fires before the textarea is updated with content from the editor iframe. Return false
26706              * to cancel the sync.
26707              * @param {HtmlEditor} this
26708              * @param {String} html
26709              */
26710             beforesync: true,
26711              /**
26712              * @event beforepush
26713              * Fires before the iframe editor is updated with content from the textarea. Return false
26714              * to cancel the push.
26715              * @param {HtmlEditor} this
26716              * @param {String} html
26717              */
26718             beforepush: true,
26719              /**
26720              * @event sync
26721              * Fires when the textarea is updated with content from the editor iframe.
26722              * @param {HtmlEditor} this
26723              * @param {String} html
26724              */
26725             sync: true,
26726              /**
26727              * @event push
26728              * Fires when the iframe editor is updated with content from the textarea.
26729              * @param {HtmlEditor} this
26730              * @param {String} html
26731              */
26732             push: true,
26733              /**
26734              * @event editmodechange
26735              * Fires when the editor switches edit modes
26736              * @param {HtmlEditor} this
26737              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26738              */
26739             editmodechange: true,
26740             /**
26741              * @event editorevent
26742              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26743              * @param {HtmlEditor} this
26744              */
26745             editorevent: true,
26746             /**
26747              * @event firstfocus
26748              * Fires when on first focus - needed by toolbars..
26749              * @param {HtmlEditor} this
26750              */
26751             firstfocus: true,
26752             /**
26753              * @event autosave
26754              * Auto save the htmlEditor value as a file into Events
26755              * @param {HtmlEditor} this
26756              */
26757             autosave: true,
26758             /**
26759              * @event savedpreview
26760              * preview the saved version of htmlEditor
26761              * @param {HtmlEditor} this
26762              */
26763             savedpreview: true,
26764             
26765             /**
26766             * @event stylesheetsclick
26767             * Fires when press the Sytlesheets button
26768             * @param {Roo.HtmlEditorCore} this
26769             */
26770             stylesheetsclick: true,
26771             /**
26772             * @event paste
26773             * Fires when press user pastes into the editor
26774             * @param {Roo.HtmlEditorCore} this
26775             */
26776             paste: true 
26777         });
26778         this.defaultAutoCreate =  {
26779             tag: "textarea",
26780             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26781             autocomplete: "new-password"
26782         };
26783     },
26784
26785     /**
26786      * Protected method that will not generally be called directly. It
26787      * is called when the editor creates its toolbar. Override this method if you need to
26788      * add custom toolbar buttons.
26789      * @param {HtmlEditor} editor
26790      */
26791     createToolbar : function(editor){
26792         Roo.log("create toolbars");
26793         if (!editor.toolbars || !editor.toolbars.length) {
26794             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26795         }
26796         
26797         for (var i =0 ; i < editor.toolbars.length;i++) {
26798             editor.toolbars[i] = Roo.factory(
26799                     typeof(editor.toolbars[i]) == 'string' ?
26800                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26801                 Roo.form.HtmlEditor);
26802             editor.toolbars[i].init(editor);
26803         }
26804          
26805         
26806     },
26807     /**
26808      * get the Context selected node
26809      * @returns {DomElement|boolean} selected node if active or false if none
26810      * 
26811      */
26812     getSelectedNode : function()
26813     {
26814         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26815             return false;
26816         }
26817         return this.toolbars[1].tb.selectedNode;
26818     
26819     },
26820     // private
26821     onRender : function(ct, position)
26822     {
26823         var _t = this;
26824         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26825         
26826         this.wrap = this.el.wrap({
26827             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26828         });
26829         
26830         this.editorcore.onRender(ct, position);
26831          
26832         if (this.resizable) {
26833             this.resizeEl = new Roo.Resizable(this.wrap, {
26834                 pinned : true,
26835                 wrap: true,
26836                 dynamic : true,
26837                 minHeight : this.height,
26838                 height: this.height,
26839                 handles : this.resizable,
26840                 width: this.width,
26841                 listeners : {
26842                     resize : function(r, w, h) {
26843                         _t.onResize(w,h); // -something
26844                     }
26845                 }
26846             });
26847             
26848         }
26849         this.createToolbar(this);
26850        
26851         
26852         if(!this.width){
26853             this.setSize(this.wrap.getSize());
26854         }
26855         if (this.resizeEl) {
26856             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26857             // should trigger onReize..
26858         }
26859         
26860         this.keyNav = new Roo.KeyNav(this.el, {
26861             
26862             "tab" : function(e){
26863                 e.preventDefault();
26864                 
26865                 var value = this.getValue();
26866                 
26867                 var start = this.el.dom.selectionStart;
26868                 var end = this.el.dom.selectionEnd;
26869                 
26870                 if(!e.shiftKey){
26871                     
26872                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26873                     this.el.dom.setSelectionRange(end + 1, end + 1);
26874                     return;
26875                 }
26876                 
26877                 var f = value.substring(0, start).split("\t");
26878                 
26879                 if(f.pop().length != 0){
26880                     return;
26881                 }
26882                 
26883                 this.setValue(f.join("\t") + value.substring(end));
26884                 this.el.dom.setSelectionRange(start - 1, start - 1);
26885                 
26886             },
26887             
26888             "home" : function(e){
26889                 e.preventDefault();
26890                 
26891                 var curr = this.el.dom.selectionStart;
26892                 var lines = this.getValue().split("\n");
26893                 
26894                 if(!lines.length){
26895                     return;
26896                 }
26897                 
26898                 if(e.ctrlKey){
26899                     this.el.dom.setSelectionRange(0, 0);
26900                     return;
26901                 }
26902                 
26903                 var pos = 0;
26904                 
26905                 for (var i = 0; i < lines.length;i++) {
26906                     pos += lines[i].length;
26907                     
26908                     if(i != 0){
26909                         pos += 1;
26910                     }
26911                     
26912                     if(pos < curr){
26913                         continue;
26914                     }
26915                     
26916                     pos -= lines[i].length;
26917                     
26918                     break;
26919                 }
26920                 
26921                 if(!e.shiftKey){
26922                     this.el.dom.setSelectionRange(pos, pos);
26923                     return;
26924                 }
26925                 
26926                 this.el.dom.selectionStart = pos;
26927                 this.el.dom.selectionEnd = curr;
26928             },
26929             
26930             "end" : function(e){
26931                 e.preventDefault();
26932                 
26933                 var curr = this.el.dom.selectionStart;
26934                 var lines = this.getValue().split("\n");
26935                 
26936                 if(!lines.length){
26937                     return;
26938                 }
26939                 
26940                 if(e.ctrlKey){
26941                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26942                     return;
26943                 }
26944                 
26945                 var pos = 0;
26946                 
26947                 for (var i = 0; i < lines.length;i++) {
26948                     
26949                     pos += lines[i].length;
26950                     
26951                     if(i != 0){
26952                         pos += 1;
26953                     }
26954                     
26955                     if(pos < curr){
26956                         continue;
26957                     }
26958                     
26959                     break;
26960                 }
26961                 
26962                 if(!e.shiftKey){
26963                     this.el.dom.setSelectionRange(pos, pos);
26964                     return;
26965                 }
26966                 
26967                 this.el.dom.selectionStart = curr;
26968                 this.el.dom.selectionEnd = pos;
26969             },
26970
26971             scope : this,
26972
26973             doRelay : function(foo, bar, hname){
26974                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26975             },
26976
26977             forceKeyDown: true
26978         });
26979         
26980 //        if(this.autosave && this.w){
26981 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26982 //        }
26983     },
26984
26985     // private
26986     onResize : function(w, h)
26987     {
26988         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26989         var ew = false;
26990         var eh = false;
26991         
26992         if(this.el ){
26993             if(typeof w == 'number'){
26994                 var aw = w - this.wrap.getFrameWidth('lr');
26995                 this.el.setWidth(this.adjustWidth('textarea', aw));
26996                 ew = aw;
26997             }
26998             if(typeof h == 'number'){
26999                 var tbh = 0;
27000                 for (var i =0; i < this.toolbars.length;i++) {
27001                     // fixme - ask toolbars for heights?
27002                     tbh += this.toolbars[i].tb.el.getHeight();
27003                     if (this.toolbars[i].footer) {
27004                         tbh += this.toolbars[i].footer.el.getHeight();
27005                     }
27006                 }
27007                 
27008                 
27009                 
27010                 
27011                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27012                 ah -= 5; // knock a few pixes off for look..
27013 //                Roo.log(ah);
27014                 this.el.setHeight(this.adjustWidth('textarea', ah));
27015                 var eh = ah;
27016             }
27017         }
27018         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27019         this.editorcore.onResize(ew,eh);
27020         
27021     },
27022
27023     /**
27024      * Toggles the editor between standard and source edit mode.
27025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27026      */
27027     toggleSourceEdit : function(sourceEditMode)
27028     {
27029         this.editorcore.toggleSourceEdit(sourceEditMode);
27030         
27031         if(this.editorcore.sourceEditMode){
27032             Roo.log('editor - showing textarea');
27033             
27034 //            Roo.log('in');
27035 //            Roo.log(this.syncValue());
27036             this.editorcore.syncValue();
27037             this.el.removeClass('x-hidden');
27038             this.el.dom.removeAttribute('tabIndex');
27039             this.el.focus();
27040             this.el.dom.scrollTop = 0;
27041             
27042             
27043             for (var i = 0; i < this.toolbars.length; i++) {
27044                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27045                     this.toolbars[i].tb.hide();
27046                     this.toolbars[i].footer.hide();
27047                 }
27048             }
27049             
27050         }else{
27051             Roo.log('editor - hiding textarea');
27052 //            Roo.log('out')
27053 //            Roo.log(this.pushValue()); 
27054             this.editorcore.pushValue();
27055             
27056             this.el.addClass('x-hidden');
27057             this.el.dom.setAttribute('tabIndex', -1);
27058             
27059             for (var i = 0; i < this.toolbars.length; i++) {
27060                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27061                     this.toolbars[i].tb.show();
27062                     this.toolbars[i].footer.show();
27063                 }
27064             }
27065             
27066             //this.deferFocus();
27067         }
27068         
27069         this.setSize(this.wrap.getSize());
27070         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27071         
27072         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27073     },
27074  
27075     // private (for BoxComponent)
27076     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27077
27078     // private (for BoxComponent)
27079     getResizeEl : function(){
27080         return this.wrap;
27081     },
27082
27083     // private (for BoxComponent)
27084     getPositionEl : function(){
27085         return this.wrap;
27086     },
27087
27088     // private
27089     initEvents : function(){
27090         this.originalValue = this.getValue();
27091     },
27092
27093     /**
27094      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27095      * @method
27096      */
27097     markInvalid : Roo.emptyFn,
27098     /**
27099      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27100      * @method
27101      */
27102     clearInvalid : Roo.emptyFn,
27103
27104     setValue : function(v){
27105         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27106         this.editorcore.pushValue();
27107     },
27108
27109     /**
27110      * update the language in the body - really done by core
27111      * @param {String} language - eg. en / ar / zh-CN etc..
27112      */
27113     updateLanguage : function(lang)
27114     {
27115         this.language = lang;
27116         this.editorcore.language = lang;
27117         this.editorcore.updateLanguage();
27118      
27119     },
27120     // private
27121     deferFocus : function(){
27122         this.focus.defer(10, this);
27123     },
27124
27125     // doc'ed in Field
27126     focus : function(){
27127         this.editorcore.focus();
27128         
27129     },
27130       
27131
27132     // private
27133     onDestroy : function(){
27134         
27135         
27136         
27137         if(this.rendered){
27138             
27139             for (var i =0; i < this.toolbars.length;i++) {
27140                 // fixme - ask toolbars for heights?
27141                 this.toolbars[i].onDestroy();
27142             }
27143             
27144             this.wrap.dom.innerHTML = '';
27145             this.wrap.remove();
27146         }
27147     },
27148
27149     // private
27150     onFirstFocus : function(){
27151         //Roo.log("onFirstFocus");
27152         this.editorcore.onFirstFocus();
27153          for (var i =0; i < this.toolbars.length;i++) {
27154             this.toolbars[i].onFirstFocus();
27155         }
27156         
27157     },
27158     
27159     // private
27160     syncValue : function()
27161     {
27162         this.editorcore.syncValue();
27163     },
27164     
27165     pushValue : function()
27166     {
27167         this.editorcore.pushValue();
27168     },
27169     
27170     setStylesheets : function(stylesheets)
27171     {
27172         this.editorcore.setStylesheets(stylesheets);
27173     },
27174     
27175     removeStylesheets : function()
27176     {
27177         this.editorcore.removeStylesheets();
27178     }
27179      
27180     
27181     // hide stuff that is not compatible
27182     /**
27183      * @event blur
27184      * @hide
27185      */
27186     /**
27187      * @event change
27188      * @hide
27189      */
27190     /**
27191      * @event focus
27192      * @hide
27193      */
27194     /**
27195      * @event specialkey
27196      * @hide
27197      */
27198     /**
27199      * @cfg {String} fieldClass @hide
27200      */
27201     /**
27202      * @cfg {String} focusClass @hide
27203      */
27204     /**
27205      * @cfg {String} autoCreate @hide
27206      */
27207     /**
27208      * @cfg {String} inputType @hide
27209      */
27210     /**
27211      * @cfg {String} invalidClass @hide
27212      */
27213     /**
27214      * @cfg {String} invalidText @hide
27215      */
27216     /**
27217      * @cfg {String} msgFx @hide
27218      */
27219     /**
27220      * @cfg {String} validateOnBlur @hide
27221      */
27222 });
27223  
27224     /*
27225  * Based on
27226  * Ext JS Library 1.1.1
27227  * Copyright(c) 2006-2007, Ext JS, LLC.
27228  *  
27229  
27230  */
27231
27232 /**
27233  * @class Roo.form.HtmlEditor.ToolbarStandard
27234  * Basic Toolbar
27235
27236  * Usage:
27237  *
27238  new Roo.form.HtmlEditor({
27239     ....
27240     toolbars : [
27241         new Roo.form.HtmlEditorToolbar1({
27242             disable : { fonts: 1 , format: 1, ..., ... , ...],
27243             btns : [ .... ]
27244         })
27245     }
27246      
27247  * 
27248  * @cfg {Object} disable List of elements to disable..
27249  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27250  * 
27251  * 
27252  * NEEDS Extra CSS? 
27253  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27254  */
27255  
27256 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27257 {
27258     
27259     Roo.apply(this, config);
27260     
27261     // default disabled, based on 'good practice'..
27262     this.disable = this.disable || {};
27263     Roo.applyIf(this.disable, {
27264         fontSize : true,
27265         colors : true,
27266         specialElements : true
27267     });
27268     
27269     
27270     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27271     // dont call parent... till later.
27272 }
27273
27274 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27275     
27276     tb: false,
27277     
27278     rendered: false,
27279     
27280     editor : false,
27281     editorcore : false,
27282     /**
27283      * @cfg {Object} disable  List of toolbar elements to disable
27284          
27285      */
27286     disable : false,
27287     
27288     
27289      /**
27290      * @cfg {String} createLinkText The default text for the create link prompt
27291      */
27292     createLinkText : 'Please enter the URL for the link:',
27293     /**
27294      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27295      */
27296     defaultLinkValue : 'http:/'+'/',
27297    
27298     
27299       /**
27300      * @cfg {Array} fontFamilies An array of available font families
27301      */
27302     fontFamilies : [
27303         'Arial',
27304         'Courier New',
27305         'Tahoma',
27306         'Times New Roman',
27307         'Verdana'
27308     ],
27309     
27310     specialChars : [
27311            "&#169;",
27312           "&#174;",     
27313           "&#8482;",    
27314           "&#163;" ,    
27315          // "&#8212;",    
27316           "&#8230;",    
27317           "&#247;" ,    
27318         //  "&#225;" ,     ?? a acute?
27319            "&#8364;"    , //Euro
27320        //   "&#8220;"    ,
27321         //  "&#8221;"    ,
27322         //  "&#8226;"    ,
27323           "&#176;"  //   , // degrees
27324
27325          // "&#233;"     , // e ecute
27326          // "&#250;"     , // u ecute?
27327     ],
27328     
27329     specialElements : [
27330         {
27331             text: "Insert Table",
27332             xtype: 'MenuItem',
27333             xns : Roo.Menu,
27334             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27335                 
27336         },
27337         {    
27338             text: "Insert Image",
27339             xtype: 'MenuItem',
27340             xns : Roo.Menu,
27341             ihtml : '<img src="about:blank"/>'
27342             
27343         }
27344         
27345          
27346     ],
27347     
27348     
27349     inputElements : [ 
27350             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27351             "input:submit", "input:button", "select", "textarea", "label" ],
27352     formats : [
27353         ["p"] ,  
27354         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27355         ["pre"],[ "code"], 
27356         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27357         ['div'],['span'],
27358         ['sup'],['sub']
27359     ],
27360     
27361     cleanStyles : [
27362         "font-size"
27363     ],
27364      /**
27365      * @cfg {String} defaultFont default font to use.
27366      */
27367     defaultFont: 'tahoma',
27368    
27369     fontSelect : false,
27370     
27371     
27372     formatCombo : false,
27373     
27374     init : function(editor)
27375     {
27376         this.editor = editor;
27377         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27378         var editorcore = this.editorcore;
27379         
27380         var _t = this;
27381         
27382         var fid = editorcore.frameId;
27383         var etb = this;
27384         function btn(id, toggle, handler){
27385             var xid = fid + '-'+ id ;
27386             return {
27387                 id : xid,
27388                 cmd : id,
27389                 cls : 'x-btn-icon x-edit-'+id,
27390                 enableToggle:toggle !== false,
27391                 scope: _t, // was editor...
27392                 handler:handler||_t.relayBtnCmd,
27393                 clickEvent:'mousedown',
27394                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27395                 tabIndex:-1
27396             };
27397         }
27398         
27399         
27400         
27401         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27402         this.tb = tb;
27403          // stop form submits
27404         tb.el.on('click', function(e){
27405             e.preventDefault(); // what does this do?
27406         });
27407
27408         if(!this.disable.font) { // && !Roo.isSafari){
27409             /* why no safari for fonts 
27410             editor.fontSelect = tb.el.createChild({
27411                 tag:'select',
27412                 tabIndex: -1,
27413                 cls:'x-font-select',
27414                 html: this.createFontOptions()
27415             });
27416             
27417             editor.fontSelect.on('change', function(){
27418                 var font = editor.fontSelect.dom.value;
27419                 editor.relayCmd('fontname', font);
27420                 editor.deferFocus();
27421             }, editor);
27422             
27423             tb.add(
27424                 editor.fontSelect.dom,
27425                 '-'
27426             );
27427             */
27428             
27429         };
27430         if(!this.disable.formats){
27431             this.formatCombo = new Roo.form.ComboBox({
27432                 store: new Roo.data.SimpleStore({
27433                     id : 'tag',
27434                     fields: ['tag'],
27435                     data : this.formats // from states.js
27436                 }),
27437                 blockFocus : true,
27438                 name : '',
27439                 //autoCreate : {tag: "div",  size: "20"},
27440                 displayField:'tag',
27441                 typeAhead: false,
27442                 mode: 'local',
27443                 editable : false,
27444                 triggerAction: 'all',
27445                 emptyText:'Add tag',
27446                 selectOnFocus:true,
27447                 width:135,
27448                 listeners : {
27449                     'select': function(c, r, i) {
27450                         editorcore.insertTag(r.get('tag'));
27451                         editor.focus();
27452                     }
27453                 }
27454
27455             });
27456             tb.addField(this.formatCombo);
27457             
27458         }
27459         
27460         if(!this.disable.format){
27461             tb.add(
27462                 btn('bold'),
27463                 btn('italic'),
27464                 btn('underline'),
27465                 btn('strikethrough')
27466             );
27467         };
27468         if(!this.disable.fontSize){
27469             tb.add(
27470                 '-',
27471                 
27472                 
27473                 btn('increasefontsize', false, editorcore.adjustFont),
27474                 btn('decreasefontsize', false, editorcore.adjustFont)
27475             );
27476         };
27477         
27478         
27479         if(!this.disable.colors){
27480             tb.add(
27481                 '-', {
27482                     id:editorcore.frameId +'-forecolor',
27483                     cls:'x-btn-icon x-edit-forecolor',
27484                     clickEvent:'mousedown',
27485                     tooltip: this.buttonTips['forecolor'] || undefined,
27486                     tabIndex:-1,
27487                     menu : new Roo.menu.ColorMenu({
27488                         allowReselect: true,
27489                         focus: Roo.emptyFn,
27490                         value:'000000',
27491                         plain:true,
27492                         selectHandler: function(cp, color){
27493                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27494                             editor.deferFocus();
27495                         },
27496                         scope: editorcore,
27497                         clickEvent:'mousedown'
27498                     })
27499                 }, {
27500                     id:editorcore.frameId +'backcolor',
27501                     cls:'x-btn-icon x-edit-backcolor',
27502                     clickEvent:'mousedown',
27503                     tooltip: this.buttonTips['backcolor'] || undefined,
27504                     tabIndex:-1,
27505                     menu : new Roo.menu.ColorMenu({
27506                         focus: Roo.emptyFn,
27507                         value:'FFFFFF',
27508                         plain:true,
27509                         allowReselect: true,
27510                         selectHandler: function(cp, color){
27511                             if(Roo.isGecko){
27512                                 editorcore.execCmd('useCSS', false);
27513                                 editorcore.execCmd('hilitecolor', color);
27514                                 editorcore.execCmd('useCSS', true);
27515                                 editor.deferFocus();
27516                             }else{
27517                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27518                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27519                                 editor.deferFocus();
27520                             }
27521                         },
27522                         scope:editorcore,
27523                         clickEvent:'mousedown'
27524                     })
27525                 }
27526             );
27527         };
27528         // now add all the items...
27529         
27530
27531         if(!this.disable.alignments){
27532             tb.add(
27533                 '-',
27534                 btn('justifyleft'),
27535                 btn('justifycenter'),
27536                 btn('justifyright')
27537             );
27538         };
27539
27540         //if(!Roo.isSafari){
27541             if(!this.disable.links){
27542                 tb.add(
27543                     '-',
27544                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27545                 );
27546             };
27547
27548             if(!this.disable.lists){
27549                 tb.add(
27550                     '-',
27551                     btn('insertorderedlist'),
27552                     btn('insertunorderedlist')
27553                 );
27554             }
27555             if(!this.disable.sourceEdit){
27556                 tb.add(
27557                     '-',
27558                     btn('sourceedit', true, function(btn){
27559                         this.toggleSourceEdit(btn.pressed);
27560                     })
27561                 );
27562             }
27563         //}
27564         
27565         var smenu = { };
27566         // special menu.. - needs to be tidied up..
27567         if (!this.disable.special) {
27568             smenu = {
27569                 text: "&#169;",
27570                 cls: 'x-edit-none',
27571                 
27572                 menu : {
27573                     items : []
27574                 }
27575             };
27576             for (var i =0; i < this.specialChars.length; i++) {
27577                 smenu.menu.items.push({
27578                     
27579                     html: this.specialChars[i],
27580                     handler: function(a,b) {
27581                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27582                         //editor.insertAtCursor(a.html);
27583                         
27584                     },
27585                     tabIndex:-1
27586                 });
27587             }
27588             
27589             
27590             tb.add(smenu);
27591             
27592             
27593         }
27594         
27595         var cmenu = { };
27596         if (!this.disable.cleanStyles) {
27597             cmenu = {
27598                 cls: 'x-btn-icon x-btn-clear',
27599                 
27600                 menu : {
27601                     items : []
27602                 }
27603             };
27604             for (var i =0; i < this.cleanStyles.length; i++) {
27605                 cmenu.menu.items.push({
27606                     actiontype : this.cleanStyles[i],
27607                     html: 'Remove ' + this.cleanStyles[i],
27608                     handler: function(a,b) {
27609 //                        Roo.log(a);
27610 //                        Roo.log(b);
27611                         var c = Roo.get(editorcore.doc.body);
27612                         c.select('[style]').each(function(s) {
27613                             s.dom.style.removeProperty(a.actiontype);
27614                         });
27615                         editorcore.syncValue();
27616                     },
27617                     tabIndex:-1
27618                 });
27619             }
27620             cmenu.menu.items.push({
27621                 actiontype : 'tablewidths',
27622                 html: 'Remove Table Widths',
27623                 handler: function(a,b) {
27624                     editorcore.cleanTableWidths();
27625                     editorcore.syncValue();
27626                 },
27627                 tabIndex:-1
27628             });
27629             cmenu.menu.items.push({
27630                 actiontype : 'word',
27631                 html: 'Remove MS Word Formating',
27632                 handler: function(a,b) {
27633                     editorcore.cleanWord();
27634                     editorcore.syncValue();
27635                 },
27636                 tabIndex:-1
27637             });
27638             
27639             cmenu.menu.items.push({
27640                 actiontype : 'all',
27641                 html: 'Remove All Styles',
27642                 handler: function(a,b) {
27643                     
27644                     var c = Roo.get(editorcore.doc.body);
27645                     c.select('[style]').each(function(s) {
27646                         s.dom.removeAttribute('style');
27647                     });
27648                     editorcore.syncValue();
27649                 },
27650                 tabIndex:-1
27651             });
27652             
27653             cmenu.menu.items.push({
27654                 actiontype : 'all',
27655                 html: 'Remove All CSS Classes',
27656                 handler: function(a,b) {
27657                     
27658                     var c = Roo.get(editorcore.doc.body);
27659                     c.select('[class]').each(function(s) {
27660                         s.dom.removeAttribute('class');
27661                     });
27662                     editorcore.cleanWord();
27663                     editorcore.syncValue();
27664                 },
27665                 tabIndex:-1
27666             });
27667             
27668              cmenu.menu.items.push({
27669                 actiontype : 'tidy',
27670                 html: 'Tidy HTML Source',
27671                 handler: function(a,b) {
27672                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27673                     editorcore.syncValue();
27674                 },
27675                 tabIndex:-1
27676             });
27677             
27678             
27679             tb.add(cmenu);
27680         }
27681          
27682         if (!this.disable.specialElements) {
27683             var semenu = {
27684                 text: "Other;",
27685                 cls: 'x-edit-none',
27686                 menu : {
27687                     items : []
27688                 }
27689             };
27690             for (var i =0; i < this.specialElements.length; i++) {
27691                 semenu.menu.items.push(
27692                     Roo.apply({ 
27693                         handler: function(a,b) {
27694                             editor.insertAtCursor(this.ihtml);
27695                         }
27696                     }, this.specialElements[i])
27697                 );
27698                     
27699             }
27700             
27701             tb.add(semenu);
27702             
27703             
27704         }
27705          
27706         
27707         if (this.btns) {
27708             for(var i =0; i< this.btns.length;i++) {
27709                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27710                 b.cls =  'x-edit-none';
27711                 
27712                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27713                     b.cls += ' x-init-enable';
27714                 }
27715                 
27716                 b.scope = editorcore;
27717                 tb.add(b);
27718             }
27719         
27720         }
27721         
27722         
27723         
27724         // disable everything...
27725         
27726         this.tb.items.each(function(item){
27727             
27728            if(
27729                 item.id != editorcore.frameId+ '-sourceedit' && 
27730                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27731             ){
27732                 
27733                 item.disable();
27734             }
27735         });
27736         this.rendered = true;
27737         
27738         // the all the btns;
27739         editor.on('editorevent', this.updateToolbar, this);
27740         // other toolbars need to implement this..
27741         //editor.on('editmodechange', this.updateToolbar, this);
27742     },
27743     
27744     
27745     relayBtnCmd : function(btn) {
27746         this.editorcore.relayCmd(btn.cmd);
27747     },
27748     // private used internally
27749     createLink : function(){
27750         //Roo.log("create link?");
27751         var ec = this.editorcore;
27752         var ar = ec.getAllAncestors();
27753         var n = false;
27754         for(var i = 0;i< ar.length;i++) {
27755             if (ar[i] && ar[i].nodeName == 'A') {
27756                 n = ar[i];
27757                 break;
27758             }
27759         }
27760         
27761         (function() {
27762             
27763             Roo.MessageBox.show({
27764                 title : "Add / Edit Link URL",
27765                 msg : "Enter the url for the link",
27766                 buttons: Roo.MessageBox.OKCANCEL,
27767                 fn: function(btn, url){
27768                     if (btn != 'ok') {
27769                         return;
27770                     }
27771                     if(url && url != 'http:/'+'/'){
27772                         if (n) {
27773                             n.setAttribute('href', url);
27774                         } else {
27775                             ec.relayCmd('createlink', url);
27776                         }
27777                     }
27778                 },
27779                 minWidth:250,
27780                 prompt:true,
27781                 //multiline: multiline,
27782                 modal : true,
27783                 value :  n  ? n.getAttribute('href') : '' 
27784             });
27785             
27786              
27787         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27788         
27789     },
27790
27791     
27792     /**
27793      * Protected method that will not generally be called directly. It triggers
27794      * a toolbar update by reading the markup state of the current selection in the editor.
27795      */
27796     updateToolbar: function(){
27797
27798         if(!this.editorcore.activated){
27799             this.editor.onFirstFocus();
27800             return;
27801         }
27802
27803         var btns = this.tb.items.map, 
27804             doc = this.editorcore.doc,
27805             frameId = this.editorcore.frameId;
27806
27807         if(!this.disable.font && !Roo.isSafari){
27808             /*
27809             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27810             if(name != this.fontSelect.dom.value){
27811                 this.fontSelect.dom.value = name;
27812             }
27813             */
27814         }
27815         if(!this.disable.format){
27816             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27817             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27818             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27819             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27820         }
27821         if(!this.disable.alignments){
27822             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27823             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27824             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27825         }
27826         if(!Roo.isSafari && !this.disable.lists){
27827             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27828             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27829         }
27830         
27831         var ans = this.editorcore.getAllAncestors();
27832         if (this.formatCombo) {
27833             
27834             
27835             var store = this.formatCombo.store;
27836             this.formatCombo.setValue("");
27837             for (var i =0; i < ans.length;i++) {
27838                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27839                     // select it..
27840                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27841                     break;
27842                 }
27843             }
27844         }
27845         
27846         
27847         
27848         // hides menus... - so this cant be on a menu...
27849         Roo.menu.MenuMgr.hideAll();
27850
27851         //this.editorsyncValue();
27852     },
27853    
27854     
27855     createFontOptions : function(){
27856         var buf = [], fs = this.fontFamilies, ff, lc;
27857         
27858         
27859         
27860         for(var i = 0, len = fs.length; i< len; i++){
27861             ff = fs[i];
27862             lc = ff.toLowerCase();
27863             buf.push(
27864                 '<option value="',lc,'" style="font-family:',ff,';"',
27865                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27866                     ff,
27867                 '</option>'
27868             );
27869         }
27870         return buf.join('');
27871     },
27872     
27873     toggleSourceEdit : function(sourceEditMode){
27874         
27875         Roo.log("toolbar toogle");
27876         if(sourceEditMode === undefined){
27877             sourceEditMode = !this.sourceEditMode;
27878         }
27879         this.sourceEditMode = sourceEditMode === true;
27880         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27881         // just toggle the button?
27882         if(btn.pressed !== this.sourceEditMode){
27883             btn.toggle(this.sourceEditMode);
27884             return;
27885         }
27886         
27887         if(sourceEditMode){
27888             Roo.log("disabling buttons");
27889             this.tb.items.each(function(item){
27890                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27891                     item.disable();
27892                 }
27893             });
27894           
27895         }else{
27896             Roo.log("enabling buttons");
27897             if(this.editorcore.initialized){
27898                 this.tb.items.each(function(item){
27899                     item.enable();
27900                 });
27901                 // initialize 'blocks'
27902                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27903                     Roo.htmleditor.Block.factory(e).updateElement(e);
27904                 },this);
27905             
27906             }
27907             
27908         }
27909         Roo.log("calling toggole on editor");
27910         // tell the editor that it's been pressed..
27911         this.editor.toggleSourceEdit(sourceEditMode);
27912        
27913     },
27914      /**
27915      * Object collection of toolbar tooltips for the buttons in the editor. The key
27916      * is the command id associated with that button and the value is a valid QuickTips object.
27917      * For example:
27918 <pre><code>
27919 {
27920     bold : {
27921         title: 'Bold (Ctrl+B)',
27922         text: 'Make the selected text bold.',
27923         cls: 'x-html-editor-tip'
27924     },
27925     italic : {
27926         title: 'Italic (Ctrl+I)',
27927         text: 'Make the selected text italic.',
27928         cls: 'x-html-editor-tip'
27929     },
27930     ...
27931 </code></pre>
27932     * @type Object
27933      */
27934     buttonTips : {
27935         bold : {
27936             title: 'Bold (Ctrl+B)',
27937             text: 'Make the selected text bold.',
27938             cls: 'x-html-editor-tip'
27939         },
27940         italic : {
27941             title: 'Italic (Ctrl+I)',
27942             text: 'Make the selected text italic.',
27943             cls: 'x-html-editor-tip'
27944         },
27945         underline : {
27946             title: 'Underline (Ctrl+U)',
27947             text: 'Underline the selected text.',
27948             cls: 'x-html-editor-tip'
27949         },
27950         strikethrough : {
27951             title: 'Strikethrough',
27952             text: 'Strikethrough the selected text.',
27953             cls: 'x-html-editor-tip'
27954         },
27955         increasefontsize : {
27956             title: 'Grow Text',
27957             text: 'Increase the font size.',
27958             cls: 'x-html-editor-tip'
27959         },
27960         decreasefontsize : {
27961             title: 'Shrink Text',
27962             text: 'Decrease the font size.',
27963             cls: 'x-html-editor-tip'
27964         },
27965         backcolor : {
27966             title: 'Text Highlight Color',
27967             text: 'Change the background color of the selected text.',
27968             cls: 'x-html-editor-tip'
27969         },
27970         forecolor : {
27971             title: 'Font Color',
27972             text: 'Change the color of the selected text.',
27973             cls: 'x-html-editor-tip'
27974         },
27975         justifyleft : {
27976             title: 'Align Text Left',
27977             text: 'Align text to the left.',
27978             cls: 'x-html-editor-tip'
27979         },
27980         justifycenter : {
27981             title: 'Center Text',
27982             text: 'Center text in the editor.',
27983             cls: 'x-html-editor-tip'
27984         },
27985         justifyright : {
27986             title: 'Align Text Right',
27987             text: 'Align text to the right.',
27988             cls: 'x-html-editor-tip'
27989         },
27990         insertunorderedlist : {
27991             title: 'Bullet List',
27992             text: 'Start a bulleted list.',
27993             cls: 'x-html-editor-tip'
27994         },
27995         insertorderedlist : {
27996             title: 'Numbered List',
27997             text: 'Start a numbered list.',
27998             cls: 'x-html-editor-tip'
27999         },
28000         createlink : {
28001             title: 'Hyperlink',
28002             text: 'Make the selected text a hyperlink.',
28003             cls: 'x-html-editor-tip'
28004         },
28005         sourceedit : {
28006             title: 'Source Edit',
28007             text: 'Switch to source editing mode.',
28008             cls: 'x-html-editor-tip'
28009         }
28010     },
28011     // private
28012     onDestroy : function(){
28013         if(this.rendered){
28014             
28015             this.tb.items.each(function(item){
28016                 if(item.menu){
28017                     item.menu.removeAll();
28018                     if(item.menu.el){
28019                         item.menu.el.destroy();
28020                     }
28021                 }
28022                 item.destroy();
28023             });
28024              
28025         }
28026     },
28027     onFirstFocus: function() {
28028         this.tb.items.each(function(item){
28029            item.enable();
28030         });
28031     }
28032 };
28033
28034
28035
28036
28037 // <script type="text/javascript">
28038 /*
28039  * Based on
28040  * Ext JS Library 1.1.1
28041  * Copyright(c) 2006-2007, Ext JS, LLC.
28042  *  
28043  
28044  */
28045
28046  
28047 /**
28048  * @class Roo.form.HtmlEditor.ToolbarContext
28049  * Context Toolbar
28050  * 
28051  * Usage:
28052  *
28053  new Roo.form.HtmlEditor({
28054     ....
28055     toolbars : [
28056         { xtype: 'ToolbarStandard', styles : {} }
28057         { xtype: 'ToolbarContext', disable : {} }
28058     ]
28059 })
28060
28061      
28062  * 
28063  * @config : {Object} disable List of elements to disable.. (not done yet.)
28064  * @config : {Object} styles  Map of styles available.
28065  * 
28066  */
28067
28068 Roo.form.HtmlEditor.ToolbarContext = function(config)
28069 {
28070     
28071     Roo.apply(this, config);
28072     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28073     // dont call parent... till later.
28074     this.styles = this.styles || {};
28075 }
28076
28077  
28078
28079 Roo.form.HtmlEditor.ToolbarContext.types = {
28080     'IMG' : [
28081         {
28082             name : 'width',
28083             title: "Width",
28084             width: 40
28085         },
28086         {
28087             name : 'height',
28088             title: "Height",
28089             width: 40
28090         },
28091         {
28092             name : 'align',
28093             title: "Align",
28094             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28095             width : 80
28096             
28097         },
28098         {
28099             name : 'border',
28100             title: "Border",
28101             width: 40
28102         },
28103         {
28104             name : 'alt',
28105             title: "Alt",
28106             width: 120
28107         },
28108         {
28109             name : 'src',
28110             title: "Src",
28111             width: 220
28112         }
28113         
28114     ],
28115     
28116     'FIGURE' : [
28117         {
28118             name : 'align',
28119             title: "Align",
28120             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28121             width : 80  
28122         }
28123     ],
28124     'A' : [
28125         {
28126             name : 'name',
28127             title: "Name",
28128             width: 50
28129         },
28130         {
28131             name : 'target',
28132             title: "Target",
28133             width: 120
28134         },
28135         {
28136             name : 'href',
28137             title: "Href",
28138             width: 220
28139         } // border?
28140         
28141     ],
28142     
28143     'INPUT' : [
28144         {
28145             name : 'name',
28146             title: "name",
28147             width: 120
28148         },
28149         {
28150             name : 'value',
28151             title: "Value",
28152             width: 120
28153         },
28154         {
28155             name : 'width',
28156             title: "Width",
28157             width: 40
28158         }
28159     ],
28160     'LABEL' : [
28161          {
28162             name : 'for',
28163             title: "For",
28164             width: 120
28165         }
28166     ],
28167     'TEXTAREA' : [
28168         {
28169             name : 'name',
28170             title: "name",
28171             width: 120
28172         },
28173         {
28174             name : 'rows',
28175             title: "Rows",
28176             width: 20
28177         },
28178         {
28179             name : 'cols',
28180             title: "Cols",
28181             width: 20
28182         }
28183     ],
28184     'SELECT' : [
28185         {
28186             name : 'name',
28187             title: "name",
28188             width: 120
28189         },
28190         {
28191             name : 'selectoptions',
28192             title: "Options",
28193             width: 200
28194         }
28195     ],
28196     
28197     // should we really allow this??
28198     // should this just be 
28199     'BODY' : [
28200         
28201         {
28202             name : 'title',
28203             title: "Title",
28204             width: 200,
28205             disabled : true
28206         }
28207     ],
28208  
28209     '*' : [
28210         // empty.
28211     ]
28212
28213 };
28214
28215 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28216 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28217
28218 Roo.form.HtmlEditor.ToolbarContext.options = {
28219         'font-family'  : [ 
28220                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28221                 [ 'Courier New', 'Courier New'],
28222                 [ 'Tahoma', 'Tahoma'],
28223                 [ 'Times New Roman,serif', 'Times'],
28224                 [ 'Verdana','Verdana' ]
28225         ]
28226 };
28227
28228 // fixme - these need to be configurable..
28229  
28230
28231 //Roo.form.HtmlEditor.ToolbarContext.types
28232
28233
28234 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28235     
28236     tb: false,
28237     
28238     rendered: false,
28239     
28240     editor : false,
28241     editorcore : false,
28242     /**
28243      * @cfg {Object} disable  List of toolbar elements to disable
28244          
28245      */
28246     disable : false,
28247     /**
28248      * @cfg {Object} styles List of styles 
28249      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28250      *
28251      * These must be defined in the page, so they get rendered correctly..
28252      * .headline { }
28253      * TD.underline { }
28254      * 
28255      */
28256     styles : false,
28257     
28258     options: false,
28259     
28260     toolbars : false,
28261     
28262     init : function(editor)
28263     {
28264         this.editor = editor;
28265         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28266         var editorcore = this.editorcore;
28267         
28268         var fid = editorcore.frameId;
28269         var etb = this;
28270         function btn(id, toggle, handler){
28271             var xid = fid + '-'+ id ;
28272             return {
28273                 id : xid,
28274                 cmd : id,
28275                 cls : 'x-btn-icon x-edit-'+id,
28276                 enableToggle:toggle !== false,
28277                 scope: editorcore, // was editor...
28278                 handler:handler||editorcore.relayBtnCmd,
28279                 clickEvent:'mousedown',
28280                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28281                 tabIndex:-1
28282             };
28283         }
28284         // create a new element.
28285         var wdiv = editor.wrap.createChild({
28286                 tag: 'div'
28287             }, editor.wrap.dom.firstChild.nextSibling, true);
28288         
28289         // can we do this more than once??
28290         
28291          // stop form submits
28292       
28293  
28294         // disable everything...
28295         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28296         this.toolbars = {};
28297         // block toolbars are built in updateToolbar when needed.
28298         for (var i in  ty) {
28299             
28300             this.toolbars[i] = this.buildToolbar(ty[i],i);
28301         }
28302         this.tb = this.toolbars.BODY;
28303         this.tb.el.show();
28304         this.buildFooter();
28305         this.footer.show();
28306         editor.on('hide', function( ) { this.footer.hide() }, this);
28307         editor.on('show', function( ) { this.footer.show() }, this);
28308         
28309          
28310         this.rendered = true;
28311         
28312         // the all the btns;
28313         editor.on('editorevent', this.updateToolbar, this);
28314         // other toolbars need to implement this..
28315         //editor.on('editmodechange', this.updateToolbar, this);
28316     },
28317     
28318     
28319     
28320     /**
28321      * Protected method that will not generally be called directly. It triggers
28322      * a toolbar update by reading the markup state of the current selection in the editor.
28323      *
28324      * Note you can force an update by calling on('editorevent', scope, false)
28325      */
28326     updateToolbar: function(editor ,ev, sel)
28327     {
28328         
28329         if (ev) {
28330             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28331         }
28332         
28333         //Roo.log(ev);
28334         // capture mouse up - this is handy for selecting images..
28335         // perhaps should go somewhere else...
28336         if(!this.editorcore.activated){
28337              this.editor.onFirstFocus();
28338             return;
28339         }
28340         //Roo.log(ev ? ev.target : 'NOTARGET');
28341         
28342         
28343         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28344         // selectNode - might want to handle IE?
28345         
28346         
28347         
28348         if (ev &&
28349             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28350             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28351             // they have click on an image...
28352             // let's see if we can change the selection...
28353             sel = ev.target;
28354             
28355             // this triggers looping?
28356             //this.editorcore.selectNode(sel);
28357              
28358         }
28359         
28360         // this forces an id..
28361         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28362              e.classList.remove('roo-ed-selection');
28363         });
28364         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28365         //Roo.get(node).addClass('roo-ed-selection');
28366       
28367         //var updateFooter = sel ? false : true; 
28368         
28369         
28370         var ans = this.editorcore.getAllAncestors();
28371         
28372         // pick
28373         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28374         
28375         if (!sel) { 
28376             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28377             sel = sel ? sel : this.editorcore.doc.body;
28378             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28379             
28380         }
28381         
28382         var tn = sel.tagName.toUpperCase();
28383         var lastSel = this.tb.selectedNode;
28384         this.tb.selectedNode = sel;
28385         var left_label = tn;
28386         
28387         // ok see if we are editing a block?
28388         
28389         var db = false;
28390         // you are not actually selecting the block.
28391         if (sel && sel.hasAttribute('data-block')) {
28392             db = sel;
28393         } else if (sel && sel.closest('[data-block]')) {
28394             
28395             db = sel.closest('[data-block]');
28396             //var cepar = sel.closest('[contenteditable=true]');
28397             //if (db && cepar && cepar.tagName != 'BODY') {
28398             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28399             //}   
28400         }
28401         
28402         
28403         var block = false;
28404         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28405         if (db && this.editorcore.enableBlocks) {
28406             block = Roo.htmleditor.Block.factory(db);
28407             
28408             
28409             if (block) {
28410                  db.className = (
28411                         db.classList.length > 0  ? db.className + ' ' : ''
28412                     )  + 'roo-ed-selection';
28413                  
28414                  // since we removed it earlier... its not there..
28415                 tn = 'BLOCK.' + db.getAttribute('data-block');
28416                 
28417                 //this.editorcore.selectNode(db);
28418                 if (typeof(this.toolbars[tn]) == 'undefined') {
28419                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28420                 }
28421                 this.toolbars[tn].selectedNode = db;
28422                 left_label = block.friendly_name;
28423                 ans = this.editorcore.getAllAncestors();
28424             }
28425             
28426                 
28427             
28428         }
28429         
28430         
28431         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28432             return; // no change?
28433         }
28434         
28435         
28436           
28437         this.tb.el.hide();
28438         ///console.log("show: " + tn);
28439         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28440         
28441         this.tb.el.show();
28442         // update name
28443         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28444         
28445         
28446         // update attributes
28447         if (block && this.tb.fields) {
28448              
28449             this.tb.fields.each(function(e) {
28450                 e.setValue(block[e.name]);
28451             });
28452             
28453             
28454         } else  if (this.tb.fields && this.tb.selectedNode) {
28455             this.tb.fields.each( function(e) {
28456                 if (e.stylename) {
28457                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28458                     return;
28459                 } 
28460                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28461             }, this);
28462             this.updateToolbarStyles(this.tb.selectedNode);  
28463         }
28464         
28465         
28466        
28467         Roo.menu.MenuMgr.hideAll();
28468
28469         
28470         
28471     
28472         // update the footer
28473         //
28474         this.updateFooter(ans);
28475              
28476     },
28477     
28478     updateToolbarStyles : function(sel)
28479     {
28480         var hasStyles = false;
28481         for(var i in this.styles) {
28482             hasStyles = true;
28483             break;
28484         }
28485         
28486         // update styles
28487         if (hasStyles && this.tb.hasStyles) { 
28488             var st = this.tb.fields.item(0);
28489             
28490             st.store.removeAll();
28491             var cn = sel.className.split(/\s+/);
28492             
28493             var avs = [];
28494             if (this.styles['*']) {
28495                 
28496                 Roo.each(this.styles['*'], function(v) {
28497                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28498                 });
28499             }
28500             if (this.styles[tn]) { 
28501                 Roo.each(this.styles[tn], function(v) {
28502                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28503                 });
28504             }
28505             
28506             st.store.loadData(avs);
28507             st.collapse();
28508             st.setValue(cn);
28509         }
28510     },
28511     
28512      
28513     updateFooter : function(ans)
28514     {
28515         var html = '';
28516         if (ans === false) {
28517             this.footDisp.dom.innerHTML = '';
28518             return;
28519         }
28520         
28521         this.footerEls = ans.reverse();
28522         Roo.each(this.footerEls, function(a,i) {
28523             if (!a) { return; }
28524             html += html.length ? ' &gt; '  :  '';
28525             
28526             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28527             
28528         });
28529        
28530         // 
28531         var sz = this.footDisp.up('td').getSize();
28532         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28533         this.footDisp.dom.style.marginLeft = '5px';
28534         
28535         this.footDisp.dom.style.overflow = 'hidden';
28536         
28537         this.footDisp.dom.innerHTML = html;
28538             
28539         
28540     },
28541    
28542        
28543     // private
28544     onDestroy : function(){
28545         if(this.rendered){
28546             
28547             this.tb.items.each(function(item){
28548                 if(item.menu){
28549                     item.menu.removeAll();
28550                     if(item.menu.el){
28551                         item.menu.el.destroy();
28552                     }
28553                 }
28554                 item.destroy();
28555             });
28556              
28557         }
28558     },
28559     onFirstFocus: function() {
28560         // need to do this for all the toolbars..
28561         this.tb.items.each(function(item){
28562            item.enable();
28563         });
28564     },
28565     buildToolbar: function(tlist, nm, friendly_name, block)
28566     {
28567         var editor = this.editor;
28568         var editorcore = this.editorcore;
28569          // create a new element.
28570         var wdiv = editor.wrap.createChild({
28571                 tag: 'div'
28572             }, editor.wrap.dom.firstChild.nextSibling, true);
28573         
28574        
28575         var tb = new Roo.Toolbar(wdiv);
28576         ///this.tb = tb; // << this sets the active toolbar..
28577         if (tlist === false && block) {
28578             tlist = block.contextMenu(this);
28579         }
28580         
28581         tb.hasStyles = false;
28582         tb.name = nm;
28583         
28584         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28585         
28586         var styles = Array.from(this.styles);
28587         
28588         
28589         // styles...
28590         if (styles && styles.length) {
28591             tb.hasStyles = true;
28592             // this needs a multi-select checkbox...
28593             tb.addField( new Roo.form.ComboBox({
28594                 store: new Roo.data.SimpleStore({
28595                     id : 'val',
28596                     fields: ['val', 'selected'],
28597                     data : [] 
28598                 }),
28599                 name : '-roo-edit-className',
28600                 attrname : 'className',
28601                 displayField: 'val',
28602                 typeAhead: false,
28603                 mode: 'local',
28604                 editable : false,
28605                 triggerAction: 'all',
28606                 emptyText:'Select Style',
28607                 selectOnFocus:true,
28608                 width: 130,
28609                 listeners : {
28610                     'select': function(c, r, i) {
28611                         // initial support only for on class per el..
28612                         tb.selectedNode.className =  r ? r.get('val') : '';
28613                         editorcore.syncValue();
28614                     }
28615                 }
28616     
28617             }));
28618         }
28619         
28620         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28621         
28622         
28623         for (var i = 0; i < tlist.length; i++) {
28624             
28625             // newer versions will use xtype cfg to create menus.
28626             if (typeof(tlist[i].xtype) != 'undefined') {
28627                 
28628                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28629                 
28630                 
28631                 continue;
28632             }
28633             
28634             var item = tlist[i];
28635             tb.add(item.title + ":&nbsp;");
28636             
28637             
28638             //optname == used so you can configure the options available..
28639             var opts = item.opts ? item.opts : false;
28640             if (item.optname) { // use the b
28641                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28642            
28643             }
28644             
28645             if (opts) {
28646                 // opts == pulldown..
28647                 tb.addField( new Roo.form.ComboBox({
28648                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28649                         id : 'val',
28650                         fields: ['val', 'display'],
28651                         data : opts  
28652                     }),
28653                     name : '-roo-edit-' + tlist[i].name,
28654                     
28655                     attrname : tlist[i].name,
28656                     stylename : item.style ? item.style : false,
28657                     
28658                     displayField: item.displayField ? item.displayField : 'val',
28659                     valueField :  'val',
28660                     typeAhead: false,
28661                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28662                     editable : false,
28663                     triggerAction: 'all',
28664                     emptyText:'Select',
28665                     selectOnFocus:true,
28666                     width: item.width ? item.width  : 130,
28667                     listeners : {
28668                         'select': function(c, r, i) {
28669                              
28670                             
28671                             if (c.stylename) {
28672                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28673                                 editorcore.syncValue();
28674                                 return;
28675                             }
28676                             if (r === false) {
28677                                 tb.selectedNode.removeAttribute(c.attrname);
28678                                 editorcore.syncValue();
28679                                 return;
28680                             }
28681                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28682                             editorcore.syncValue();
28683                         }
28684                     }
28685
28686                 }));
28687                 continue;
28688                     
28689                  
28690                 /*
28691                 tb.addField( new Roo.form.TextField({
28692                     name: i,
28693                     width: 100,
28694                     //allowBlank:false,
28695                     value: ''
28696                 }));
28697                 continue;
28698                 */
28699             }
28700             tb.addField( new Roo.form.TextField({
28701                 name: '-roo-edit-' + tlist[i].name,
28702                 attrname : tlist[i].name,
28703                 
28704                 width: item.width,
28705                 //allowBlank:true,
28706                 value: '',
28707                 listeners: {
28708                     'change' : function(f, nv, ov) {
28709                         
28710                          
28711                         tb.selectedNode.setAttribute(f.attrname, nv);
28712                         editorcore.syncValue();
28713                     }
28714                 }
28715             }));
28716              
28717         }
28718         
28719         var _this = this;
28720         var show_delete = !block || block.deleteTitle !== false;
28721         if(nm == 'BODY'){
28722             show_delete = false;
28723             tb.addSeparator();
28724         
28725             tb.addButton( {
28726                 text: 'Stylesheets',
28727
28728                 listeners : {
28729                     click : function ()
28730                     {
28731                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28732                     }
28733                 }
28734             });
28735         }
28736         
28737         tb.addFill();
28738         if (show_delete) {
28739             tb.addButton({
28740                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28741         
28742                 listeners : {
28743                     click : function ()
28744                     {
28745                         var sn = tb.selectedNode;
28746                         if (block) {
28747                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28748                             
28749                         }
28750                         if (!sn) {
28751                             return;
28752                         }
28753                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28754                         if (sn.hasAttribute('data-block')) {
28755                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28756                             sn.parentNode.removeChild(sn);
28757                             
28758                         } else if (sn && sn.tagName != 'BODY') {
28759                             // remove and keep parents.
28760                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28761                             a.replaceTag(sn);
28762                         }
28763                         
28764                         
28765                         var range = editorcore.createRange();
28766             
28767                         range.setStart(stn,0);
28768                         range.setEnd(stn,0); 
28769                         var selection = editorcore.getSelection();
28770                         selection.removeAllRanges();
28771                         selection.addRange(range);
28772                         
28773                         
28774                         //_this.updateToolbar(null, null, pn);
28775                         _this.updateToolbar(null, null, null);
28776                         _this.updateFooter(false);
28777                         
28778                     }
28779                 }
28780                 
28781                         
28782                     
28783                 
28784             });
28785         }    
28786         
28787         tb.el.on('click', function(e){
28788             e.preventDefault(); // what does this do?
28789         });
28790         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28791         tb.el.hide();
28792         
28793         // dont need to disable them... as they will get hidden
28794         return tb;
28795          
28796         
28797     },
28798     buildFooter : function()
28799     {
28800         
28801         var fel = this.editor.wrap.createChild();
28802         this.footer = new Roo.Toolbar(fel);
28803         // toolbar has scrolly on left / right?
28804         var footDisp= new Roo.Toolbar.Fill();
28805         var _t = this;
28806         this.footer.add(
28807             {
28808                 text : '&lt;',
28809                 xtype: 'Button',
28810                 handler : function() {
28811                     _t.footDisp.scrollTo('left',0,true)
28812                 }
28813             }
28814         );
28815         this.footer.add( footDisp );
28816         this.footer.add( 
28817             {
28818                 text : '&gt;',
28819                 xtype: 'Button',
28820                 handler : function() {
28821                     // no animation..
28822                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28823                 }
28824             }
28825         );
28826         var fel = Roo.get(footDisp.el);
28827         fel.addClass('x-editor-context');
28828         this.footDispWrap = fel; 
28829         this.footDispWrap.overflow  = 'hidden';
28830         
28831         this.footDisp = fel.createChild();
28832         this.footDispWrap.on('click', this.onContextClick, this)
28833         
28834         
28835     },
28836     // when the footer contect changes
28837     onContextClick : function (ev,dom)
28838     {
28839         ev.preventDefault();
28840         var  cn = dom.className;
28841         //Roo.log(cn);
28842         if (!cn.match(/x-ed-loc-/)) {
28843             return;
28844         }
28845         var n = cn.split('-').pop();
28846         var ans = this.footerEls;
28847         var sel = ans[n];
28848         
28849         this.editorcore.selectNode(sel);
28850         
28851         
28852         this.updateToolbar(null, null, sel);
28853         
28854         
28855     }
28856     
28857     
28858     
28859     
28860     
28861 });
28862
28863
28864
28865
28866
28867 /*
28868  * Based on:
28869  * Ext JS Library 1.1.1
28870  * Copyright(c) 2006-2007, Ext JS, LLC.
28871  *
28872  * Originally Released Under LGPL - original licence link has changed is not relivant.
28873  *
28874  * Fork - LGPL
28875  * <script type="text/javascript">
28876  */
28877  
28878 /**
28879  * @class Roo.form.BasicForm
28880  * @extends Roo.util.Observable
28881  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28882  * @constructor
28883  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28884  * @param {Object} config Configuration options
28885  */
28886 Roo.form.BasicForm = function(el, config){
28887     this.allItems = [];
28888     this.childForms = [];
28889     Roo.apply(this, config);
28890     /*
28891      * The Roo.form.Field items in this form.
28892      * @type MixedCollection
28893      */
28894      
28895      
28896     this.items = new Roo.util.MixedCollection(false, function(o){
28897         return o.id || (o.id = Roo.id());
28898     });
28899     this.addEvents({
28900         /**
28901          * @event beforeaction
28902          * Fires before any action is performed. Return false to cancel the action.
28903          * @param {Form} this
28904          * @param {Action} action The action to be performed
28905          */
28906         beforeaction: true,
28907         /**
28908          * @event actionfailed
28909          * Fires when an action fails.
28910          * @param {Form} this
28911          * @param {Action} action The action that failed
28912          */
28913         actionfailed : true,
28914         /**
28915          * @event actioncomplete
28916          * Fires when an action is completed.
28917          * @param {Form} this
28918          * @param {Action} action The action that completed
28919          */
28920         actioncomplete : true
28921     });
28922     if(el){
28923         this.initEl(el);
28924     }
28925     Roo.form.BasicForm.superclass.constructor.call(this);
28926     
28927     Roo.form.BasicForm.popover.apply();
28928 };
28929
28930 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28931     /**
28932      * @cfg {String} method
28933      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28934      */
28935     /**
28936      * @cfg {DataReader} reader
28937      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28938      * This is optional as there is built-in support for processing JSON.
28939      */
28940     /**
28941      * @cfg {DataReader} errorReader
28942      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28943      * This is completely optional as there is built-in support for processing JSON.
28944      */
28945     /**
28946      * @cfg {String} url
28947      * The URL to use for form actions if one isn't supplied in the action options.
28948      */
28949     /**
28950      * @cfg {Boolean} fileUpload
28951      * Set to true if this form is a file upload.
28952      */
28953      
28954     /**
28955      * @cfg {Object} baseParams
28956      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28957      */
28958      /**
28959      
28960     /**
28961      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28962      */
28963     timeout: 30,
28964
28965     // private
28966     activeAction : null,
28967
28968     /**
28969      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28970      * or setValues() data instead of when the form was first created.
28971      */
28972     trackResetOnLoad : false,
28973     
28974     
28975     /**
28976      * childForms - used for multi-tab forms
28977      * @type {Array}
28978      */
28979     childForms : false,
28980     
28981     /**
28982      * allItems - full list of fields.
28983      * @type {Array}
28984      */
28985     allItems : false,
28986     
28987     /**
28988      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28989      * element by passing it or its id or mask the form itself by passing in true.
28990      * @type Mixed
28991      */
28992     waitMsgTarget : false,
28993     
28994     /**
28995      * @type Boolean
28996      */
28997     disableMask : false,
28998     
28999     /**
29000      * @cfg {Boolean} errorMask (true|false) default false
29001      */
29002     errorMask : false,
29003     
29004     /**
29005      * @cfg {Number} maskOffset Default 100
29006      */
29007     maskOffset : 100,
29008
29009     // private
29010     initEl : function(el){
29011         this.el = Roo.get(el);
29012         this.id = this.el.id || Roo.id();
29013         this.el.on('submit', this.onSubmit, this);
29014         this.el.addClass('x-form');
29015     },
29016
29017     // private
29018     onSubmit : function(e){
29019         e.stopEvent();
29020     },
29021
29022     /**
29023      * Returns true if client-side validation on the form is successful.
29024      * @return Boolean
29025      */
29026     isValid : function(){
29027         var valid = true;
29028         var target = false;
29029         this.items.each(function(f){
29030             if(f.validate()){
29031                 return;
29032             }
29033             
29034             valid = false;
29035                 
29036             if(!target && f.el.isVisible(true)){
29037                 target = f;
29038             }
29039         });
29040         
29041         if(this.errorMask && !valid){
29042             Roo.form.BasicForm.popover.mask(this, target);
29043         }
29044         
29045         return valid;
29046     },
29047     /**
29048      * Returns array of invalid form fields.
29049      * @return Array
29050      */
29051     
29052     invalidFields : function()
29053     {
29054         var ret = [];
29055         this.items.each(function(f){
29056             if(f.validate()){
29057                 return;
29058             }
29059             ret.push(f);
29060             
29061         });
29062         
29063         return ret;
29064     },
29065     
29066     
29067     /**
29068      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29069      * @return Boolean
29070      */
29071     isDirty : function(){
29072         var dirty = false;
29073         this.items.each(function(f){
29074            if(f.isDirty()){
29075                dirty = true;
29076                return false;
29077            }
29078         });
29079         return dirty;
29080     },
29081     
29082     /**
29083      * Returns true if any fields in this form have changed since their original load. (New version)
29084      * @return Boolean
29085      */
29086     
29087     hasChanged : function()
29088     {
29089         var dirty = false;
29090         this.items.each(function(f){
29091            if(f.hasChanged()){
29092                dirty = true;
29093                return false;
29094            }
29095         });
29096         return dirty;
29097         
29098     },
29099     /**
29100      * Resets all hasChanged to 'false' -
29101      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29102      * So hasChanged storage is only to be used for this purpose
29103      * @return Boolean
29104      */
29105     resetHasChanged : function()
29106     {
29107         this.items.each(function(f){
29108            f.resetHasChanged();
29109         });
29110         
29111     },
29112     
29113     
29114     /**
29115      * Performs a predefined action (submit or load) or custom actions you define on this form.
29116      * @param {String} actionName The name of the action type
29117      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29118      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29119      * accept other config options):
29120      * <pre>
29121 Property          Type             Description
29122 ----------------  ---------------  ----------------------------------------------------------------------------------
29123 url               String           The url for the action (defaults to the form's url)
29124 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29125 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29126 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29127                                    validate the form on the client (defaults to false)
29128      * </pre>
29129      * @return {BasicForm} this
29130      */
29131     doAction : function(action, options){
29132         if(typeof action == 'string'){
29133             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29134         }
29135         if(this.fireEvent('beforeaction', this, action) !== false){
29136             this.beforeAction(action);
29137             action.run.defer(100, action);
29138         }
29139         return this;
29140     },
29141
29142     /**
29143      * Shortcut to do a submit action.
29144      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29145      * @return {BasicForm} this
29146      */
29147     submit : function(options){
29148         this.doAction('submit', options);
29149         return this;
29150     },
29151
29152     /**
29153      * Shortcut to do a load action.
29154      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29155      * @return {BasicForm} this
29156      */
29157     load : function(options){
29158         this.doAction('load', options);
29159         return this;
29160     },
29161
29162     /**
29163      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29164      * @param {Record} record The record to edit
29165      * @return {BasicForm} this
29166      */
29167     updateRecord : function(record){
29168         record.beginEdit();
29169         var fs = record.fields;
29170         fs.each(function(f){
29171             var field = this.findField(f.name);
29172             if(field){
29173                 record.set(f.name, field.getValue());
29174             }
29175         }, this);
29176         record.endEdit();
29177         return this;
29178     },
29179
29180     /**
29181      * Loads an Roo.data.Record into this form.
29182      * @param {Record} record The record to load
29183      * @return {BasicForm} this
29184      */
29185     loadRecord : function(record){
29186         this.setValues(record.data);
29187         return this;
29188     },
29189
29190     // private
29191     beforeAction : function(action){
29192         var o = action.options;
29193         
29194         if(!this.disableMask) {
29195             if(this.waitMsgTarget === true){
29196                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29197             }else if(this.waitMsgTarget){
29198                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29199                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29200             }else {
29201                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29202             }
29203         }
29204         
29205          
29206     },
29207
29208     // private
29209     afterAction : function(action, success){
29210         this.activeAction = null;
29211         var o = action.options;
29212         
29213         if(!this.disableMask) {
29214             if(this.waitMsgTarget === true){
29215                 this.el.unmask();
29216             }else if(this.waitMsgTarget){
29217                 this.waitMsgTarget.unmask();
29218             }else{
29219                 Roo.MessageBox.updateProgress(1);
29220                 Roo.MessageBox.hide();
29221             }
29222         }
29223         
29224         if(success){
29225             if(o.reset){
29226                 this.reset();
29227             }
29228             Roo.callback(o.success, o.scope, [this, action]);
29229             this.fireEvent('actioncomplete', this, action);
29230             
29231         }else{
29232             
29233             // failure condition..
29234             // we have a scenario where updates need confirming.
29235             // eg. if a locking scenario exists..
29236             // we look for { errors : { needs_confirm : true }} in the response.
29237             if (
29238                 (typeof(action.result) != 'undefined')  &&
29239                 (typeof(action.result.errors) != 'undefined')  &&
29240                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29241            ){
29242                 var _t = this;
29243                 Roo.MessageBox.confirm(
29244                     "Change requires confirmation",
29245                     action.result.errorMsg,
29246                     function(r) {
29247                         if (r != 'yes') {
29248                             return;
29249                         }
29250                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29251                     }
29252                     
29253                 );
29254                 
29255                 
29256                 
29257                 return;
29258             }
29259             
29260             Roo.callback(o.failure, o.scope, [this, action]);
29261             // show an error message if no failed handler is set..
29262             if (!this.hasListener('actionfailed')) {
29263                 Roo.MessageBox.alert("Error",
29264                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29265                         action.result.errorMsg :
29266                         "Saving Failed, please check your entries or try again"
29267                 );
29268             }
29269             
29270             this.fireEvent('actionfailed', this, action);
29271         }
29272         
29273     },
29274
29275     /**
29276      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29277      * @param {String} id The value to search for
29278      * @return Field
29279      */
29280     findField : function(id){
29281         var field = this.items.get(id);
29282         if(!field){
29283             this.items.each(function(f){
29284                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29285                     field = f;
29286                     return false;
29287                 }
29288             });
29289         }
29290         return field || null;
29291     },
29292
29293     /**
29294      * Add a secondary form to this one, 
29295      * Used to provide tabbed forms. One form is primary, with hidden values 
29296      * which mirror the elements from the other forms.
29297      * 
29298      * @param {Roo.form.Form} form to add.
29299      * 
29300      */
29301     addForm : function(form)
29302     {
29303        
29304         if (this.childForms.indexOf(form) > -1) {
29305             // already added..
29306             return;
29307         }
29308         this.childForms.push(form);
29309         var n = '';
29310         Roo.each(form.allItems, function (fe) {
29311             
29312             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29313             if (this.findField(n)) { // already added..
29314                 return;
29315             }
29316             var add = new Roo.form.Hidden({
29317                 name : n
29318             });
29319             add.render(this.el);
29320             
29321             this.add( add );
29322         }, this);
29323         
29324     },
29325     /**
29326      * Mark fields in this form invalid in bulk.
29327      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29328      * @return {BasicForm} this
29329      */
29330     markInvalid : function(errors){
29331         if(errors instanceof Array){
29332             for(var i = 0, len = errors.length; i < len; i++){
29333                 var fieldError = errors[i];
29334                 var f = this.findField(fieldError.id);
29335                 if(f){
29336                     f.markInvalid(fieldError.msg);
29337                 }
29338             }
29339         }else{
29340             var field, id;
29341             for(id in errors){
29342                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29343                     field.markInvalid(errors[id]);
29344                 }
29345             }
29346         }
29347         Roo.each(this.childForms || [], function (f) {
29348             f.markInvalid(errors);
29349         });
29350         
29351         return this;
29352     },
29353
29354     /**
29355      * Set values for fields in this form in bulk.
29356      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29357      * @return {BasicForm} this
29358      */
29359     setValues : function(values){
29360         if(values instanceof Array){ // array of objects
29361             for(var i = 0, len = values.length; i < len; i++){
29362                 var v = values[i];
29363                 var f = this.findField(v.id);
29364                 if(f){
29365                     f.setValue(v.value);
29366                     if(this.trackResetOnLoad){
29367                         f.originalValue = f.getValue();
29368                     }
29369                 }
29370             }
29371         }else{ // object hash
29372             var field, id;
29373             for(id in values){
29374                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29375                     
29376                     if (field.setFromData && 
29377                         field.valueField && 
29378                         field.displayField &&
29379                         // combos' with local stores can 
29380                         // be queried via setValue()
29381                         // to set their value..
29382                         (field.store && !field.store.isLocal)
29383                         ) {
29384                         // it's a combo
29385                         var sd = { };
29386                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29387                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29388                         field.setFromData(sd);
29389                         
29390                     } else {
29391                         field.setValue(values[id]);
29392                     }
29393                     
29394                     
29395                     if(this.trackResetOnLoad){
29396                         field.originalValue = field.getValue();
29397                     }
29398                 }
29399             }
29400         }
29401         this.resetHasChanged();
29402         
29403         
29404         Roo.each(this.childForms || [], function (f) {
29405             f.setValues(values);
29406             f.resetHasChanged();
29407         });
29408                 
29409         return this;
29410     },
29411  
29412     /**
29413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29414      * they are returned as an array.
29415      * @param {Boolean} asString
29416      * @return {Object}
29417      */
29418     getValues : function(asString)
29419     {
29420         if (this.childForms) {
29421             // copy values from the child forms
29422             Roo.each(this.childForms, function (f) {
29423                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29424             }, this);
29425         }
29426         
29427         // use formdata
29428         if (typeof(FormData) != 'undefined' && asString !== true) {
29429             // this relies on a 'recent' version of chrome apparently...
29430             try {
29431                 var fd = (new FormData(this.el.dom)).entries();
29432                 var ret = {};
29433                 var ent = fd.next();
29434                 while (!ent.done) {
29435                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29436                     ent = fd.next();
29437                 };
29438                 return ret;
29439             } catch(e) {
29440                 
29441             }
29442             
29443         }
29444         
29445         
29446         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29447         if(asString === true){
29448             return fs;
29449         }
29450         return Roo.urlDecode(fs);
29451     },
29452     
29453     /**
29454      * Returns the fields in this form as an object with key/value pairs. 
29455      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29456      * Normally this will not return readOnly data 
29457      * @param {Boolean} with_readonly return readonly field data.
29458      * @return {Object}
29459      */
29460     getFieldValues : function(with_readonly)
29461     {
29462         if (this.childForms) {
29463             // copy values from the child forms
29464             // should this call getFieldValues - probably not as we do not currently copy
29465             // hidden fields when we generate..
29466             Roo.each(this.childForms, function (f) {
29467                 this.setValues(f.getFieldValues());
29468             }, this);
29469         }
29470         
29471         var ret = {};
29472         this.items.each(function(f){
29473             
29474             if (f.readOnly && with_readonly !== true) {
29475                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29476                         // if a subform contains a copy of them.
29477                         // if you have subforms with the same editable data, you will need to copy the data back
29478                         // and forth.
29479             }
29480             
29481             if (!f.getName()) {
29482                 return;
29483             }
29484             var v = f.getValue();
29485             if (f.inputType =='radio') {
29486                 if (typeof(ret[f.getName()]) == 'undefined') {
29487                     ret[f.getName()] = ''; // empty..
29488                 }
29489                 
29490                 if (!f.el.dom.checked) {
29491                     return;
29492                     
29493                 }
29494                 v = f.el.dom.value;
29495                 
29496             }
29497             
29498             // not sure if this supported any more..
29499             if ((typeof(v) == 'object') && f.getRawValue) {
29500                 v = f.getRawValue() ; // dates..
29501             }
29502             // combo boxes where name != hiddenName...
29503             if (f.name != f.getName()) {
29504                 ret[f.name] = f.getRawValue();
29505             }
29506             ret[f.getName()] = v;
29507         });
29508         
29509         return ret;
29510     },
29511
29512     /**
29513      * Clears all invalid messages in this form.
29514      * @return {BasicForm} this
29515      */
29516     clearInvalid : function(){
29517         this.items.each(function(f){
29518            f.clearInvalid();
29519         });
29520         
29521         Roo.each(this.childForms || [], function (f) {
29522             f.clearInvalid();
29523         });
29524         
29525         
29526         return this;
29527     },
29528
29529     /**
29530      * Resets this form.
29531      * @return {BasicForm} this
29532      */
29533     reset : function(){
29534         this.items.each(function(f){
29535             f.reset();
29536         });
29537         
29538         Roo.each(this.childForms || [], function (f) {
29539             f.reset();
29540         });
29541         this.resetHasChanged();
29542         
29543         return this;
29544     },
29545
29546     /**
29547      * Add Roo.form components to this form.
29548      * @param {Field} field1
29549      * @param {Field} field2 (optional)
29550      * @param {Field} etc (optional)
29551      * @return {BasicForm} this
29552      */
29553     add : function(){
29554         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29555         return this;
29556     },
29557
29558
29559     /**
29560      * Removes a field from the items collection (does NOT remove its markup).
29561      * @param {Field} field
29562      * @return {BasicForm} this
29563      */
29564     remove : function(field){
29565         this.items.remove(field);
29566         return this;
29567     },
29568
29569     /**
29570      * Looks at the fields in this form, checks them for an id attribute,
29571      * and calls applyTo on the existing dom element with that id.
29572      * @return {BasicForm} this
29573      */
29574     render : function(){
29575         this.items.each(function(f){
29576             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29577                 f.applyTo(f.id);
29578             }
29579         });
29580         return this;
29581     },
29582
29583     /**
29584      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29585      * @param {Object} values
29586      * @return {BasicForm} this
29587      */
29588     applyToFields : function(o){
29589         this.items.each(function(f){
29590            Roo.apply(f, o);
29591         });
29592         return this;
29593     },
29594
29595     /**
29596      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29597      * @param {Object} values
29598      * @return {BasicForm} this
29599      */
29600     applyIfToFields : function(o){
29601         this.items.each(function(f){
29602            Roo.applyIf(f, o);
29603         });
29604         return this;
29605     }
29606 });
29607
29608 // back compat
29609 Roo.BasicForm = Roo.form.BasicForm;
29610
29611 Roo.apply(Roo.form.BasicForm, {
29612     
29613     popover : {
29614         
29615         padding : 5,
29616         
29617         isApplied : false,
29618         
29619         isMasked : false,
29620         
29621         form : false,
29622         
29623         target : false,
29624         
29625         intervalID : false,
29626         
29627         maskEl : false,
29628         
29629         apply : function()
29630         {
29631             if(this.isApplied){
29632                 return;
29633             }
29634             
29635             this.maskEl = {
29636                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29637                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29638                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29639                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29640             };
29641             
29642             this.maskEl.top.enableDisplayMode("block");
29643             this.maskEl.left.enableDisplayMode("block");
29644             this.maskEl.bottom.enableDisplayMode("block");
29645             this.maskEl.right.enableDisplayMode("block");
29646             
29647             Roo.get(document.body).on('click', function(){
29648                 this.unmask();
29649             }, this);
29650             
29651             Roo.get(document.body).on('touchstart', function(){
29652                 this.unmask();
29653             }, this);
29654             
29655             this.isApplied = true
29656         },
29657         
29658         mask : function(form, target)
29659         {
29660             this.form = form;
29661             
29662             this.target = target;
29663             
29664             if(!this.form.errorMask || !target.el){
29665                 return;
29666             }
29667             
29668             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29669             
29670             var ot = this.target.el.calcOffsetsTo(scrollable);
29671             
29672             var scrollTo = ot[1] - this.form.maskOffset;
29673             
29674             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29675             
29676             scrollable.scrollTo('top', scrollTo);
29677             
29678             var el = this.target.wrap || this.target.el;
29679             
29680             var box = el.getBox();
29681             
29682             this.maskEl.top.setStyle('position', 'absolute');
29683             this.maskEl.top.setStyle('z-index', 10000);
29684             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29685             this.maskEl.top.setLeft(0);
29686             this.maskEl.top.setTop(0);
29687             this.maskEl.top.show();
29688             
29689             this.maskEl.left.setStyle('position', 'absolute');
29690             this.maskEl.left.setStyle('z-index', 10000);
29691             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29692             this.maskEl.left.setLeft(0);
29693             this.maskEl.left.setTop(box.y - this.padding);
29694             this.maskEl.left.show();
29695
29696             this.maskEl.bottom.setStyle('position', 'absolute');
29697             this.maskEl.bottom.setStyle('z-index', 10000);
29698             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29699             this.maskEl.bottom.setLeft(0);
29700             this.maskEl.bottom.setTop(box.bottom + this.padding);
29701             this.maskEl.bottom.show();
29702
29703             this.maskEl.right.setStyle('position', 'absolute');
29704             this.maskEl.right.setStyle('z-index', 10000);
29705             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29706             this.maskEl.right.setLeft(box.right + this.padding);
29707             this.maskEl.right.setTop(box.y - this.padding);
29708             this.maskEl.right.show();
29709
29710             this.intervalID = window.setInterval(function() {
29711                 Roo.form.BasicForm.popover.unmask();
29712             }, 10000);
29713
29714             window.onwheel = function(){ return false;};
29715             
29716             (function(){ this.isMasked = true; }).defer(500, this);
29717             
29718         },
29719         
29720         unmask : function()
29721         {
29722             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29723                 return;
29724             }
29725             
29726             this.maskEl.top.setStyle('position', 'absolute');
29727             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29728             this.maskEl.top.hide();
29729
29730             this.maskEl.left.setStyle('position', 'absolute');
29731             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29732             this.maskEl.left.hide();
29733
29734             this.maskEl.bottom.setStyle('position', 'absolute');
29735             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29736             this.maskEl.bottom.hide();
29737
29738             this.maskEl.right.setStyle('position', 'absolute');
29739             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29740             this.maskEl.right.hide();
29741             
29742             window.onwheel = function(){ return true;};
29743             
29744             if(this.intervalID){
29745                 window.clearInterval(this.intervalID);
29746                 this.intervalID = false;
29747             }
29748             
29749             this.isMasked = false;
29750             
29751         }
29752         
29753     }
29754     
29755 });/*
29756  * Based on:
29757  * Ext JS Library 1.1.1
29758  * Copyright(c) 2006-2007, Ext JS, LLC.
29759  *
29760  * Originally Released Under LGPL - original licence link has changed is not relivant.
29761  *
29762  * Fork - LGPL
29763  * <script type="text/javascript">
29764  */
29765
29766 /**
29767  * @class Roo.form.Form
29768  * @extends Roo.form.BasicForm
29769  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29770  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29771  * @constructor
29772  * @param {Object} config Configuration options
29773  */
29774 Roo.form.Form = function(config){
29775     var xitems =  [];
29776     if (config.items) {
29777         xitems = config.items;
29778         delete config.items;
29779     }
29780    
29781     
29782     Roo.form.Form.superclass.constructor.call(this, null, config);
29783     this.url = this.url || this.action;
29784     if(!this.root){
29785         this.root = new Roo.form.Layout(Roo.applyIf({
29786             id: Roo.id()
29787         }, config));
29788     }
29789     this.active = this.root;
29790     /**
29791      * Array of all the buttons that have been added to this form via {@link addButton}
29792      * @type Array
29793      */
29794     this.buttons = [];
29795     this.allItems = [];
29796     this.addEvents({
29797         /**
29798          * @event clientvalidation
29799          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29800          * @param {Form} this
29801          * @param {Boolean} valid true if the form has passed client-side validation
29802          */
29803         clientvalidation: true,
29804         /**
29805          * @event rendered
29806          * Fires when the form is rendered
29807          * @param {Roo.form.Form} form
29808          */
29809         rendered : true
29810     });
29811     
29812     if (this.progressUrl) {
29813             // push a hidden field onto the list of fields..
29814             this.addxtype( {
29815                     xns: Roo.form, 
29816                     xtype : 'Hidden', 
29817                     name : 'UPLOAD_IDENTIFIER' 
29818             });
29819         }
29820         
29821     
29822     Roo.each(xitems, this.addxtype, this);
29823     
29824 };
29825
29826 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29827      /**
29828      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29829      */
29830     
29831     /**
29832      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29833      */
29834     /**
29835      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29836      */
29837     /**
29838      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29839      */
29840     buttonAlign:'center',
29841
29842     /**
29843      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29844      */
29845     minButtonWidth:75,
29846
29847     /**
29848      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29849      * This property cascades to child containers if not set.
29850      */
29851     labelAlign:'left',
29852
29853     /**
29854      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29855      * fires a looping event with that state. This is required to bind buttons to the valid
29856      * state using the config value formBind:true on the button.
29857      */
29858     monitorValid : false,
29859
29860     /**
29861      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29862      */
29863     monitorPoll : 200,
29864     
29865     /**
29866      * @cfg {String} progressUrl - Url to return progress data 
29867      */
29868     
29869     progressUrl : false,
29870     /**
29871      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29872      * sending a formdata with extra parameters - eg uploaded elements.
29873      */
29874     
29875     formData : false,
29876     
29877     /**
29878      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29879      * fields are added and the column is closed. If no fields are passed the column remains open
29880      * until end() is called.
29881      * @param {Object} config The config to pass to the column
29882      * @param {Field} field1 (optional)
29883      * @param {Field} field2 (optional)
29884      * @param {Field} etc (optional)
29885      * @return Column The column container object
29886      */
29887     column : function(c){
29888         var col = new Roo.form.Column(c);
29889         this.start(col);
29890         if(arguments.length > 1){ // duplicate code required because of Opera
29891             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29892             this.end();
29893         }
29894         return col;
29895     },
29896
29897     /**
29898      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29899      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29900      * until end() is called.
29901      * @param {Object} config The config to pass to the fieldset
29902      * @param {Field} field1 (optional)
29903      * @param {Field} field2 (optional)
29904      * @param {Field} etc (optional)
29905      * @return FieldSet The fieldset container object
29906      */
29907     fieldset : function(c){
29908         var fs = new Roo.form.FieldSet(c);
29909         this.start(fs);
29910         if(arguments.length > 1){ // duplicate code required because of Opera
29911             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29912             this.end();
29913         }
29914         return fs;
29915     },
29916
29917     /**
29918      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29919      * fields are added and the container is closed. If no fields are passed the container remains open
29920      * until end() is called.
29921      * @param {Object} config The config to pass to the Layout
29922      * @param {Field} field1 (optional)
29923      * @param {Field} field2 (optional)
29924      * @param {Field} etc (optional)
29925      * @return Layout The container object
29926      */
29927     container : function(c){
29928         var l = new Roo.form.Layout(c);
29929         this.start(l);
29930         if(arguments.length > 1){ // duplicate code required because of Opera
29931             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29932             this.end();
29933         }
29934         return l;
29935     },
29936
29937     /**
29938      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29939      * @param {Object} container A Roo.form.Layout or subclass of Layout
29940      * @return {Form} this
29941      */
29942     start : function(c){
29943         // cascade label info
29944         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29945         this.active.stack.push(c);
29946         c.ownerCt = this.active;
29947         this.active = c;
29948         return this;
29949     },
29950
29951     /**
29952      * Closes the current open container
29953      * @return {Form} this
29954      */
29955     end : function(){
29956         if(this.active == this.root){
29957             return this;
29958         }
29959         this.active = this.active.ownerCt;
29960         return this;
29961     },
29962
29963     /**
29964      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29965      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29966      * as the label of the field.
29967      * @param {Field} field1
29968      * @param {Field} field2 (optional)
29969      * @param {Field} etc. (optional)
29970      * @return {Form} this
29971      */
29972     add : function(){
29973         this.active.stack.push.apply(this.active.stack, arguments);
29974         this.allItems.push.apply(this.allItems,arguments);
29975         var r = [];
29976         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29977             if(a[i].isFormField){
29978                 r.push(a[i]);
29979             }
29980         }
29981         if(r.length > 0){
29982             Roo.form.Form.superclass.add.apply(this, r);
29983         }
29984         return this;
29985     },
29986     
29987
29988     
29989     
29990     
29991      /**
29992      * Find any element that has been added to a form, using it's ID or name
29993      * This can include framesets, columns etc. along with regular fields..
29994      * @param {String} id - id or name to find.
29995      
29996      * @return {Element} e - or false if nothing found.
29997      */
29998     findbyId : function(id)
29999     {
30000         var ret = false;
30001         if (!id) {
30002             return ret;
30003         }
30004         Roo.each(this.allItems, function(f){
30005             if (f.id == id || f.name == id ){
30006                 ret = f;
30007                 return false;
30008             }
30009         });
30010         return ret;
30011     },
30012
30013     
30014     
30015     /**
30016      * Render this form into the passed container. This should only be called once!
30017      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30018      * @return {Form} this
30019      */
30020     render : function(ct)
30021     {
30022         
30023         
30024         
30025         ct = Roo.get(ct);
30026         var o = this.autoCreate || {
30027             tag: 'form',
30028             method : this.method || 'POST',
30029             id : this.id || Roo.id()
30030         };
30031         this.initEl(ct.createChild(o));
30032
30033         this.root.render(this.el);
30034         
30035        
30036              
30037         this.items.each(function(f){
30038             f.render('x-form-el-'+f.id);
30039         });
30040
30041         if(this.buttons.length > 0){
30042             // tables are required to maintain order and for correct IE layout
30043             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30044                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30045                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30046             }}, null, true);
30047             var tr = tb.getElementsByTagName('tr')[0];
30048             for(var i = 0, len = this.buttons.length; i < len; i++) {
30049                 var b = this.buttons[i];
30050                 var td = document.createElement('td');
30051                 td.className = 'x-form-btn-td';
30052                 b.render(tr.appendChild(td));
30053             }
30054         }
30055         if(this.monitorValid){ // initialize after render
30056             this.startMonitoring();
30057         }
30058         this.fireEvent('rendered', this);
30059         return this;
30060     },
30061
30062     /**
30063      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30064      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30065      * object or a valid Roo.DomHelper element config
30066      * @param {Function} handler The function called when the button is clicked
30067      * @param {Object} scope (optional) The scope of the handler function
30068      * @return {Roo.Button}
30069      */
30070     addButton : function(config, handler, scope){
30071         var bc = {
30072             handler: handler,
30073             scope: scope,
30074             minWidth: this.minButtonWidth,
30075             hideParent:true
30076         };
30077         if(typeof config == "string"){
30078             bc.text = config;
30079         }else{
30080             Roo.apply(bc, config);
30081         }
30082         var btn = new Roo.Button(null, bc);
30083         this.buttons.push(btn);
30084         return btn;
30085     },
30086
30087      /**
30088      * Adds a series of form elements (using the xtype property as the factory method.
30089      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30090      * @param {Object} config 
30091      */
30092     
30093     addxtype : function()
30094     {
30095         var ar = Array.prototype.slice.call(arguments, 0);
30096         var ret = false;
30097         for(var i = 0; i < ar.length; i++) {
30098             if (!ar[i]) {
30099                 continue; // skip -- if this happends something invalid got sent, we 
30100                 // should ignore it, as basically that interface element will not show up
30101                 // and that should be pretty obvious!!
30102             }
30103             
30104             if (Roo.form[ar[i].xtype]) {
30105                 ar[i].form = this;
30106                 var fe = Roo.factory(ar[i], Roo.form);
30107                 if (!ret) {
30108                     ret = fe;
30109                 }
30110                 fe.form = this;
30111                 if (fe.store) {
30112                     fe.store.form = this;
30113                 }
30114                 if (fe.isLayout) {  
30115                          
30116                     this.start(fe);
30117                     this.allItems.push(fe);
30118                     if (fe.items && fe.addxtype) {
30119                         fe.addxtype.apply(fe, fe.items);
30120                         delete fe.items;
30121                     }
30122                      this.end();
30123                     continue;
30124                 }
30125                 
30126                 
30127                  
30128                 this.add(fe);
30129               //  console.log('adding ' + ar[i].xtype);
30130             }
30131             if (ar[i].xtype == 'Button') {  
30132                 //console.log('adding button');
30133                 //console.log(ar[i]);
30134                 this.addButton(ar[i]);
30135                 this.allItems.push(fe);
30136                 continue;
30137             }
30138             
30139             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30140                 alert('end is not supported on xtype any more, use items');
30141             //    this.end();
30142             //    //console.log('adding end');
30143             }
30144             
30145         }
30146         return ret;
30147     },
30148     
30149     /**
30150      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30151      * option "monitorValid"
30152      */
30153     startMonitoring : function(){
30154         if(!this.bound){
30155             this.bound = true;
30156             Roo.TaskMgr.start({
30157                 run : this.bindHandler,
30158                 interval : this.monitorPoll || 200,
30159                 scope: this
30160             });
30161         }
30162     },
30163
30164     /**
30165      * Stops monitoring of the valid state of this form
30166      */
30167     stopMonitoring : function(){
30168         this.bound = false;
30169     },
30170
30171     // private
30172     bindHandler : function(){
30173         if(!this.bound){
30174             return false; // stops binding
30175         }
30176         var valid = true;
30177         this.items.each(function(f){
30178             if(!f.isValid(true)){
30179                 valid = false;
30180                 return false;
30181             }
30182         });
30183         for(var i = 0, len = this.buttons.length; i < len; i++){
30184             var btn = this.buttons[i];
30185             if(btn.formBind === true && btn.disabled === valid){
30186                 btn.setDisabled(!valid);
30187             }
30188         }
30189         this.fireEvent('clientvalidation', this, valid);
30190     }
30191     
30192     
30193     
30194     
30195     
30196     
30197     
30198     
30199 });
30200
30201
30202 // back compat
30203 Roo.Form = Roo.form.Form;
30204 /*
30205  * Based on:
30206  * Ext JS Library 1.1.1
30207  * Copyright(c) 2006-2007, Ext JS, LLC.
30208  *
30209  * Originally Released Under LGPL - original licence link has changed is not relivant.
30210  *
30211  * Fork - LGPL
30212  * <script type="text/javascript">
30213  */
30214
30215 // as we use this in bootstrap.
30216 Roo.namespace('Roo.form');
30217  /**
30218  * @class Roo.form.Action
30219  * Internal Class used to handle form actions
30220  * @constructor
30221  * @param {Roo.form.BasicForm} el The form element or its id
30222  * @param {Object} config Configuration options
30223  */
30224
30225  
30226  
30227 // define the action interface
30228 Roo.form.Action = function(form, options){
30229     this.form = form;
30230     this.options = options || {};
30231 };
30232 /**
30233  * Client Validation Failed
30234  * @const 
30235  */
30236 Roo.form.Action.CLIENT_INVALID = 'client';
30237 /**
30238  * Server Validation Failed
30239  * @const 
30240  */
30241 Roo.form.Action.SERVER_INVALID = 'server';
30242  /**
30243  * Connect to Server Failed
30244  * @const 
30245  */
30246 Roo.form.Action.CONNECT_FAILURE = 'connect';
30247 /**
30248  * Reading Data from Server Failed
30249  * @const 
30250  */
30251 Roo.form.Action.LOAD_FAILURE = 'load';
30252
30253 Roo.form.Action.prototype = {
30254     type : 'default',
30255     failureType : undefined,
30256     response : undefined,
30257     result : undefined,
30258
30259     // interface method
30260     run : function(options){
30261
30262     },
30263
30264     // interface method
30265     success : function(response){
30266
30267     },
30268
30269     // interface method
30270     handleResponse : function(response){
30271
30272     },
30273
30274     // default connection failure
30275     failure : function(response){
30276         
30277         this.response = response;
30278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30279         this.form.afterAction(this, false);
30280     },
30281
30282     processResponse : function(response){
30283         this.response = response;
30284         if(!response.responseText){
30285             return true;
30286         }
30287         this.result = this.handleResponse(response);
30288         return this.result;
30289     },
30290
30291     // utility functions used internally
30292     getUrl : function(appendParams){
30293         var url = this.options.url || this.form.url || this.form.el.dom.action;
30294         if(appendParams){
30295             var p = this.getParams();
30296             if(p){
30297                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30298             }
30299         }
30300         return url;
30301     },
30302
30303     getMethod : function(){
30304         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30305     },
30306
30307     getParams : function(){
30308         var bp = this.form.baseParams;
30309         var p = this.options.params;
30310         if(p){
30311             if(typeof p == "object"){
30312                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30313             }else if(typeof p == 'string' && bp){
30314                 p += '&' + Roo.urlEncode(bp);
30315             }
30316         }else if(bp){
30317             p = Roo.urlEncode(bp);
30318         }
30319         return p;
30320     },
30321
30322     createCallback : function(){
30323         return {
30324             success: this.success,
30325             failure: this.failure,
30326             scope: this,
30327             timeout: (this.form.timeout*1000),
30328             upload: this.form.fileUpload ? this.success : undefined
30329         };
30330     }
30331 };
30332
30333 Roo.form.Action.Submit = function(form, options){
30334     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30335 };
30336
30337 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30338     type : 'submit',
30339
30340     haveProgress : false,
30341     uploadComplete : false,
30342     
30343     // uploadProgress indicator.
30344     uploadProgress : function()
30345     {
30346         if (!this.form.progressUrl) {
30347             return;
30348         }
30349         
30350         if (!this.haveProgress) {
30351             Roo.MessageBox.progress("Uploading", "Uploading");
30352         }
30353         if (this.uploadComplete) {
30354            Roo.MessageBox.hide();
30355            return;
30356         }
30357         
30358         this.haveProgress = true;
30359    
30360         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30361         
30362         var c = new Roo.data.Connection();
30363         c.request({
30364             url : this.form.progressUrl,
30365             params: {
30366                 id : uid
30367             },
30368             method: 'GET',
30369             success : function(req){
30370                //console.log(data);
30371                 var rdata = false;
30372                 var edata;
30373                 try  {
30374                    rdata = Roo.decode(req.responseText)
30375                 } catch (e) {
30376                     Roo.log("Invalid data from server..");
30377                     Roo.log(edata);
30378                     return;
30379                 }
30380                 if (!rdata || !rdata.success) {
30381                     Roo.log(rdata);
30382                     Roo.MessageBox.alert(Roo.encode(rdata));
30383                     return;
30384                 }
30385                 var data = rdata.data;
30386                 
30387                 if (this.uploadComplete) {
30388                    Roo.MessageBox.hide();
30389                    return;
30390                 }
30391                    
30392                 if (data){
30393                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30394                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30395                     );
30396                 }
30397                 this.uploadProgress.defer(2000,this);
30398             },
30399        
30400             failure: function(data) {
30401                 Roo.log('progress url failed ');
30402                 Roo.log(data);
30403             },
30404             scope : this
30405         });
30406            
30407     },
30408     
30409     
30410     run : function()
30411     {
30412         // run get Values on the form, so it syncs any secondary forms.
30413         this.form.getValues();
30414         
30415         var o = this.options;
30416         var method = this.getMethod();
30417         var isPost = method == 'POST';
30418         if(o.clientValidation === false || this.form.isValid()){
30419             
30420             if (this.form.progressUrl) {
30421                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30422                     (new Date() * 1) + '' + Math.random());
30423                     
30424             } 
30425             
30426             
30427             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30428                 form:this.form.el.dom,
30429                 url:this.getUrl(!isPost),
30430                 method: method,
30431                 params:isPost ? this.getParams() : null,
30432                 isUpload: this.form.fileUpload,
30433                 formData : this.form.formData
30434             }));
30435             
30436             this.uploadProgress();
30437
30438         }else if (o.clientValidation !== false){ // client validation failed
30439             this.failureType = Roo.form.Action.CLIENT_INVALID;
30440             this.form.afterAction(this, false);
30441         }
30442     },
30443
30444     success : function(response)
30445     {
30446         this.uploadComplete= true;
30447         if (this.haveProgress) {
30448             Roo.MessageBox.hide();
30449         }
30450         
30451         
30452         var result = this.processResponse(response);
30453         if(result === true || result.success){
30454             this.form.afterAction(this, true);
30455             return;
30456         }
30457         if(result.errors){
30458             this.form.markInvalid(result.errors);
30459             this.failureType = Roo.form.Action.SERVER_INVALID;
30460         }
30461         this.form.afterAction(this, false);
30462     },
30463     failure : function(response)
30464     {
30465         this.uploadComplete= true;
30466         if (this.haveProgress) {
30467             Roo.MessageBox.hide();
30468         }
30469         
30470         this.response = response;
30471         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30472         this.form.afterAction(this, false);
30473     },
30474     
30475     handleResponse : function(response){
30476         if(this.form.errorReader){
30477             var rs = this.form.errorReader.read(response);
30478             var errors = [];
30479             if(rs.records){
30480                 for(var i = 0, len = rs.records.length; i < len; i++) {
30481                     var r = rs.records[i];
30482                     errors[i] = r.data;
30483                 }
30484             }
30485             if(errors.length < 1){
30486                 errors = null;
30487             }
30488             return {
30489                 success : rs.success,
30490                 errors : errors
30491             };
30492         }
30493         var ret = false;
30494         try {
30495             ret = Roo.decode(response.responseText);
30496         } catch (e) {
30497             ret = {
30498                 success: false,
30499                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30500                 errors : []
30501             };
30502         }
30503         return ret;
30504         
30505     }
30506 });
30507
30508
30509 Roo.form.Action.Load = function(form, options){
30510     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30511     this.reader = this.form.reader;
30512 };
30513
30514 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30515     type : 'load',
30516
30517     run : function(){
30518         
30519         Roo.Ajax.request(Roo.apply(
30520                 this.createCallback(), {
30521                     method:this.getMethod(),
30522                     url:this.getUrl(false),
30523                     params:this.getParams()
30524         }));
30525     },
30526
30527     success : function(response){
30528         
30529         var result = this.processResponse(response);
30530         if(result === true || !result.success || !result.data){
30531             this.failureType = Roo.form.Action.LOAD_FAILURE;
30532             this.form.afterAction(this, false);
30533             return;
30534         }
30535         this.form.clearInvalid();
30536         this.form.setValues(result.data);
30537         this.form.afterAction(this, true);
30538     },
30539
30540     handleResponse : function(response){
30541         if(this.form.reader){
30542             var rs = this.form.reader.read(response);
30543             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30544             return {
30545                 success : rs.success,
30546                 data : data
30547             };
30548         }
30549         return Roo.decode(response.responseText);
30550     }
30551 });
30552
30553 Roo.form.Action.ACTION_TYPES = {
30554     'load' : Roo.form.Action.Load,
30555     'submit' : Roo.form.Action.Submit
30556 };/*
30557  * Based on:
30558  * Ext JS Library 1.1.1
30559  * Copyright(c) 2006-2007, Ext JS, LLC.
30560  *
30561  * Originally Released Under LGPL - original licence link has changed is not relivant.
30562  *
30563  * Fork - LGPL
30564  * <script type="text/javascript">
30565  */
30566  
30567 /**
30568  * @class Roo.form.Layout
30569  * @extends Roo.Component
30570  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30571  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30572  * @constructor
30573  * @param {Object} config Configuration options
30574  */
30575 Roo.form.Layout = function(config){
30576     var xitems = [];
30577     if (config.items) {
30578         xitems = config.items;
30579         delete config.items;
30580     }
30581     Roo.form.Layout.superclass.constructor.call(this, config);
30582     this.stack = [];
30583     Roo.each(xitems, this.addxtype, this);
30584      
30585 };
30586
30587 Roo.extend(Roo.form.Layout, Roo.Component, {
30588     /**
30589      * @cfg {String/Object} autoCreate
30590      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30591      */
30592     /**
30593      * @cfg {String/Object/Function} style
30594      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30595      * a function which returns such a specification.
30596      */
30597     /**
30598      * @cfg {String} labelAlign
30599      * Valid values are "left," "top" and "right" (defaults to "left")
30600      */
30601     /**
30602      * @cfg {Number} labelWidth
30603      * Fixed width in pixels of all field labels (defaults to undefined)
30604      */
30605     /**
30606      * @cfg {Boolean} clear
30607      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30608      */
30609     clear : true,
30610     /**
30611      * @cfg {String} labelSeparator
30612      * The separator to use after field labels (defaults to ':')
30613      */
30614     labelSeparator : ':',
30615     /**
30616      * @cfg {Boolean} hideLabels
30617      * True to suppress the display of field labels in this layout (defaults to false)
30618      */
30619     hideLabels : false,
30620
30621     // private
30622     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30623     
30624     isLayout : true,
30625     
30626     // private
30627     onRender : function(ct, position){
30628         if(this.el){ // from markup
30629             this.el = Roo.get(this.el);
30630         }else {  // generate
30631             var cfg = this.getAutoCreate();
30632             this.el = ct.createChild(cfg, position);
30633         }
30634         if(this.style){
30635             this.el.applyStyles(this.style);
30636         }
30637         if(this.labelAlign){
30638             this.el.addClass('x-form-label-'+this.labelAlign);
30639         }
30640         if(this.hideLabels){
30641             this.labelStyle = "display:none";
30642             this.elementStyle = "padding-left:0;";
30643         }else{
30644             if(typeof this.labelWidth == 'number'){
30645                 this.labelStyle = "width:"+this.labelWidth+"px;";
30646                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30647             }
30648             if(this.labelAlign == 'top'){
30649                 this.labelStyle = "width:auto;";
30650                 this.elementStyle = "padding-left:0;";
30651             }
30652         }
30653         var stack = this.stack;
30654         var slen = stack.length;
30655         if(slen > 0){
30656             if(!this.fieldTpl){
30657                 var t = new Roo.Template(
30658                     '<div class="x-form-item {5}">',
30659                         '<label for="{0}" style="{2}">{1}{4}</label>',
30660                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30661                         '</div>',
30662                     '</div><div class="x-form-clear-left"></div>'
30663                 );
30664                 t.disableFormats = true;
30665                 t.compile();
30666                 Roo.form.Layout.prototype.fieldTpl = t;
30667             }
30668             for(var i = 0; i < slen; i++) {
30669                 if(stack[i].isFormField){
30670                     this.renderField(stack[i]);
30671                 }else{
30672                     this.renderComponent(stack[i]);
30673                 }
30674             }
30675         }
30676         if(this.clear){
30677             this.el.createChild({cls:'x-form-clear'});
30678         }
30679     },
30680
30681     // private
30682     renderField : function(f){
30683         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30684                f.id, //0
30685                f.fieldLabel, //1
30686                f.labelStyle||this.labelStyle||'', //2
30687                this.elementStyle||'', //3
30688                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30689                f.itemCls||this.itemCls||''  //5
30690        ], true).getPrevSibling());
30691     },
30692
30693     // private
30694     renderComponent : function(c){
30695         c.render(c.isLayout ? this.el : this.el.createChild());    
30696     },
30697     /**
30698      * Adds a object form elements (using the xtype property as the factory method.)
30699      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30700      * @param {Object} config 
30701      */
30702     addxtype : function(o)
30703     {
30704         // create the lement.
30705         o.form = this.form;
30706         var fe = Roo.factory(o, Roo.form);
30707         this.form.allItems.push(fe);
30708         this.stack.push(fe);
30709         
30710         if (fe.isFormField) {
30711             this.form.items.add(fe);
30712         }
30713          
30714         return fe;
30715     }
30716 });
30717
30718 /**
30719  * @class Roo.form.Column
30720  * @extends Roo.form.Layout
30721  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30722  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30723  * @constructor
30724  * @param {Object} config Configuration options
30725  */
30726 Roo.form.Column = function(config){
30727     Roo.form.Column.superclass.constructor.call(this, config);
30728 };
30729
30730 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30731     /**
30732      * @cfg {Number/String} width
30733      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30734      */
30735     /**
30736      * @cfg {String/Object} autoCreate
30737      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30738      */
30739
30740     // private
30741     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30742
30743     // private
30744     onRender : function(ct, position){
30745         Roo.form.Column.superclass.onRender.call(this, ct, position);
30746         if(this.width){
30747             this.el.setWidth(this.width);
30748         }
30749     }
30750 });
30751
30752
30753 /**
30754  * @class Roo.form.Row
30755  * @extends Roo.form.Layout
30756  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30757  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30758  * @constructor
30759  * @param {Object} config Configuration options
30760  */
30761
30762  
30763 Roo.form.Row = function(config){
30764     Roo.form.Row.superclass.constructor.call(this, config);
30765 };
30766  
30767 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30768       /**
30769      * @cfg {Number/String} width
30770      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30771      */
30772     /**
30773      * @cfg {Number/String} height
30774      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30775      */
30776     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30777     
30778     padWidth : 20,
30779     // private
30780     onRender : function(ct, position){
30781         //console.log('row render');
30782         if(!this.rowTpl){
30783             var t = new Roo.Template(
30784                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30785                     '<label for="{0}" style="{2}">{1}{4}</label>',
30786                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30787                     '</div>',
30788                 '</div>'
30789             );
30790             t.disableFormats = true;
30791             t.compile();
30792             Roo.form.Layout.prototype.rowTpl = t;
30793         }
30794         this.fieldTpl = this.rowTpl;
30795         
30796         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30797         var labelWidth = 100;
30798         
30799         if ((this.labelAlign != 'top')) {
30800             if (typeof this.labelWidth == 'number') {
30801                 labelWidth = this.labelWidth
30802             }
30803             this.padWidth =  20 + labelWidth;
30804             
30805         }
30806         
30807         Roo.form.Column.superclass.onRender.call(this, ct, position);
30808         if(this.width){
30809             this.el.setWidth(this.width);
30810         }
30811         if(this.height){
30812             this.el.setHeight(this.height);
30813         }
30814     },
30815     
30816     // private
30817     renderField : function(f){
30818         f.fieldEl = this.fieldTpl.append(this.el, [
30819                f.id, f.fieldLabel,
30820                f.labelStyle||this.labelStyle||'',
30821                this.elementStyle||'',
30822                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30823                f.itemCls||this.itemCls||'',
30824                f.width ? f.width + this.padWidth : 160 + this.padWidth
30825        ],true);
30826     }
30827 });
30828  
30829
30830 /**
30831  * @class Roo.form.FieldSet
30832  * @extends Roo.form.Layout
30833  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30834  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30835  * @constructor
30836  * @param {Object} config Configuration options
30837  */
30838 Roo.form.FieldSet = function(config){
30839     Roo.form.FieldSet.superclass.constructor.call(this, config);
30840 };
30841
30842 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30843     /**
30844      * @cfg {String} legend
30845      * The text to display as the legend for the FieldSet (defaults to '')
30846      */
30847     /**
30848      * @cfg {String/Object} autoCreate
30849      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30850      */
30851
30852     // private
30853     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30854
30855     // private
30856     onRender : function(ct, position){
30857         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30858         if(this.legend){
30859             this.setLegend(this.legend);
30860         }
30861     },
30862
30863     // private
30864     setLegend : function(text){
30865         if(this.rendered){
30866             this.el.child('legend').update(text);
30867         }
30868     }
30869 });/*
30870  * Based on:
30871  * Ext JS Library 1.1.1
30872  * Copyright(c) 2006-2007, Ext JS, LLC.
30873  *
30874  * Originally Released Under LGPL - original licence link has changed is not relivant.
30875  *
30876  * Fork - LGPL
30877  * <script type="text/javascript">
30878  */
30879 /**
30880  * @class Roo.form.VTypes
30881  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30882  * @static
30883  */
30884 Roo.form.VTypes = function(){
30885     // closure these in so they are only created once.
30886     var alpha = /^[a-zA-Z_]+$/;
30887     var alphanum = /^[a-zA-Z0-9_]+$/;
30888     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30889     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30890
30891     // All these messages and functions are configurable
30892     return {
30893         /**
30894          * The function used to validate email addresses
30895          * @param {String} value The email address
30896          */
30897         'email' : function(v){
30898             return email.test(v);
30899         },
30900         /**
30901          * The error text to display when the email validation function returns false
30902          * @type String
30903          */
30904         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30905         /**
30906          * The keystroke filter mask to be applied on email input
30907          * @type RegExp
30908          */
30909         'emailMask' : /[a-z0-9_\.\-@]/i,
30910
30911         /**
30912          * The function used to validate URLs
30913          * @param {String} value The URL
30914          */
30915         'url' : function(v){
30916             return url.test(v);
30917         },
30918         /**
30919          * The error text to display when the url validation function returns false
30920          * @type String
30921          */
30922         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30923         
30924         /**
30925          * The function used to validate alpha values
30926          * @param {String} value The value
30927          */
30928         'alpha' : function(v){
30929             return alpha.test(v);
30930         },
30931         /**
30932          * The error text to display when the alpha validation function returns false
30933          * @type String
30934          */
30935         'alphaText' : 'This field should only contain letters and _',
30936         /**
30937          * The keystroke filter mask to be applied on alpha input
30938          * @type RegExp
30939          */
30940         'alphaMask' : /[a-z_]/i,
30941
30942         /**
30943          * The function used to validate alphanumeric values
30944          * @param {String} value The value
30945          */
30946         'alphanum' : function(v){
30947             return alphanum.test(v);
30948         },
30949         /**
30950          * The error text to display when the alphanumeric validation function returns false
30951          * @type String
30952          */
30953         'alphanumText' : 'This field should only contain letters, numbers and _',
30954         /**
30955          * The keystroke filter mask to be applied on alphanumeric input
30956          * @type RegExp
30957          */
30958         'alphanumMask' : /[a-z0-9_]/i
30959     };
30960 }();//<script type="text/javascript">
30961
30962 /**
30963  * @class Roo.form.FCKeditor
30964  * @extends Roo.form.TextArea
30965  * Wrapper around the FCKEditor http://www.fckeditor.net
30966  * @constructor
30967  * Creates a new FCKeditor
30968  * @param {Object} config Configuration options
30969  */
30970 Roo.form.FCKeditor = function(config){
30971     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30972     this.addEvents({
30973          /**
30974          * @event editorinit
30975          * Fired when the editor is initialized - you can add extra handlers here..
30976          * @param {FCKeditor} this
30977          * @param {Object} the FCK object.
30978          */
30979         editorinit : true
30980     });
30981     
30982     
30983 };
30984 Roo.form.FCKeditor.editors = { };
30985 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30986 {
30987     //defaultAutoCreate : {
30988     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30989     //},
30990     // private
30991     /**
30992      * @cfg {Object} fck options - see fck manual for details.
30993      */
30994     fckconfig : false,
30995     
30996     /**
30997      * @cfg {Object} fck toolbar set (Basic or Default)
30998      */
30999     toolbarSet : 'Basic',
31000     /**
31001      * @cfg {Object} fck BasePath
31002      */ 
31003     basePath : '/fckeditor/',
31004     
31005     
31006     frame : false,
31007     
31008     value : '',
31009     
31010    
31011     onRender : function(ct, position)
31012     {
31013         if(!this.el){
31014             this.defaultAutoCreate = {
31015                 tag: "textarea",
31016                 style:"width:300px;height:60px;",
31017                 autocomplete: "new-password"
31018             };
31019         }
31020         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31021         /*
31022         if(this.grow){
31023             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31024             if(this.preventScrollbars){
31025                 this.el.setStyle("overflow", "hidden");
31026             }
31027             this.el.setHeight(this.growMin);
31028         }
31029         */
31030         //console.log('onrender' + this.getId() );
31031         Roo.form.FCKeditor.editors[this.getId()] = this;
31032          
31033
31034         this.replaceTextarea() ;
31035         
31036     },
31037     
31038     getEditor : function() {
31039         return this.fckEditor;
31040     },
31041     /**
31042      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31043      * @param {Mixed} value The value to set
31044      */
31045     
31046     
31047     setValue : function(value)
31048     {
31049         //console.log('setValue: ' + value);
31050         
31051         if(typeof(value) == 'undefined') { // not sure why this is happending...
31052             return;
31053         }
31054         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31055         
31056         //if(!this.el || !this.getEditor()) {
31057         //    this.value = value;
31058             //this.setValue.defer(100,this,[value]);    
31059         //    return;
31060         //} 
31061         
31062         if(!this.getEditor()) {
31063             return;
31064         }
31065         
31066         this.getEditor().SetData(value);
31067         
31068         //
31069
31070     },
31071
31072     /**
31073      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31074      * @return {Mixed} value The field value
31075      */
31076     getValue : function()
31077     {
31078         
31079         if (this.frame && this.frame.dom.style.display == 'none') {
31080             return Roo.form.FCKeditor.superclass.getValue.call(this);
31081         }
31082         
31083         if(!this.el || !this.getEditor()) {
31084            
31085            // this.getValue.defer(100,this); 
31086             return this.value;
31087         }
31088        
31089         
31090         var value=this.getEditor().GetData();
31091         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31092         return Roo.form.FCKeditor.superclass.getValue.call(this);
31093         
31094
31095     },
31096
31097     /**
31098      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31099      * @return {Mixed} value The field value
31100      */
31101     getRawValue : function()
31102     {
31103         if (this.frame && this.frame.dom.style.display == 'none') {
31104             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31105         }
31106         
31107         if(!this.el || !this.getEditor()) {
31108             //this.getRawValue.defer(100,this); 
31109             return this.value;
31110             return;
31111         }
31112         
31113         
31114         
31115         var value=this.getEditor().GetData();
31116         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31117         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31118          
31119     },
31120     
31121     setSize : function(w,h) {
31122         
31123         
31124         
31125         //if (this.frame && this.frame.dom.style.display == 'none') {
31126         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31127         //    return;
31128         //}
31129         //if(!this.el || !this.getEditor()) {
31130         //    this.setSize.defer(100,this, [w,h]); 
31131         //    return;
31132         //}
31133         
31134         
31135         
31136         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31137         
31138         this.frame.dom.setAttribute('width', w);
31139         this.frame.dom.setAttribute('height', h);
31140         this.frame.setSize(w,h);
31141         
31142     },
31143     
31144     toggleSourceEdit : function(value) {
31145         
31146       
31147          
31148         this.el.dom.style.display = value ? '' : 'none';
31149         this.frame.dom.style.display = value ?  'none' : '';
31150         
31151     },
31152     
31153     
31154     focus: function(tag)
31155     {
31156         if (this.frame.dom.style.display == 'none') {
31157             return Roo.form.FCKeditor.superclass.focus.call(this);
31158         }
31159         if(!this.el || !this.getEditor()) {
31160             this.focus.defer(100,this, [tag]); 
31161             return;
31162         }
31163         
31164         
31165         
31166         
31167         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31168         this.getEditor().Focus();
31169         if (tgs.length) {
31170             if (!this.getEditor().Selection.GetSelection()) {
31171                 this.focus.defer(100,this, [tag]); 
31172                 return;
31173             }
31174             
31175             
31176             var r = this.getEditor().EditorDocument.createRange();
31177             r.setStart(tgs[0],0);
31178             r.setEnd(tgs[0],0);
31179             this.getEditor().Selection.GetSelection().removeAllRanges();
31180             this.getEditor().Selection.GetSelection().addRange(r);
31181             this.getEditor().Focus();
31182         }
31183         
31184     },
31185     
31186     
31187     
31188     replaceTextarea : function()
31189     {
31190         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31191             return ;
31192         }
31193         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31194         //{
31195             // We must check the elements firstly using the Id and then the name.
31196         var oTextarea = document.getElementById( this.getId() );
31197         
31198         var colElementsByName = document.getElementsByName( this.getId() ) ;
31199          
31200         oTextarea.style.display = 'none' ;
31201
31202         if ( oTextarea.tabIndex ) {            
31203             this.TabIndex = oTextarea.tabIndex ;
31204         }
31205         
31206         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31207         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31208         this.frame = Roo.get(this.getId() + '___Frame')
31209     },
31210     
31211     _getConfigHtml : function()
31212     {
31213         var sConfig = '' ;
31214
31215         for ( var o in this.fckconfig ) {
31216             sConfig += sConfig.length > 0  ? '&amp;' : '';
31217             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31218         }
31219
31220         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31221     },
31222     
31223     
31224     _getIFrameHtml : function()
31225     {
31226         var sFile = 'fckeditor.html' ;
31227         /* no idea what this is about..
31228         try
31229         {
31230             if ( (/fcksource=true/i).test( window.top.location.search ) )
31231                 sFile = 'fckeditor.original.html' ;
31232         }
31233         catch (e) { 
31234         */
31235
31236         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31237         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31238         
31239         
31240         var html = '<iframe id="' + this.getId() +
31241             '___Frame" src="' + sLink +
31242             '" width="' + this.width +
31243             '" height="' + this.height + '"' +
31244             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31245             ' frameborder="0" scrolling="no"></iframe>' ;
31246
31247         return html ;
31248     },
31249     
31250     _insertHtmlBefore : function( html, element )
31251     {
31252         if ( element.insertAdjacentHTML )       {
31253             // IE
31254             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31255         } else { // Gecko
31256             var oRange = document.createRange() ;
31257             oRange.setStartBefore( element ) ;
31258             var oFragment = oRange.createContextualFragment( html );
31259             element.parentNode.insertBefore( oFragment, element ) ;
31260         }
31261     }
31262     
31263     
31264   
31265     
31266     
31267     
31268     
31269
31270 });
31271
31272 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31273
31274 function FCKeditor_OnComplete(editorInstance){
31275     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31276     f.fckEditor = editorInstance;
31277     //console.log("loaded");
31278     f.fireEvent('editorinit', f, editorInstance);
31279
31280   
31281
31282  
31283
31284
31285
31286
31287
31288
31289
31290
31291
31292
31293
31294
31295
31296
31297
31298 //<script type="text/javascript">
31299 /**
31300  * @class Roo.form.GridField
31301  * @extends Roo.form.Field
31302  * Embed a grid (or editable grid into a form)
31303  * STATUS ALPHA
31304  * 
31305  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31306  * it needs 
31307  * xgrid.store = Roo.data.Store
31308  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31309  * xgrid.store.reader = Roo.data.JsonReader 
31310  * 
31311  * 
31312  * @constructor
31313  * Creates a new GridField
31314  * @param {Object} config Configuration options
31315  */
31316 Roo.form.GridField = function(config){
31317     Roo.form.GridField.superclass.constructor.call(this, config);
31318      
31319 };
31320
31321 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31322     /**
31323      * @cfg {Number} width  - used to restrict width of grid..
31324      */
31325     width : 100,
31326     /**
31327      * @cfg {Number} height - used to restrict height of grid..
31328      */
31329     height : 50,
31330      /**
31331      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31332          * 
31333          *}
31334      */
31335     xgrid : false, 
31336     /**
31337      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31338      * {tag: "input", type: "checkbox", autocomplete: "off"})
31339      */
31340    // defaultAutoCreate : { tag: 'div' },
31341     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31342     /**
31343      * @cfg {String} addTitle Text to include for adding a title.
31344      */
31345     addTitle : false,
31346     //
31347     onResize : function(){
31348         Roo.form.Field.superclass.onResize.apply(this, arguments);
31349     },
31350
31351     initEvents : function(){
31352         // Roo.form.Checkbox.superclass.initEvents.call(this);
31353         // has no events...
31354        
31355     },
31356
31357
31358     getResizeEl : function(){
31359         return this.wrap;
31360     },
31361
31362     getPositionEl : function(){
31363         return this.wrap;
31364     },
31365
31366     // private
31367     onRender : function(ct, position){
31368         
31369         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31370         var style = this.style;
31371         delete this.style;
31372         
31373         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31374         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31375         this.viewEl = this.wrap.createChild({ tag: 'div' });
31376         if (style) {
31377             this.viewEl.applyStyles(style);
31378         }
31379         if (this.width) {
31380             this.viewEl.setWidth(this.width);
31381         }
31382         if (this.height) {
31383             this.viewEl.setHeight(this.height);
31384         }
31385         //if(this.inputValue !== undefined){
31386         //this.setValue(this.value);
31387         
31388         
31389         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31390         
31391         
31392         this.grid.render();
31393         this.grid.getDataSource().on('remove', this.refreshValue, this);
31394         this.grid.getDataSource().on('update', this.refreshValue, this);
31395         this.grid.on('afteredit', this.refreshValue, this);
31396  
31397     },
31398      
31399     
31400     /**
31401      * Sets the value of the item. 
31402      * @param {String} either an object  or a string..
31403      */
31404     setValue : function(v){
31405         //this.value = v;
31406         v = v || []; // empty set..
31407         // this does not seem smart - it really only affects memoryproxy grids..
31408         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31409             var ds = this.grid.getDataSource();
31410             // assumes a json reader..
31411             var data = {}
31412             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31413             ds.loadData( data);
31414         }
31415         // clear selection so it does not get stale.
31416         if (this.grid.sm) { 
31417             this.grid.sm.clearSelections();
31418         }
31419         
31420         Roo.form.GridField.superclass.setValue.call(this, v);
31421         this.refreshValue();
31422         // should load data in the grid really....
31423     },
31424     
31425     // private
31426     refreshValue: function() {
31427          var val = [];
31428         this.grid.getDataSource().each(function(r) {
31429             val.push(r.data);
31430         });
31431         this.el.dom.value = Roo.encode(val);
31432     }
31433     
31434      
31435     
31436     
31437 });/*
31438  * Based on:
31439  * Ext JS Library 1.1.1
31440  * Copyright(c) 2006-2007, Ext JS, LLC.
31441  *
31442  * Originally Released Under LGPL - original licence link has changed is not relivant.
31443  *
31444  * Fork - LGPL
31445  * <script type="text/javascript">
31446  */
31447 /**
31448  * @class Roo.form.DisplayField
31449  * @extends Roo.form.Field
31450  * A generic Field to display non-editable data.
31451  * @cfg {Boolean} closable (true|false) default false
31452  * @constructor
31453  * Creates a new Display Field item.
31454  * @param {Object} config Configuration options
31455  */
31456 Roo.form.DisplayField = function(config){
31457     Roo.form.DisplayField.superclass.constructor.call(this, config);
31458     
31459     this.addEvents({
31460         /**
31461          * @event close
31462          * Fires after the click the close btn
31463              * @param {Roo.form.DisplayField} this
31464              */
31465         close : true
31466     });
31467 };
31468
31469 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31470     inputType:      'hidden',
31471     allowBlank:     true,
31472     readOnly:         true,
31473     
31474  
31475     /**
31476      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31477      */
31478     focusClass : undefined,
31479     /**
31480      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31481      */
31482     fieldClass: 'x-form-field',
31483     
31484      /**
31485      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31486      */
31487     valueRenderer: undefined,
31488     
31489     width: 100,
31490     /**
31491      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31492      * {tag: "input", type: "checkbox", autocomplete: "off"})
31493      */
31494      
31495  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31496  
31497     closable : false,
31498     
31499     onResize : function(){
31500         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31501         
31502     },
31503
31504     initEvents : function(){
31505         // Roo.form.Checkbox.superclass.initEvents.call(this);
31506         // has no events...
31507         
31508         if(this.closable){
31509             this.closeEl.on('click', this.onClose, this);
31510         }
31511        
31512     },
31513
31514
31515     getResizeEl : function(){
31516         return this.wrap;
31517     },
31518
31519     getPositionEl : function(){
31520         return this.wrap;
31521     },
31522
31523     // private
31524     onRender : function(ct, position){
31525         
31526         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31527         //if(this.inputValue !== undefined){
31528         this.wrap = this.el.wrap();
31529         
31530         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31531         
31532         if(this.closable){
31533             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31534         }
31535         
31536         if (this.bodyStyle) {
31537             this.viewEl.applyStyles(this.bodyStyle);
31538         }
31539         //this.viewEl.setStyle('padding', '2px');
31540         
31541         this.setValue(this.value);
31542         
31543     },
31544 /*
31545     // private
31546     initValue : Roo.emptyFn,
31547
31548   */
31549
31550         // private
31551     onClick : function(){
31552         
31553     },
31554
31555     /**
31556      * Sets the checked state of the checkbox.
31557      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31558      */
31559     setValue : function(v){
31560         this.value = v;
31561         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31562         // this might be called before we have a dom element..
31563         if (!this.viewEl) {
31564             return;
31565         }
31566         this.viewEl.dom.innerHTML = html;
31567         Roo.form.DisplayField.superclass.setValue.call(this, v);
31568
31569     },
31570     
31571     onClose : function(e)
31572     {
31573         e.preventDefault();
31574         
31575         this.fireEvent('close', this);
31576     }
31577 });/*
31578  * 
31579  * Licence- LGPL
31580  * 
31581  */
31582
31583 /**
31584  * @class Roo.form.DayPicker
31585  * @extends Roo.form.Field
31586  * A Day picker show [M] [T] [W] ....
31587  * @constructor
31588  * Creates a new Day Picker
31589  * @param {Object} config Configuration options
31590  */
31591 Roo.form.DayPicker= function(config){
31592     Roo.form.DayPicker.superclass.constructor.call(this, config);
31593      
31594 };
31595
31596 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31597     /**
31598      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31599      */
31600     focusClass : undefined,
31601     /**
31602      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31603      */
31604     fieldClass: "x-form-field",
31605    
31606     /**
31607      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31608      * {tag: "input", type: "checkbox", autocomplete: "off"})
31609      */
31610     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31611     
31612    
31613     actionMode : 'viewEl', 
31614     //
31615     // private
31616  
31617     inputType : 'hidden',
31618     
31619      
31620     inputElement: false, // real input element?
31621     basedOn: false, // ????
31622     
31623     isFormField: true, // not sure where this is needed!!!!
31624
31625     onResize : function(){
31626         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31627         if(!this.boxLabel){
31628             this.el.alignTo(this.wrap, 'c-c');
31629         }
31630     },
31631
31632     initEvents : function(){
31633         Roo.form.Checkbox.superclass.initEvents.call(this);
31634         this.el.on("click", this.onClick,  this);
31635         this.el.on("change", this.onClick,  this);
31636     },
31637
31638
31639     getResizeEl : function(){
31640         return this.wrap;
31641     },
31642
31643     getPositionEl : function(){
31644         return this.wrap;
31645     },
31646
31647     
31648     // private
31649     onRender : function(ct, position){
31650         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31651        
31652         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31653         
31654         var r1 = '<table><tr>';
31655         var r2 = '<tr class="x-form-daypick-icons">';
31656         for (var i=0; i < 7; i++) {
31657             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31658             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31659         }
31660         
31661         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31662         viewEl.select('img').on('click', this.onClick, this);
31663         this.viewEl = viewEl;   
31664         
31665         
31666         // this will not work on Chrome!!!
31667         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31668         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31669         
31670         
31671           
31672
31673     },
31674
31675     // private
31676     initValue : Roo.emptyFn,
31677
31678     /**
31679      * Returns the checked state of the checkbox.
31680      * @return {Boolean} True if checked, else false
31681      */
31682     getValue : function(){
31683         return this.el.dom.value;
31684         
31685     },
31686
31687         // private
31688     onClick : function(e){ 
31689         //this.setChecked(!this.checked);
31690         Roo.get(e.target).toggleClass('x-menu-item-checked');
31691         this.refreshValue();
31692         //if(this.el.dom.checked != this.checked){
31693         //    this.setValue(this.el.dom.checked);
31694        // }
31695     },
31696     
31697     // private
31698     refreshValue : function()
31699     {
31700         var val = '';
31701         this.viewEl.select('img',true).each(function(e,i,n)  {
31702             val += e.is(".x-menu-item-checked") ? String(n) : '';
31703         });
31704         this.setValue(val, true);
31705     },
31706
31707     /**
31708      * Sets the checked state of the checkbox.
31709      * On is always based on a string comparison between inputValue and the param.
31710      * @param {Boolean/String} value - the value to set 
31711      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31712      */
31713     setValue : function(v,suppressEvent){
31714         if (!this.el.dom) {
31715             return;
31716         }
31717         var old = this.el.dom.value ;
31718         this.el.dom.value = v;
31719         if (suppressEvent) {
31720             return ;
31721         }
31722          
31723         // update display..
31724         this.viewEl.select('img',true).each(function(e,i,n)  {
31725             
31726             var on = e.is(".x-menu-item-checked");
31727             var newv = v.indexOf(String(n)) > -1;
31728             if (on != newv) {
31729                 e.toggleClass('x-menu-item-checked');
31730             }
31731             
31732         });
31733         
31734         
31735         this.fireEvent('change', this, v, old);
31736         
31737         
31738     },
31739    
31740     // handle setting of hidden value by some other method!!?!?
31741     setFromHidden: function()
31742     {
31743         if(!this.el){
31744             return;
31745         }
31746         //console.log("SET FROM HIDDEN");
31747         //alert('setFrom hidden');
31748         this.setValue(this.el.dom.value);
31749     },
31750     
31751     onDestroy : function()
31752     {
31753         if(this.viewEl){
31754             Roo.get(this.viewEl).remove();
31755         }
31756          
31757         Roo.form.DayPicker.superclass.onDestroy.call(this);
31758     }
31759
31760 });/*
31761  * RooJS Library 1.1.1
31762  * Copyright(c) 2008-2011  Alan Knowles
31763  *
31764  * License - LGPL
31765  */
31766  
31767
31768 /**
31769  * @class Roo.form.ComboCheck
31770  * @extends Roo.form.ComboBox
31771  * A combobox for multiple select items.
31772  *
31773  * FIXME - could do with a reset button..
31774  * 
31775  * @constructor
31776  * Create a new ComboCheck
31777  * @param {Object} config Configuration options
31778  */
31779 Roo.form.ComboCheck = function(config){
31780     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31781     // should verify some data...
31782     // like
31783     // hiddenName = required..
31784     // displayField = required
31785     // valudField == required
31786     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31787     var _t = this;
31788     Roo.each(req, function(e) {
31789         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31790             throw "Roo.form.ComboCheck : missing value for: " + e;
31791         }
31792     });
31793     
31794     
31795 };
31796
31797 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31798      
31799      
31800     editable : false,
31801      
31802     selectedClass: 'x-menu-item-checked', 
31803     
31804     // private
31805     onRender : function(ct, position){
31806         var _t = this;
31807         
31808         
31809         
31810         if(!this.tpl){
31811             var cls = 'x-combo-list';
31812
31813             
31814             this.tpl =  new Roo.Template({
31815                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31816                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31817                    '<span>{' + this.displayField + '}</span>' +
31818                     '</div>' 
31819                 
31820             });
31821         }
31822  
31823         
31824         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31825         this.view.singleSelect = false;
31826         this.view.multiSelect = true;
31827         this.view.toggleSelect = true;
31828         this.pageTb.add(new Roo.Toolbar.Fill(), {
31829             
31830             text: 'Done',
31831             handler: function()
31832             {
31833                 _t.collapse();
31834             }
31835         });
31836     },
31837     
31838     onViewOver : function(e, t){
31839         // do nothing...
31840         return;
31841         
31842     },
31843     
31844     onViewClick : function(doFocus,index){
31845         return;
31846         
31847     },
31848     select: function () {
31849         //Roo.log("SELECT CALLED");
31850     },
31851      
31852     selectByValue : function(xv, scrollIntoView){
31853         var ar = this.getValueArray();
31854         var sels = [];
31855         
31856         Roo.each(ar, function(v) {
31857             if(v === undefined || v === null){
31858                 return;
31859             }
31860             var r = this.findRecord(this.valueField, v);
31861             if(r){
31862                 sels.push(this.store.indexOf(r))
31863                 
31864             }
31865         },this);
31866         this.view.select(sels);
31867         return false;
31868     },
31869     
31870     
31871     
31872     onSelect : function(record, index){
31873        // Roo.log("onselect Called");
31874        // this is only called by the clear button now..
31875         this.view.clearSelections();
31876         this.setValue('[]');
31877         if (this.value != this.valueBefore) {
31878             this.fireEvent('change', this, this.value, this.valueBefore);
31879             this.valueBefore = this.value;
31880         }
31881     },
31882     getValueArray : function()
31883     {
31884         var ar = [] ;
31885         
31886         try {
31887             //Roo.log(this.value);
31888             if (typeof(this.value) == 'undefined') {
31889                 return [];
31890             }
31891             var ar = Roo.decode(this.value);
31892             return  ar instanceof Array ? ar : []; //?? valid?
31893             
31894         } catch(e) {
31895             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31896             return [];
31897         }
31898          
31899     },
31900     expand : function ()
31901     {
31902         
31903         Roo.form.ComboCheck.superclass.expand.call(this);
31904         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31905         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31906         
31907
31908     },
31909     
31910     collapse : function(){
31911         Roo.form.ComboCheck.superclass.collapse.call(this);
31912         var sl = this.view.getSelectedIndexes();
31913         var st = this.store;
31914         var nv = [];
31915         var tv = [];
31916         var r;
31917         Roo.each(sl, function(i) {
31918             r = st.getAt(i);
31919             nv.push(r.get(this.valueField));
31920         },this);
31921         this.setValue(Roo.encode(nv));
31922         if (this.value != this.valueBefore) {
31923
31924             this.fireEvent('change', this, this.value, this.valueBefore);
31925             this.valueBefore = this.value;
31926         }
31927         
31928     },
31929     
31930     setValue : function(v){
31931         // Roo.log(v);
31932         this.value = v;
31933         
31934         var vals = this.getValueArray();
31935         var tv = [];
31936         Roo.each(vals, function(k) {
31937             var r = this.findRecord(this.valueField, k);
31938             if(r){
31939                 tv.push(r.data[this.displayField]);
31940             }else if(this.valueNotFoundText !== undefined){
31941                 tv.push( this.valueNotFoundText );
31942             }
31943         },this);
31944        // Roo.log(tv);
31945         
31946         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31947         this.hiddenField.value = v;
31948         this.value = v;
31949     }
31950     
31951 });/*
31952  * Based on:
31953  * Ext JS Library 1.1.1
31954  * Copyright(c) 2006-2007, Ext JS, LLC.
31955  *
31956  * Originally Released Under LGPL - original licence link has changed is not relivant.
31957  *
31958  * Fork - LGPL
31959  * <script type="text/javascript">
31960  */
31961  
31962 /**
31963  * @class Roo.form.Signature
31964  * @extends Roo.form.Field
31965  * Signature field.  
31966  * @constructor
31967  * 
31968  * @param {Object} config Configuration options
31969  */
31970
31971 Roo.form.Signature = function(config){
31972     Roo.form.Signature.superclass.constructor.call(this, config);
31973     
31974     this.addEvents({// not in used??
31975          /**
31976          * @event confirm
31977          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31978              * @param {Roo.form.Signature} combo This combo box
31979              */
31980         'confirm' : true,
31981         /**
31982          * @event reset
31983          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31984              * @param {Roo.form.ComboBox} combo This combo box
31985              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31986              */
31987         'reset' : true
31988     });
31989 };
31990
31991 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31992     /**
31993      * @cfg {Object} labels Label to use when rendering a form.
31994      * defaults to 
31995      * labels : { 
31996      *      clear : "Clear",
31997      *      confirm : "Confirm"
31998      *  }
31999      */
32000     labels : { 
32001         clear : "Clear",
32002         confirm : "Confirm"
32003     },
32004     /**
32005      * @cfg {Number} width The signature panel width (defaults to 300)
32006      */
32007     width: 300,
32008     /**
32009      * @cfg {Number} height The signature panel height (defaults to 100)
32010      */
32011     height : 100,
32012     /**
32013      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32014      */
32015     allowBlank : false,
32016     
32017     //private
32018     // {Object} signPanel The signature SVG panel element (defaults to {})
32019     signPanel : {},
32020     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32021     isMouseDown : false,
32022     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32023     isConfirmed : false,
32024     // {String} signatureTmp SVG mapping string (defaults to empty string)
32025     signatureTmp : '',
32026     
32027     
32028     defaultAutoCreate : { // modified by initCompnoent..
32029         tag: "input",
32030         type:"hidden"
32031     },
32032
32033     // private
32034     onRender : function(ct, position){
32035         
32036         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32037         
32038         this.wrap = this.el.wrap({
32039             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32040         });
32041         
32042         this.createToolbar(this);
32043         this.signPanel = this.wrap.createChild({
32044                 tag: 'div',
32045                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32046             }, this.el
32047         );
32048             
32049         this.svgID = Roo.id();
32050         this.svgEl = this.signPanel.createChild({
32051               xmlns : 'http://www.w3.org/2000/svg',
32052               tag : 'svg',
32053               id : this.svgID + "-svg",
32054               width: this.width,
32055               height: this.height,
32056               viewBox: '0 0 '+this.width+' '+this.height,
32057               cn : [
32058                 {
32059                     tag: "rect",
32060                     id: this.svgID + "-svg-r",
32061                     width: this.width,
32062                     height: this.height,
32063                     fill: "#ffa"
32064                 },
32065                 {
32066                     tag: "line",
32067                     id: this.svgID + "-svg-l",
32068                     x1: "0", // start
32069                     y1: (this.height*0.8), // start set the line in 80% of height
32070                     x2: this.width, // end
32071                     y2: (this.height*0.8), // end set the line in 80% of height
32072                     'stroke': "#666",
32073                     'stroke-width': "1",
32074                     'stroke-dasharray': "3",
32075                     'shape-rendering': "crispEdges",
32076                     'pointer-events': "none"
32077                 },
32078                 {
32079                     tag: "path",
32080                     id: this.svgID + "-svg-p",
32081                     'stroke': "navy",
32082                     'stroke-width': "3",
32083                     'fill': "none",
32084                     'pointer-events': 'none'
32085                 }
32086               ]
32087         });
32088         this.createSVG();
32089         this.svgBox = this.svgEl.dom.getScreenCTM();
32090     },
32091     createSVG : function(){ 
32092         var svg = this.signPanel;
32093         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32094         var t = this;
32095
32096         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32097         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32098         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32099         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32100         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32101         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32102         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32103         
32104     },
32105     isTouchEvent : function(e){
32106         return e.type.match(/^touch/);
32107     },
32108     getCoords : function (e) {
32109         var pt    = this.svgEl.dom.createSVGPoint();
32110         pt.x = e.clientX; 
32111         pt.y = e.clientY;
32112         if (this.isTouchEvent(e)) {
32113             pt.x =  e.targetTouches[0].clientX;
32114             pt.y = e.targetTouches[0].clientY;
32115         }
32116         var a = this.svgEl.dom.getScreenCTM();
32117         var b = a.inverse();
32118         var mx = pt.matrixTransform(b);
32119         return mx.x + ',' + mx.y;
32120     },
32121     //mouse event headler 
32122     down : function (e) {
32123         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32124         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32125         
32126         this.isMouseDown = true;
32127         
32128         e.preventDefault();
32129     },
32130     move : function (e) {
32131         if (this.isMouseDown) {
32132             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32133             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32134         }
32135         
32136         e.preventDefault();
32137     },
32138     up : function (e) {
32139         this.isMouseDown = false;
32140         var sp = this.signatureTmp.split(' ');
32141         
32142         if(sp.length > 1){
32143             if(!sp[sp.length-2].match(/^L/)){
32144                 sp.pop();
32145                 sp.pop();
32146                 sp.push("");
32147                 this.signatureTmp = sp.join(" ");
32148             }
32149         }
32150         if(this.getValue() != this.signatureTmp){
32151             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32152             this.isConfirmed = false;
32153         }
32154         e.preventDefault();
32155     },
32156     
32157     /**
32158      * Protected method that will not generally be called directly. It
32159      * is called when the editor creates its toolbar. Override this method if you need to
32160      * add custom toolbar buttons.
32161      * @param {HtmlEditor} editor
32162      */
32163     createToolbar : function(editor){
32164          function btn(id, toggle, handler){
32165             var xid = fid + '-'+ id ;
32166             return {
32167                 id : xid,
32168                 cmd : id,
32169                 cls : 'x-btn-icon x-edit-'+id,
32170                 enableToggle:toggle !== false,
32171                 scope: editor, // was editor...
32172                 handler:handler||editor.relayBtnCmd,
32173                 clickEvent:'mousedown',
32174                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32175                 tabIndex:-1
32176             };
32177         }
32178         
32179         
32180         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32181         this.tb = tb;
32182         this.tb.add(
32183            {
32184                 cls : ' x-signature-btn x-signature-'+id,
32185                 scope: editor, // was editor...
32186                 handler: this.reset,
32187                 clickEvent:'mousedown',
32188                 text: this.labels.clear
32189             },
32190             {
32191                  xtype : 'Fill',
32192                  xns: Roo.Toolbar
32193             }, 
32194             {
32195                 cls : '  x-signature-btn x-signature-'+id,
32196                 scope: editor, // was editor...
32197                 handler: this.confirmHandler,
32198                 clickEvent:'mousedown',
32199                 text: this.labels.confirm
32200             }
32201         );
32202     
32203     },
32204     //public
32205     /**
32206      * when user is clicked confirm then show this image.....
32207      * 
32208      * @return {String} Image Data URI
32209      */
32210     getImageDataURI : function(){
32211         var svg = this.svgEl.dom.parentNode.innerHTML;
32212         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32213         return src; 
32214     },
32215     /**
32216      * 
32217      * @return {Boolean} this.isConfirmed
32218      */
32219     getConfirmed : function(){
32220         return this.isConfirmed;
32221     },
32222     /**
32223      * 
32224      * @return {Number} this.width
32225      */
32226     getWidth : function(){
32227         return this.width;
32228     },
32229     /**
32230      * 
32231      * @return {Number} this.height
32232      */
32233     getHeight : function(){
32234         return this.height;
32235     },
32236     // private
32237     getSignature : function(){
32238         return this.signatureTmp;
32239     },
32240     // private
32241     reset : function(){
32242         this.signatureTmp = '';
32243         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32244         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32245         this.isConfirmed = false;
32246         Roo.form.Signature.superclass.reset.call(this);
32247     },
32248     setSignature : function(s){
32249         this.signatureTmp = s;
32250         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32251         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32252         this.setValue(s);
32253         this.isConfirmed = false;
32254         Roo.form.Signature.superclass.reset.call(this);
32255     }, 
32256     test : function(){
32257 //        Roo.log(this.signPanel.dom.contentWindow.up())
32258     },
32259     //private
32260     setConfirmed : function(){
32261         
32262         
32263         
32264 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32265     },
32266     // private
32267     confirmHandler : function(){
32268         if(!this.getSignature()){
32269             return;
32270         }
32271         
32272         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32273         this.setValue(this.getSignature());
32274         this.isConfirmed = true;
32275         
32276         this.fireEvent('confirm', this);
32277     },
32278     // private
32279     // Subclasses should provide the validation implementation by overriding this
32280     validateValue : function(value){
32281         if(this.allowBlank){
32282             return true;
32283         }
32284         
32285         if(this.isConfirmed){
32286             return true;
32287         }
32288         return false;
32289     }
32290 });/*
32291  * Based on:
32292  * Ext JS Library 1.1.1
32293  * Copyright(c) 2006-2007, Ext JS, LLC.
32294  *
32295  * Originally Released Under LGPL - original licence link has changed is not relivant.
32296  *
32297  * Fork - LGPL
32298  * <script type="text/javascript">
32299  */
32300  
32301
32302 /**
32303  * @class Roo.form.ComboBox
32304  * @extends Roo.form.TriggerField
32305  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32306  * @constructor
32307  * Create a new ComboBox.
32308  * @param {Object} config Configuration options
32309  */
32310 Roo.form.Select = function(config){
32311     Roo.form.Select.superclass.constructor.call(this, config);
32312      
32313 };
32314
32315 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32316     /**
32317      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32318      */
32319     /**
32320      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32321      * rendering into an Roo.Editor, defaults to false)
32322      */
32323     /**
32324      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32325      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32326      */
32327     /**
32328      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32329      */
32330     /**
32331      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32332      * the dropdown list (defaults to undefined, with no header element)
32333      */
32334
32335      /**
32336      * @cfg {String/Roo.Template} tpl The template to use to render the output
32337      */
32338      
32339     // private
32340     defaultAutoCreate : {tag: "select"  },
32341     /**
32342      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32343      */
32344     listWidth: undefined,
32345     /**
32346      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32347      * mode = 'remote' or 'text' if mode = 'local')
32348      */
32349     displayField: undefined,
32350     /**
32351      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32352      * mode = 'remote' or 'value' if mode = 'local'). 
32353      * Note: use of a valueField requires the user make a selection
32354      * in order for a value to be mapped.
32355      */
32356     valueField: undefined,
32357     
32358     
32359     /**
32360      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32361      * field's data value (defaults to the underlying DOM element's name)
32362      */
32363     hiddenName: undefined,
32364     /**
32365      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32366      */
32367     listClass: '',
32368     /**
32369      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32370      */
32371     selectedClass: 'x-combo-selected',
32372     /**
32373      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32374      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32375      * which displays a downward arrow icon).
32376      */
32377     triggerClass : 'x-form-arrow-trigger',
32378     /**
32379      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32380      */
32381     shadow:'sides',
32382     /**
32383      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32384      * anchor positions (defaults to 'tl-bl')
32385      */
32386     listAlign: 'tl-bl?',
32387     /**
32388      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32389      */
32390     maxHeight: 300,
32391     /**
32392      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32393      * query specified by the allQuery config option (defaults to 'query')
32394      */
32395     triggerAction: 'query',
32396     /**
32397      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32398      * (defaults to 4, does not apply if editable = false)
32399      */
32400     minChars : 4,
32401     /**
32402      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32403      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32404      */
32405     typeAhead: false,
32406     /**
32407      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32408      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32409      */
32410     queryDelay: 500,
32411     /**
32412      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32413      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32414      */
32415     pageSize: 0,
32416     /**
32417      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32418      * when editable = true (defaults to false)
32419      */
32420     selectOnFocus:false,
32421     /**
32422      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32423      */
32424     queryParam: 'query',
32425     /**
32426      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32427      * when mode = 'remote' (defaults to 'Loading...')
32428      */
32429     loadingText: 'Loading...',
32430     /**
32431      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32432      */
32433     resizable: false,
32434     /**
32435      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32436      */
32437     handleHeight : 8,
32438     /**
32439      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32440      * traditional select (defaults to true)
32441      */
32442     editable: true,
32443     /**
32444      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32445      */
32446     allQuery: '',
32447     /**
32448      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32449      */
32450     mode: 'remote',
32451     /**
32452      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32453      * listWidth has a higher value)
32454      */
32455     minListWidth : 70,
32456     /**
32457      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32458      * allow the user to set arbitrary text into the field (defaults to false)
32459      */
32460     forceSelection:false,
32461     /**
32462      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32463      * if typeAhead = true (defaults to 250)
32464      */
32465     typeAheadDelay : 250,
32466     /**
32467      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32468      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32469      */
32470     valueNotFoundText : undefined,
32471     
32472     /**
32473      * @cfg {String} defaultValue The value displayed after loading the store.
32474      */
32475     defaultValue: '',
32476     
32477     /**
32478      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32479      */
32480     blockFocus : false,
32481     
32482     /**
32483      * @cfg {Boolean} disableClear Disable showing of clear button.
32484      */
32485     disableClear : false,
32486     /**
32487      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32488      */
32489     alwaysQuery : false,
32490     
32491     //private
32492     addicon : false,
32493     editicon: false,
32494     
32495     // element that contains real text value.. (when hidden is used..)
32496      
32497     // private
32498     onRender : function(ct, position){
32499         Roo.form.Field.prototype.onRender.call(this, ct, position);
32500         
32501         if(this.store){
32502             this.store.on('beforeload', this.onBeforeLoad, this);
32503             this.store.on('load', this.onLoad, this);
32504             this.store.on('loadexception', this.onLoadException, this);
32505             this.store.load({});
32506         }
32507         
32508         
32509         
32510     },
32511
32512     // private
32513     initEvents : function(){
32514         //Roo.form.ComboBox.superclass.initEvents.call(this);
32515  
32516     },
32517
32518     onDestroy : function(){
32519        
32520         if(this.store){
32521             this.store.un('beforeload', this.onBeforeLoad, this);
32522             this.store.un('load', this.onLoad, this);
32523             this.store.un('loadexception', this.onLoadException, this);
32524         }
32525         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32526     },
32527
32528     // private
32529     fireKey : function(e){
32530         if(e.isNavKeyPress() && !this.list.isVisible()){
32531             this.fireEvent("specialkey", this, e);
32532         }
32533     },
32534
32535     // private
32536     onResize: function(w, h){
32537         
32538         return; 
32539     
32540         
32541     },
32542
32543     /**
32544      * Allow or prevent the user from directly editing the field text.  If false is passed,
32545      * the user will only be able to select from the items defined in the dropdown list.  This method
32546      * is the runtime equivalent of setting the 'editable' config option at config time.
32547      * @param {Boolean} value True to allow the user to directly edit the field text
32548      */
32549     setEditable : function(value){
32550          
32551     },
32552
32553     // private
32554     onBeforeLoad : function(){
32555         
32556         Roo.log("Select before load");
32557         return;
32558     
32559         this.innerList.update(this.loadingText ?
32560                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32561         //this.restrictHeight();
32562         this.selectedIndex = -1;
32563     },
32564
32565     // private
32566     onLoad : function(){
32567
32568     
32569         var dom = this.el.dom;
32570         dom.innerHTML = '';
32571          var od = dom.ownerDocument;
32572          
32573         if (this.emptyText) {
32574             var op = od.createElement('option');
32575             op.setAttribute('value', '');
32576             op.innerHTML = String.format('{0}', this.emptyText);
32577             dom.appendChild(op);
32578         }
32579         if(this.store.getCount() > 0){
32580            
32581             var vf = this.valueField;
32582             var df = this.displayField;
32583             this.store.data.each(function(r) {
32584                 // which colmsn to use... testing - cdoe / title..
32585                 var op = od.createElement('option');
32586                 op.setAttribute('value', r.data[vf]);
32587                 op.innerHTML = String.format('{0}', r.data[df]);
32588                 dom.appendChild(op);
32589             });
32590             if (typeof(this.defaultValue != 'undefined')) {
32591                 this.setValue(this.defaultValue);
32592             }
32593             
32594              
32595         }else{
32596             //this.onEmptyResults();
32597         }
32598         //this.el.focus();
32599     },
32600     // private
32601     onLoadException : function()
32602     {
32603         dom.innerHTML = '';
32604             
32605         Roo.log("Select on load exception");
32606         return;
32607     
32608         this.collapse();
32609         Roo.log(this.store.reader.jsonData);
32610         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32611             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32612         }
32613         
32614         
32615     },
32616     // private
32617     onTypeAhead : function(){
32618          
32619     },
32620
32621     // private
32622     onSelect : function(record, index){
32623         Roo.log('on select?');
32624         return;
32625         if(this.fireEvent('beforeselect', this, record, index) !== false){
32626             this.setFromData(index > -1 ? record.data : false);
32627             this.collapse();
32628             this.fireEvent('select', this, record, index);
32629         }
32630     },
32631
32632     /**
32633      * Returns the currently selected field value or empty string if no value is set.
32634      * @return {String} value The selected value
32635      */
32636     getValue : function(){
32637         var dom = this.el.dom;
32638         this.value = dom.options[dom.selectedIndex].value;
32639         return this.value;
32640         
32641     },
32642
32643     /**
32644      * Clears any text/value currently set in the field
32645      */
32646     clearValue : function(){
32647         this.value = '';
32648         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32649         
32650     },
32651
32652     /**
32653      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32654      * will be displayed in the field.  If the value does not match the data value of an existing item,
32655      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32656      * Otherwise the field will be blank (although the value will still be set).
32657      * @param {String} value The value to match
32658      */
32659     setValue : function(v){
32660         var d = this.el.dom;
32661         for (var i =0; i < d.options.length;i++) {
32662             if (v == d.options[i].value) {
32663                 d.selectedIndex = i;
32664                 this.value = v;
32665                 return;
32666             }
32667         }
32668         this.clearValue();
32669     },
32670     /**
32671      * @property {Object} the last set data for the element
32672      */
32673     
32674     lastData : false,
32675     /**
32676      * Sets the value of the field based on a object which is related to the record format for the store.
32677      * @param {Object} value the value to set as. or false on reset?
32678      */
32679     setFromData : function(o){
32680         Roo.log('setfrom data?');
32681          
32682         
32683         
32684     },
32685     // private
32686     reset : function(){
32687         this.clearValue();
32688     },
32689     // private
32690     findRecord : function(prop, value){
32691         
32692         return false;
32693     
32694         var record;
32695         if(this.store.getCount() > 0){
32696             this.store.each(function(r){
32697                 if(r.data[prop] == value){
32698                     record = r;
32699                     return false;
32700                 }
32701                 return true;
32702             });
32703         }
32704         return record;
32705     },
32706     
32707     getName: function()
32708     {
32709         // returns hidden if it's set..
32710         if (!this.rendered) {return ''};
32711         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32712         
32713     },
32714      
32715
32716     
32717
32718     // private
32719     onEmptyResults : function(){
32720         Roo.log('empty results');
32721         //this.collapse();
32722     },
32723
32724     /**
32725      * Returns true if the dropdown list is expanded, else false.
32726      */
32727     isExpanded : function(){
32728         return false;
32729     },
32730
32731     /**
32732      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32733      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32734      * @param {String} value The data value of the item to select
32735      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32736      * selected item if it is not currently in view (defaults to true)
32737      * @return {Boolean} True if the value matched an item in the list, else false
32738      */
32739     selectByValue : function(v, scrollIntoView){
32740         Roo.log('select By Value');
32741         return false;
32742     
32743         if(v !== undefined && v !== null){
32744             var r = this.findRecord(this.valueField || this.displayField, v);
32745             if(r){
32746                 this.select(this.store.indexOf(r), scrollIntoView);
32747                 return true;
32748             }
32749         }
32750         return false;
32751     },
32752
32753     /**
32754      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32755      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32756      * @param {Number} index The zero-based index of the list item to select
32757      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32758      * selected item if it is not currently in view (defaults to true)
32759      */
32760     select : function(index, scrollIntoView){
32761         Roo.log('select ');
32762         return  ;
32763         
32764         this.selectedIndex = index;
32765         this.view.select(index);
32766         if(scrollIntoView !== false){
32767             var el = this.view.getNode(index);
32768             if(el){
32769                 this.innerList.scrollChildIntoView(el, false);
32770             }
32771         }
32772     },
32773
32774       
32775
32776     // private
32777     validateBlur : function(){
32778         
32779         return;
32780         
32781     },
32782
32783     // private
32784     initQuery : function(){
32785         this.doQuery(this.getRawValue());
32786     },
32787
32788     // private
32789     doForce : function(){
32790         if(this.el.dom.value.length > 0){
32791             this.el.dom.value =
32792                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32793              
32794         }
32795     },
32796
32797     /**
32798      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32799      * query allowing the query action to be canceled if needed.
32800      * @param {String} query The SQL query to execute
32801      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32802      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32803      * saved in the current store (defaults to false)
32804      */
32805     doQuery : function(q, forceAll){
32806         
32807         Roo.log('doQuery?');
32808         if(q === undefined || q === null){
32809             q = '';
32810         }
32811         var qe = {
32812             query: q,
32813             forceAll: forceAll,
32814             combo: this,
32815             cancel:false
32816         };
32817         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32818             return false;
32819         }
32820         q = qe.query;
32821         forceAll = qe.forceAll;
32822         if(forceAll === true || (q.length >= this.minChars)){
32823             if(this.lastQuery != q || this.alwaysQuery){
32824                 this.lastQuery = q;
32825                 if(this.mode == 'local'){
32826                     this.selectedIndex = -1;
32827                     if(forceAll){
32828                         this.store.clearFilter();
32829                     }else{
32830                         this.store.filter(this.displayField, q);
32831                     }
32832                     this.onLoad();
32833                 }else{
32834                     this.store.baseParams[this.queryParam] = q;
32835                     this.store.load({
32836                         params: this.getParams(q)
32837                     });
32838                     this.expand();
32839                 }
32840             }else{
32841                 this.selectedIndex = -1;
32842                 this.onLoad();   
32843             }
32844         }
32845     },
32846
32847     // private
32848     getParams : function(q){
32849         var p = {};
32850         //p[this.queryParam] = q;
32851         if(this.pageSize){
32852             p.start = 0;
32853             p.limit = this.pageSize;
32854         }
32855         return p;
32856     },
32857
32858     /**
32859      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32860      */
32861     collapse : function(){
32862         
32863     },
32864
32865     // private
32866     collapseIf : function(e){
32867         
32868     },
32869
32870     /**
32871      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32872      */
32873     expand : function(){
32874         
32875     } ,
32876
32877     // private
32878      
32879
32880     /** 
32881     * @cfg {Boolean} grow 
32882     * @hide 
32883     */
32884     /** 
32885     * @cfg {Number} growMin 
32886     * @hide 
32887     */
32888     /** 
32889     * @cfg {Number} growMax 
32890     * @hide 
32891     */
32892     /**
32893      * @hide
32894      * @method autoSize
32895      */
32896     
32897     setWidth : function()
32898     {
32899         
32900     },
32901     getResizeEl : function(){
32902         return this.el;
32903     }
32904 });//<script type="text/javasscript">
32905  
32906
32907 /**
32908  * @class Roo.DDView
32909  * A DnD enabled version of Roo.View.
32910  * @param {Element/String} container The Element in which to create the View.
32911  * @param {String} tpl The template string used to create the markup for each element of the View
32912  * @param {Object} config The configuration properties. These include all the config options of
32913  * {@link Roo.View} plus some specific to this class.<br>
32914  * <p>
32915  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32916  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32917  * <p>
32918  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32919 .x-view-drag-insert-above {
32920         border-top:1px dotted #3366cc;
32921 }
32922 .x-view-drag-insert-below {
32923         border-bottom:1px dotted #3366cc;
32924 }
32925 </code></pre>
32926  * 
32927  */
32928  
32929 Roo.DDView = function(container, tpl, config) {
32930     Roo.DDView.superclass.constructor.apply(this, arguments);
32931     this.getEl().setStyle("outline", "0px none");
32932     this.getEl().unselectable();
32933     if (this.dragGroup) {
32934         this.setDraggable(this.dragGroup.split(","));
32935     }
32936     if (this.dropGroup) {
32937         this.setDroppable(this.dropGroup.split(","));
32938     }
32939     if (this.deletable) {
32940         this.setDeletable();
32941     }
32942     this.isDirtyFlag = false;
32943         this.addEvents({
32944                 "drop" : true
32945         });
32946 };
32947
32948 Roo.extend(Roo.DDView, Roo.View, {
32949 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32950 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32951 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32952 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32953
32954         isFormField: true,
32955
32956         reset: Roo.emptyFn,
32957         
32958         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32959
32960         validate: function() {
32961                 return true;
32962         },
32963         
32964         destroy: function() {
32965                 this.purgeListeners();
32966                 this.getEl.removeAllListeners();
32967                 this.getEl().remove();
32968                 if (this.dragZone) {
32969                         if (this.dragZone.destroy) {
32970                                 this.dragZone.destroy();
32971                         }
32972                 }
32973                 if (this.dropZone) {
32974                         if (this.dropZone.destroy) {
32975                                 this.dropZone.destroy();
32976                         }
32977                 }
32978         },
32979
32980 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32981         getName: function() {
32982                 return this.name;
32983         },
32984
32985 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32986         setValue: function(v) {
32987                 if (!this.store) {
32988                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32989                 }
32990                 var data = {};
32991                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32992                 this.store.proxy = new Roo.data.MemoryProxy(data);
32993                 this.store.load();
32994         },
32995
32996 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32997         getValue: function() {
32998                 var result = '(';
32999                 this.store.each(function(rec) {
33000                         result += rec.id + ',';
33001                 });
33002                 return result.substr(0, result.length - 1) + ')';
33003         },
33004         
33005         getIds: function() {
33006                 var i = 0, result = new Array(this.store.getCount());
33007                 this.store.each(function(rec) {
33008                         result[i++] = rec.id;
33009                 });
33010                 return result;
33011         },
33012         
33013         isDirty: function() {
33014                 return this.isDirtyFlag;
33015         },
33016
33017 /**
33018  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33019  *      whole Element becomes the target, and this causes the drop gesture to append.
33020  */
33021     getTargetFromEvent : function(e) {
33022                 var target = e.getTarget();
33023                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33024                 target = target.parentNode;
33025                 }
33026                 if (!target) {
33027                         target = this.el.dom.lastChild || this.el.dom;
33028                 }
33029                 return target;
33030     },
33031
33032 /**
33033  *      Create the drag data which consists of an object which has the property "ddel" as
33034  *      the drag proxy element. 
33035  */
33036     getDragData : function(e) {
33037         var target = this.findItemFromChild(e.getTarget());
33038                 if(target) {
33039                         this.handleSelection(e);
33040                         var selNodes = this.getSelectedNodes();
33041             var dragData = {
33042                 source: this,
33043                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33044                 nodes: selNodes,
33045                 records: []
33046                         };
33047                         var selectedIndices = this.getSelectedIndexes();
33048                         for (var i = 0; i < selectedIndices.length; i++) {
33049                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33050                         }
33051                         if (selNodes.length == 1) {
33052                                 dragData.ddel = target.cloneNode(true); // the div element
33053                         } else {
33054                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33055                                 div.className = 'multi-proxy';
33056                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33057                                         div.appendChild(selNodes[i].cloneNode(true));
33058                                 }
33059                                 dragData.ddel = div;
33060                         }
33061             //console.log(dragData)
33062             //console.log(dragData.ddel.innerHTML)
33063                         return dragData;
33064                 }
33065         //console.log('nodragData')
33066                 return false;
33067     },
33068     
33069 /**     Specify to which ddGroup items in this DDView may be dragged. */
33070     setDraggable: function(ddGroup) {
33071         if (ddGroup instanceof Array) {
33072                 Roo.each(ddGroup, this.setDraggable, this);
33073                 return;
33074         }
33075         if (this.dragZone) {
33076                 this.dragZone.addToGroup(ddGroup);
33077         } else {
33078                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33079                                 containerScroll: true,
33080                                 ddGroup: ddGroup 
33081
33082                         });
33083 //                      Draggability implies selection. DragZone's mousedown selects the element.
33084                         if (!this.multiSelect) { this.singleSelect = true; }
33085
33086 //                      Wire the DragZone's handlers up to methods in *this*
33087                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33088                 }
33089     },
33090
33091 /**     Specify from which ddGroup this DDView accepts drops. */
33092     setDroppable: function(ddGroup) {
33093         if (ddGroup instanceof Array) {
33094                 Roo.each(ddGroup, this.setDroppable, this);
33095                 return;
33096         }
33097         if (this.dropZone) {
33098                 this.dropZone.addToGroup(ddGroup);
33099         } else {
33100                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33101                                 containerScroll: true,
33102                                 ddGroup: ddGroup
33103                         });
33104
33105 //                      Wire the DropZone's handlers up to methods in *this*
33106                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33107                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33108                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33109                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33110                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33111                 }
33112     },
33113
33114 /**     Decide whether to drop above or below a View node. */
33115     getDropPoint : function(e, n, dd){
33116         if (n == this.el.dom) { return "above"; }
33117                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33118                 var c = t + (b - t) / 2;
33119                 var y = Roo.lib.Event.getPageY(e);
33120                 if(y <= c) {
33121                         return "above";
33122                 }else{
33123                         return "below";
33124                 }
33125     },
33126
33127     onNodeEnter : function(n, dd, e, data){
33128                 return false;
33129     },
33130     
33131     onNodeOver : function(n, dd, e, data){
33132                 var pt = this.getDropPoint(e, n, dd);
33133                 // set the insert point style on the target node
33134                 var dragElClass = this.dropNotAllowed;
33135                 if (pt) {
33136                         var targetElClass;
33137                         if (pt == "above"){
33138                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33139                                 targetElClass = "x-view-drag-insert-above";
33140                         } else {
33141                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33142                                 targetElClass = "x-view-drag-insert-below";
33143                         }
33144                         if (this.lastInsertClass != targetElClass){
33145                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33146                                 this.lastInsertClass = targetElClass;
33147                         }
33148                 }
33149                 return dragElClass;
33150         },
33151
33152     onNodeOut : function(n, dd, e, data){
33153                 this.removeDropIndicators(n);
33154     },
33155
33156     onNodeDrop : function(n, dd, e, data){
33157         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33158                 return false;
33159         }
33160         var pt = this.getDropPoint(e, n, dd);
33161                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33162                 if (pt == "below") { insertAt++; }
33163                 for (var i = 0; i < data.records.length; i++) {
33164                         var r = data.records[i];
33165                         var dup = this.store.getById(r.id);
33166                         if (dup && (dd != this.dragZone)) {
33167                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33168                         } else {
33169                                 if (data.copy) {
33170                                         this.store.insert(insertAt++, r.copy());
33171                                 } else {
33172                                         data.source.isDirtyFlag = true;
33173                                         r.store.remove(r);
33174                                         this.store.insert(insertAt++, r);
33175                                 }
33176                                 this.isDirtyFlag = true;
33177                         }
33178                 }
33179                 this.dragZone.cachedTarget = null;
33180                 return true;
33181     },
33182
33183     removeDropIndicators : function(n){
33184                 if(n){
33185                         Roo.fly(n).removeClass([
33186                                 "x-view-drag-insert-above",
33187                                 "x-view-drag-insert-below"]);
33188                         this.lastInsertClass = "_noclass";
33189                 }
33190     },
33191
33192 /**
33193  *      Utility method. Add a delete option to the DDView's context menu.
33194  *      @param {String} imageUrl The URL of the "delete" icon image.
33195  */
33196         setDeletable: function(imageUrl) {
33197                 if (!this.singleSelect && !this.multiSelect) {
33198                         this.singleSelect = true;
33199                 }
33200                 var c = this.getContextMenu();
33201                 this.contextMenu.on("itemclick", function(item) {
33202                         switch (item.id) {
33203                                 case "delete":
33204                                         this.remove(this.getSelectedIndexes());
33205                                         break;
33206                         }
33207                 }, this);
33208                 this.contextMenu.add({
33209                         icon: imageUrl,
33210                         id: "delete",
33211                         text: 'Delete'
33212                 });
33213         },
33214         
33215 /**     Return the context menu for this DDView. */
33216         getContextMenu: function() {
33217                 if (!this.contextMenu) {
33218 //                      Create the View's context menu
33219                         this.contextMenu = new Roo.menu.Menu({
33220                                 id: this.id + "-contextmenu"
33221                         });
33222                         this.el.on("contextmenu", this.showContextMenu, this);
33223                 }
33224                 return this.contextMenu;
33225         },
33226         
33227         disableContextMenu: function() {
33228                 if (this.contextMenu) {
33229                         this.el.un("contextmenu", this.showContextMenu, this);
33230                 }
33231         },
33232
33233         showContextMenu: function(e, item) {
33234         item = this.findItemFromChild(e.getTarget());
33235                 if (item) {
33236                         e.stopEvent();
33237                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33238                         this.contextMenu.showAt(e.getXY());
33239             }
33240     },
33241
33242 /**
33243  *      Remove {@link Roo.data.Record}s at the specified indices.
33244  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33245  */
33246     remove: function(selectedIndices) {
33247                 selectedIndices = [].concat(selectedIndices);
33248                 for (var i = 0; i < selectedIndices.length; i++) {
33249                         var rec = this.store.getAt(selectedIndices[i]);
33250                         this.store.remove(rec);
33251                 }
33252     },
33253
33254 /**
33255  *      Double click fires the event, but also, if this is draggable, and there is only one other
33256  *      related DropZone, it transfers the selected node.
33257  */
33258     onDblClick : function(e){
33259         var item = this.findItemFromChild(e.getTarget());
33260         if(item){
33261             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33262                 return false;
33263             }
33264             if (this.dragGroup) {
33265                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33266                     while (targets.indexOf(this.dropZone) > -1) {
33267                             targets.remove(this.dropZone);
33268                                 }
33269                     if (targets.length == 1) {
33270                                         this.dragZone.cachedTarget = null;
33271                         var el = Roo.get(targets[0].getEl());
33272                         var box = el.getBox(true);
33273                         targets[0].onNodeDrop(el.dom, {
33274                                 target: el.dom,
33275                                 xy: [box.x, box.y + box.height - 1]
33276                         }, null, this.getDragData(e));
33277                     }
33278                 }
33279         }
33280     },
33281     
33282     handleSelection: function(e) {
33283                 this.dragZone.cachedTarget = null;
33284         var item = this.findItemFromChild(e.getTarget());
33285         if (!item) {
33286                 this.clearSelections(true);
33287                 return;
33288         }
33289                 if (item && (this.multiSelect || this.singleSelect)){
33290                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33291                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33292                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33293                                 this.unselect(item);
33294                         } else {
33295                                 this.select(item, this.multiSelect && e.ctrlKey);
33296                                 this.lastSelection = item;
33297                         }
33298                 }
33299     },
33300
33301     onItemClick : function(item, index, e){
33302                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33303                         return false;
33304                 }
33305                 return true;
33306     },
33307
33308     unselect : function(nodeInfo, suppressEvent){
33309                 var node = this.getNode(nodeInfo);
33310                 if(node && this.isSelected(node)){
33311                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33312                                 Roo.fly(node).removeClass(this.selectedClass);
33313                                 this.selections.remove(node);
33314                                 if(!suppressEvent){
33315                                         this.fireEvent("selectionchange", this, this.selections);
33316                                 }
33317                         }
33318                 }
33319     }
33320 });
33321 /*
33322  * Based on:
33323  * Ext JS Library 1.1.1
33324  * Copyright(c) 2006-2007, Ext JS, LLC.
33325  *
33326  * Originally Released Under LGPL - original licence link has changed is not relivant.
33327  *
33328  * Fork - LGPL
33329  * <script type="text/javascript">
33330  */
33331  
33332 /**
33333  * @class Roo.LayoutManager
33334  * @extends Roo.util.Observable
33335  * Base class for layout managers.
33336  */
33337 Roo.LayoutManager = function(container, config){
33338     Roo.LayoutManager.superclass.constructor.call(this);
33339     this.el = Roo.get(container);
33340     // ie scrollbar fix
33341     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33342         document.body.scroll = "no";
33343     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33344         this.el.position('relative');
33345     }
33346     this.id = this.el.id;
33347     this.el.addClass("x-layout-container");
33348     /** false to disable window resize monitoring @type Boolean */
33349     this.monitorWindowResize = true;
33350     this.regions = {};
33351     this.addEvents({
33352         /**
33353          * @event layout
33354          * Fires when a layout is performed. 
33355          * @param {Roo.LayoutManager} this
33356          */
33357         "layout" : true,
33358         /**
33359          * @event regionresized
33360          * Fires when the user resizes a region. 
33361          * @param {Roo.LayoutRegion} region The resized region
33362          * @param {Number} newSize The new size (width for east/west, height for north/south)
33363          */
33364         "regionresized" : true,
33365         /**
33366          * @event regioncollapsed
33367          * Fires when a region is collapsed. 
33368          * @param {Roo.LayoutRegion} region The collapsed region
33369          */
33370         "regioncollapsed" : true,
33371         /**
33372          * @event regionexpanded
33373          * Fires when a region is expanded.  
33374          * @param {Roo.LayoutRegion} region The expanded region
33375          */
33376         "regionexpanded" : true
33377     });
33378     this.updating = false;
33379     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33380 };
33381
33382 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33383     /**
33384      * Returns true if this layout is currently being updated
33385      * @return {Boolean}
33386      */
33387     isUpdating : function(){
33388         return this.updating; 
33389     },
33390     
33391     /**
33392      * Suspend the LayoutManager from doing auto-layouts while
33393      * making multiple add or remove calls
33394      */
33395     beginUpdate : function(){
33396         this.updating = true;    
33397     },
33398     
33399     /**
33400      * Restore auto-layouts and optionally disable the manager from performing a layout
33401      * @param {Boolean} noLayout true to disable a layout update 
33402      */
33403     endUpdate : function(noLayout){
33404         this.updating = false;
33405         if(!noLayout){
33406             this.layout();
33407         }    
33408     },
33409     
33410     layout: function(){
33411         
33412     },
33413     
33414     onRegionResized : function(region, newSize){
33415         this.fireEvent("regionresized", region, newSize);
33416         this.layout();
33417     },
33418     
33419     onRegionCollapsed : function(region){
33420         this.fireEvent("regioncollapsed", region);
33421     },
33422     
33423     onRegionExpanded : function(region){
33424         this.fireEvent("regionexpanded", region);
33425     },
33426         
33427     /**
33428      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33429      * performs box-model adjustments.
33430      * @return {Object} The size as an object {width: (the width), height: (the height)}
33431      */
33432     getViewSize : function(){
33433         var size;
33434         if(this.el.dom != document.body){
33435             size = this.el.getSize();
33436         }else{
33437             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33438         }
33439         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33440         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33441         return size;
33442     },
33443     
33444     /**
33445      * Returns the Element this layout is bound to.
33446      * @return {Roo.Element}
33447      */
33448     getEl : function(){
33449         return this.el;
33450     },
33451     
33452     /**
33453      * Returns the specified region.
33454      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33455      * @return {Roo.LayoutRegion}
33456      */
33457     getRegion : function(target){
33458         return this.regions[target.toLowerCase()];
33459     },
33460     
33461     onWindowResize : function(){
33462         if(this.monitorWindowResize){
33463             this.layout();
33464         }
33465     }
33466 });/*
33467  * Based on:
33468  * Ext JS Library 1.1.1
33469  * Copyright(c) 2006-2007, Ext JS, LLC.
33470  *
33471  * Originally Released Under LGPL - original licence link has changed is not relivant.
33472  *
33473  * Fork - LGPL
33474  * <script type="text/javascript">
33475  */
33476 /**
33477  * @class Roo.BorderLayout
33478  * @extends Roo.LayoutManager
33479  * @children Roo.ContentPanel
33480  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33481  * please see: <br><br>
33482  * <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>
33483  * <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>
33484  * Example:
33485  <pre><code>
33486  var layout = new Roo.BorderLayout(document.body, {
33487     north: {
33488         initialSize: 25,
33489         titlebar: false
33490     },
33491     west: {
33492         split:true,
33493         initialSize: 200,
33494         minSize: 175,
33495         maxSize: 400,
33496         titlebar: true,
33497         collapsible: true
33498     },
33499     east: {
33500         split:true,
33501         initialSize: 202,
33502         minSize: 175,
33503         maxSize: 400,
33504         titlebar: true,
33505         collapsible: true
33506     },
33507     south: {
33508         split:true,
33509         initialSize: 100,
33510         minSize: 100,
33511         maxSize: 200,
33512         titlebar: true,
33513         collapsible: true
33514     },
33515     center: {
33516         titlebar: true,
33517         autoScroll:true,
33518         resizeTabs: true,
33519         minTabWidth: 50,
33520         preferredTabWidth: 150
33521     }
33522 });
33523
33524 // shorthand
33525 var CP = Roo.ContentPanel;
33526
33527 layout.beginUpdate();
33528 layout.add("north", new CP("north", "North"));
33529 layout.add("south", new CP("south", {title: "South", closable: true}));
33530 layout.add("west", new CP("west", {title: "West"}));
33531 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33532 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33533 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33534 layout.getRegion("center").showPanel("center1");
33535 layout.endUpdate();
33536 </code></pre>
33537
33538 <b>The container the layout is rendered into can be either the body element or any other element.
33539 If it is not the body element, the container needs to either be an absolute positioned element,
33540 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33541 the container size if it is not the body element.</b>
33542
33543 * @constructor
33544 * Create a new BorderLayout
33545 * @param {String/HTMLElement/Element} container The container this layout is bound to
33546 * @param {Object} config Configuration options
33547  */
33548 Roo.BorderLayout = function(container, config){
33549     config = config || {};
33550     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33551     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33552     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33553         var target = this.factory.validRegions[i];
33554         if(config[target]){
33555             this.addRegion(target, config[target]);
33556         }
33557     }
33558 };
33559
33560 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33561         
33562         /**
33563          * @cfg {Roo.LayoutRegion} east
33564          */
33565         /**
33566          * @cfg {Roo.LayoutRegion} west
33567          */
33568         /**
33569          * @cfg {Roo.LayoutRegion} north
33570          */
33571         /**
33572          * @cfg {Roo.LayoutRegion} south
33573          */
33574         /**
33575          * @cfg {Roo.LayoutRegion} center
33576          */
33577     /**
33578      * Creates and adds a new region if it doesn't already exist.
33579      * @param {String} target The target region key (north, south, east, west or center).
33580      * @param {Object} config The regions config object
33581      * @return {BorderLayoutRegion} The new region
33582      */
33583     addRegion : function(target, config){
33584         if(!this.regions[target]){
33585             var r = this.factory.create(target, this, config);
33586             this.bindRegion(target, r);
33587         }
33588         return this.regions[target];
33589     },
33590
33591     // private (kinda)
33592     bindRegion : function(name, r){
33593         this.regions[name] = r;
33594         r.on("visibilitychange", this.layout, this);
33595         r.on("paneladded", this.layout, this);
33596         r.on("panelremoved", this.layout, this);
33597         r.on("invalidated", this.layout, this);
33598         r.on("resized", this.onRegionResized, this);
33599         r.on("collapsed", this.onRegionCollapsed, this);
33600         r.on("expanded", this.onRegionExpanded, this);
33601     },
33602
33603     /**
33604      * Performs a layout update.
33605      */
33606     layout : function(){
33607         if(this.updating) {
33608             return;
33609         }
33610         var size = this.getViewSize();
33611         var w = size.width;
33612         var h = size.height;
33613         var centerW = w;
33614         var centerH = h;
33615         var centerY = 0;
33616         var centerX = 0;
33617         //var x = 0, y = 0;
33618
33619         var rs = this.regions;
33620         var north = rs["north"];
33621         var south = rs["south"]; 
33622         var west = rs["west"];
33623         var east = rs["east"];
33624         var center = rs["center"];
33625         //if(this.hideOnLayout){ // not supported anymore
33626             //c.el.setStyle("display", "none");
33627         //}
33628         if(north && north.isVisible()){
33629             var b = north.getBox();
33630             var m = north.getMargins();
33631             b.width = w - (m.left+m.right);
33632             b.x = m.left;
33633             b.y = m.top;
33634             centerY = b.height + b.y + m.bottom;
33635             centerH -= centerY;
33636             north.updateBox(this.safeBox(b));
33637         }
33638         if(south && south.isVisible()){
33639             var b = south.getBox();
33640             var m = south.getMargins();
33641             b.width = w - (m.left+m.right);
33642             b.x = m.left;
33643             var totalHeight = (b.height + m.top + m.bottom);
33644             b.y = h - totalHeight + m.top;
33645             centerH -= totalHeight;
33646             south.updateBox(this.safeBox(b));
33647         }
33648         if(west && west.isVisible()){
33649             var b = west.getBox();
33650             var m = west.getMargins();
33651             b.height = centerH - (m.top+m.bottom);
33652             b.x = m.left;
33653             b.y = centerY + m.top;
33654             var totalWidth = (b.width + m.left + m.right);
33655             centerX += totalWidth;
33656             centerW -= totalWidth;
33657             west.updateBox(this.safeBox(b));
33658         }
33659         if(east && east.isVisible()){
33660             var b = east.getBox();
33661             var m = east.getMargins();
33662             b.height = centerH - (m.top+m.bottom);
33663             var totalWidth = (b.width + m.left + m.right);
33664             b.x = w - totalWidth + m.left;
33665             b.y = centerY + m.top;
33666             centerW -= totalWidth;
33667             east.updateBox(this.safeBox(b));
33668         }
33669         if(center){
33670             var m = center.getMargins();
33671             var centerBox = {
33672                 x: centerX + m.left,
33673                 y: centerY + m.top,
33674                 width: centerW - (m.left+m.right),
33675                 height: centerH - (m.top+m.bottom)
33676             };
33677             //if(this.hideOnLayout){
33678                 //center.el.setStyle("display", "block");
33679             //}
33680             center.updateBox(this.safeBox(centerBox));
33681         }
33682         this.el.repaint();
33683         this.fireEvent("layout", this);
33684     },
33685
33686     // private
33687     safeBox : function(box){
33688         box.width = Math.max(0, box.width);
33689         box.height = Math.max(0, box.height);
33690         return box;
33691     },
33692
33693     /**
33694      * Adds a ContentPanel (or subclass) to this layout.
33695      * @param {String} target The target region key (north, south, east, west or center).
33696      * @param {Roo.ContentPanel} panel The panel to add
33697      * @return {Roo.ContentPanel} The added panel
33698      */
33699     add : function(target, panel){
33700          
33701         target = target.toLowerCase();
33702         return this.regions[target].add(panel);
33703     },
33704
33705     /**
33706      * Remove a ContentPanel (or subclass) to this layout.
33707      * @param {String} target The target region key (north, south, east, west or center).
33708      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33709      * @return {Roo.ContentPanel} The removed panel
33710      */
33711     remove : function(target, panel){
33712         target = target.toLowerCase();
33713         return this.regions[target].remove(panel);
33714     },
33715
33716     /**
33717      * Searches all regions for a panel with the specified id
33718      * @param {String} panelId
33719      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33720      */
33721     findPanel : function(panelId){
33722         var rs = this.regions;
33723         for(var target in rs){
33724             if(typeof rs[target] != "function"){
33725                 var p = rs[target].getPanel(panelId);
33726                 if(p){
33727                     return p;
33728                 }
33729             }
33730         }
33731         return null;
33732     },
33733
33734     /**
33735      * Searches all regions for a panel with the specified id and activates (shows) it.
33736      * @param {String/ContentPanel} panelId The panels id or the panel itself
33737      * @return {Roo.ContentPanel} The shown panel or null
33738      */
33739     showPanel : function(panelId) {
33740       var rs = this.regions;
33741       for(var target in rs){
33742          var r = rs[target];
33743          if(typeof r != "function"){
33744             if(r.hasPanel(panelId)){
33745                return r.showPanel(panelId);
33746             }
33747          }
33748       }
33749       return null;
33750    },
33751
33752    /**
33753      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33754      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33755      */
33756     restoreState : function(provider){
33757         if(!provider){
33758             provider = Roo.state.Manager;
33759         }
33760         var sm = new Roo.LayoutStateManager();
33761         sm.init(this, provider);
33762     },
33763
33764     /**
33765      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33766      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33767      * a valid ContentPanel config object.  Example:
33768      * <pre><code>
33769 // Create the main layout
33770 var layout = new Roo.BorderLayout('main-ct', {
33771     west: {
33772         split:true,
33773         minSize: 175,
33774         titlebar: true
33775     },
33776     center: {
33777         title:'Components'
33778     }
33779 }, 'main-ct');
33780
33781 // Create and add multiple ContentPanels at once via configs
33782 layout.batchAdd({
33783    west: {
33784        id: 'source-files',
33785        autoCreate:true,
33786        title:'Ext Source Files',
33787        autoScroll:true,
33788        fitToFrame:true
33789    },
33790    center : {
33791        el: cview,
33792        autoScroll:true,
33793        fitToFrame:true,
33794        toolbar: tb,
33795        resizeEl:'cbody'
33796    }
33797 });
33798 </code></pre>
33799      * @param {Object} regions An object containing ContentPanel configs by region name
33800      */
33801     batchAdd : function(regions){
33802         this.beginUpdate();
33803         for(var rname in regions){
33804             var lr = this.regions[rname];
33805             if(lr){
33806                 this.addTypedPanels(lr, regions[rname]);
33807             }
33808         }
33809         this.endUpdate();
33810     },
33811
33812     // private
33813     addTypedPanels : function(lr, ps){
33814         if(typeof ps == 'string'){
33815             lr.add(new Roo.ContentPanel(ps));
33816         }
33817         else if(ps instanceof Array){
33818             for(var i =0, len = ps.length; i < len; i++){
33819                 this.addTypedPanels(lr, ps[i]);
33820             }
33821         }
33822         else if(!ps.events){ // raw config?
33823             var el = ps.el;
33824             delete ps.el; // prevent conflict
33825             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33826         }
33827         else {  // panel object assumed!
33828             lr.add(ps);
33829         }
33830     },
33831     /**
33832      * Adds a xtype elements to the layout.
33833      * <pre><code>
33834
33835 layout.addxtype({
33836        xtype : 'ContentPanel',
33837        region: 'west',
33838        items: [ .... ]
33839    }
33840 );
33841
33842 layout.addxtype({
33843         xtype : 'NestedLayoutPanel',
33844         region: 'west',
33845         layout: {
33846            center: { },
33847            west: { }   
33848         },
33849         items : [ ... list of content panels or nested layout panels.. ]
33850    }
33851 );
33852 </code></pre>
33853      * @param {Object} cfg Xtype definition of item to add.
33854      */
33855     addxtype : function(cfg)
33856     {
33857         // basically accepts a pannel...
33858         // can accept a layout region..!?!?
33859         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33860         
33861         if (!cfg.xtype.match(/Panel$/)) {
33862             return false;
33863         }
33864         var ret = false;
33865         
33866         if (typeof(cfg.region) == 'undefined') {
33867             Roo.log("Failed to add Panel, region was not set");
33868             Roo.log(cfg);
33869             return false;
33870         }
33871         var region = cfg.region;
33872         delete cfg.region;
33873         
33874           
33875         var xitems = [];
33876         if (cfg.items) {
33877             xitems = cfg.items;
33878             delete cfg.items;
33879         }
33880         var nb = false;
33881         
33882         switch(cfg.xtype) 
33883         {
33884             case 'ContentPanel':  // ContentPanel (el, cfg)
33885             case 'ScrollPanel':  // ContentPanel (el, cfg)
33886             case 'ViewPanel': 
33887                 if(cfg.autoCreate) {
33888                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33889                 } else {
33890                     var el = this.el.createChild();
33891                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33892                 }
33893                 
33894                 this.add(region, ret);
33895                 break;
33896             
33897             
33898             case 'TreePanel': // our new panel!
33899                 cfg.el = this.el.createChild();
33900                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33901                 this.add(region, ret);
33902                 break;
33903             
33904             case 'NestedLayoutPanel': 
33905                 // create a new Layout (which is  a Border Layout...
33906                 var el = this.el.createChild();
33907                 var clayout = cfg.layout;
33908                 delete cfg.layout;
33909                 clayout.items   = clayout.items  || [];
33910                 // replace this exitems with the clayout ones..
33911                 xitems = clayout.items;
33912                  
33913                 
33914                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33915                     cfg.background = false;
33916                 }
33917                 var layout = new Roo.BorderLayout(el, clayout);
33918                 
33919                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33920                 //console.log('adding nested layout panel '  + cfg.toSource());
33921                 this.add(region, ret);
33922                 nb = {}; /// find first...
33923                 break;
33924                 
33925             case 'GridPanel': 
33926             
33927                 // needs grid and region
33928                 
33929                 //var el = this.getRegion(region).el.createChild();
33930                 var el = this.el.createChild();
33931                 // create the grid first...
33932                 
33933                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33934                 delete cfg.grid;
33935                 if (region == 'center' && this.active ) {
33936                     cfg.background = false;
33937                 }
33938                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33939                 
33940                 this.add(region, ret);
33941                 if (cfg.background) {
33942                     ret.on('activate', function(gp) {
33943                         if (!gp.grid.rendered) {
33944                             gp.grid.render();
33945                         }
33946                     });
33947                 } else {
33948                     grid.render();
33949                 }
33950                 break;
33951            
33952            
33953            
33954                 
33955                 
33956                 
33957             default:
33958                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33959                     
33960                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33961                     this.add(region, ret);
33962                 } else {
33963                 
33964                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33965                     return null;
33966                 }
33967                 
33968              // GridPanel (grid, cfg)
33969             
33970         }
33971         this.beginUpdate();
33972         // add children..
33973         var region = '';
33974         var abn = {};
33975         Roo.each(xitems, function(i)  {
33976             region = nb && i.region ? i.region : false;
33977             
33978             var add = ret.addxtype(i);
33979            
33980             if (region) {
33981                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33982                 if (!i.background) {
33983                     abn[region] = nb[region] ;
33984                 }
33985             }
33986             
33987         });
33988         this.endUpdate();
33989
33990         // make the last non-background panel active..
33991         //if (nb) { Roo.log(abn); }
33992         if (nb) {
33993             
33994             for(var r in abn) {
33995                 region = this.getRegion(r);
33996                 if (region) {
33997                     // tried using nb[r], but it does not work..
33998                      
33999                     region.showPanel(abn[r]);
34000                    
34001                 }
34002             }
34003         }
34004         return ret;
34005         
34006     }
34007 });
34008
34009 /**
34010  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34011  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34012  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34013  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34014  * <pre><code>
34015 // shorthand
34016 var CP = Roo.ContentPanel;
34017
34018 var layout = Roo.BorderLayout.create({
34019     north: {
34020         initialSize: 25,
34021         titlebar: false,
34022         panels: [new CP("north", "North")]
34023     },
34024     west: {
34025         split:true,
34026         initialSize: 200,
34027         minSize: 175,
34028         maxSize: 400,
34029         titlebar: true,
34030         collapsible: true,
34031         panels: [new CP("west", {title: "West"})]
34032     },
34033     east: {
34034         split:true,
34035         initialSize: 202,
34036         minSize: 175,
34037         maxSize: 400,
34038         titlebar: true,
34039         collapsible: true,
34040         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34041     },
34042     south: {
34043         split:true,
34044         initialSize: 100,
34045         minSize: 100,
34046         maxSize: 200,
34047         titlebar: true,
34048         collapsible: true,
34049         panels: [new CP("south", {title: "South", closable: true})]
34050     },
34051     center: {
34052         titlebar: true,
34053         autoScroll:true,
34054         resizeTabs: true,
34055         minTabWidth: 50,
34056         preferredTabWidth: 150,
34057         panels: [
34058             new CP("center1", {title: "Close Me", closable: true}),
34059             new CP("center2", {title: "Center Panel", closable: false})
34060         ]
34061     }
34062 }, document.body);
34063
34064 layout.getRegion("center").showPanel("center1");
34065 </code></pre>
34066  * @param config
34067  * @param targetEl
34068  */
34069 Roo.BorderLayout.create = function(config, targetEl){
34070     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34071     layout.beginUpdate();
34072     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34073     for(var j = 0, jlen = regions.length; j < jlen; j++){
34074         var lr = regions[j];
34075         if(layout.regions[lr] && config[lr].panels){
34076             var r = layout.regions[lr];
34077             var ps = config[lr].panels;
34078             layout.addTypedPanels(r, ps);
34079         }
34080     }
34081     layout.endUpdate();
34082     return layout;
34083 };
34084
34085 // private
34086 Roo.BorderLayout.RegionFactory = {
34087     // private
34088     validRegions : ["north","south","east","west","center"],
34089
34090     // private
34091     create : function(target, mgr, config){
34092         target = target.toLowerCase();
34093         if(config.lightweight || config.basic){
34094             return new Roo.BasicLayoutRegion(mgr, config, target);
34095         }
34096         switch(target){
34097             case "north":
34098                 return new Roo.NorthLayoutRegion(mgr, config);
34099             case "south":
34100                 return new Roo.SouthLayoutRegion(mgr, config);
34101             case "east":
34102                 return new Roo.EastLayoutRegion(mgr, config);
34103             case "west":
34104                 return new Roo.WestLayoutRegion(mgr, config);
34105             case "center":
34106                 return new Roo.CenterLayoutRegion(mgr, config);
34107         }
34108         throw 'Layout region "'+target+'" not supported.';
34109     }
34110 };/*
34111  * Based on:
34112  * Ext JS Library 1.1.1
34113  * Copyright(c) 2006-2007, Ext JS, LLC.
34114  *
34115  * Originally Released Under LGPL - original licence link has changed is not relivant.
34116  *
34117  * Fork - LGPL
34118  * <script type="text/javascript">
34119  */
34120  
34121 /**
34122  * @class Roo.BasicLayoutRegion
34123  * @extends Roo.util.Observable
34124  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34125  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34126  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34127  */
34128 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34129     this.mgr = mgr;
34130     this.position  = pos;
34131     this.events = {
34132         /**
34133          * @scope Roo.BasicLayoutRegion
34134          */
34135         
34136         /**
34137          * @event beforeremove
34138          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34139          * @param {Roo.LayoutRegion} this
34140          * @param {Roo.ContentPanel} panel The panel
34141          * @param {Object} e The cancel event object
34142          */
34143         "beforeremove" : true,
34144         /**
34145          * @event invalidated
34146          * Fires when the layout for this region is changed.
34147          * @param {Roo.LayoutRegion} this
34148          */
34149         "invalidated" : true,
34150         /**
34151          * @event visibilitychange
34152          * Fires when this region is shown or hidden 
34153          * @param {Roo.LayoutRegion} this
34154          * @param {Boolean} visibility true or false
34155          */
34156         "visibilitychange" : true,
34157         /**
34158          * @event paneladded
34159          * Fires when a panel is added. 
34160          * @param {Roo.LayoutRegion} this
34161          * @param {Roo.ContentPanel} panel The panel
34162          */
34163         "paneladded" : true,
34164         /**
34165          * @event panelremoved
34166          * Fires when a panel is removed. 
34167          * @param {Roo.LayoutRegion} this
34168          * @param {Roo.ContentPanel} panel The panel
34169          */
34170         "panelremoved" : true,
34171         /**
34172          * @event beforecollapse
34173          * Fires when this region before collapse.
34174          * @param {Roo.LayoutRegion} this
34175          */
34176         "beforecollapse" : true,
34177         /**
34178          * @event collapsed
34179          * Fires when this region is collapsed.
34180          * @param {Roo.LayoutRegion} this
34181          */
34182         "collapsed" : true,
34183         /**
34184          * @event expanded
34185          * Fires when this region is expanded.
34186          * @param {Roo.LayoutRegion} this
34187          */
34188         "expanded" : true,
34189         /**
34190          * @event slideshow
34191          * Fires when this region is slid into view.
34192          * @param {Roo.LayoutRegion} this
34193          */
34194         "slideshow" : true,
34195         /**
34196          * @event slidehide
34197          * Fires when this region slides out of view. 
34198          * @param {Roo.LayoutRegion} this
34199          */
34200         "slidehide" : true,
34201         /**
34202          * @event panelactivated
34203          * Fires when a panel is activated. 
34204          * @param {Roo.LayoutRegion} this
34205          * @param {Roo.ContentPanel} panel The activated panel
34206          */
34207         "panelactivated" : true,
34208         /**
34209          * @event resized
34210          * Fires when the user resizes this region. 
34211          * @param {Roo.LayoutRegion} this
34212          * @param {Number} newSize The new size (width for east/west, height for north/south)
34213          */
34214         "resized" : true
34215     };
34216     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34217     this.panels = new Roo.util.MixedCollection();
34218     this.panels.getKey = this.getPanelId.createDelegate(this);
34219     this.box = null;
34220     this.activePanel = null;
34221     // ensure listeners are added...
34222     
34223     if (config.listeners || config.events) {
34224         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34225             listeners : config.listeners || {},
34226             events : config.events || {}
34227         });
34228     }
34229     
34230     if(skipConfig !== true){
34231         this.applyConfig(config);
34232     }
34233 };
34234
34235 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34236     getPanelId : function(p){
34237         return p.getId();
34238     },
34239     
34240     applyConfig : function(config){
34241         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34242         this.config = config;
34243         
34244     },
34245     
34246     /**
34247      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34248      * the width, for horizontal (north, south) the height.
34249      * @param {Number} newSize The new width or height
34250      */
34251     resizeTo : function(newSize){
34252         var el = this.el ? this.el :
34253                  (this.activePanel ? this.activePanel.getEl() : null);
34254         if(el){
34255             switch(this.position){
34256                 case "east":
34257                 case "west":
34258                     el.setWidth(newSize);
34259                     this.fireEvent("resized", this, newSize);
34260                 break;
34261                 case "north":
34262                 case "south":
34263                     el.setHeight(newSize);
34264                     this.fireEvent("resized", this, newSize);
34265                 break;                
34266             }
34267         }
34268     },
34269     
34270     getBox : function(){
34271         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34272     },
34273     
34274     getMargins : function(){
34275         return this.margins;
34276     },
34277     
34278     updateBox : function(box){
34279         this.box = box;
34280         var el = this.activePanel.getEl();
34281         el.dom.style.left = box.x + "px";
34282         el.dom.style.top = box.y + "px";
34283         this.activePanel.setSize(box.width, box.height);
34284     },
34285     
34286     /**
34287      * Returns the container element for this region.
34288      * @return {Roo.Element}
34289      */
34290     getEl : function(){
34291         return this.activePanel;
34292     },
34293     
34294     /**
34295      * Returns true if this region is currently visible.
34296      * @return {Boolean}
34297      */
34298     isVisible : function(){
34299         return this.activePanel ? true : false;
34300     },
34301     
34302     setActivePanel : function(panel){
34303         panel = this.getPanel(panel);
34304         if(this.activePanel && this.activePanel != panel){
34305             this.activePanel.setActiveState(false);
34306             this.activePanel.getEl().setLeftTop(-10000,-10000);
34307         }
34308         this.activePanel = panel;
34309         panel.setActiveState(true);
34310         if(this.box){
34311             panel.setSize(this.box.width, this.box.height);
34312         }
34313         this.fireEvent("panelactivated", this, panel);
34314         this.fireEvent("invalidated");
34315     },
34316     
34317     /**
34318      * Show the specified panel.
34319      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34320      * @return {Roo.ContentPanel} The shown panel or null
34321      */
34322     showPanel : function(panel){
34323         if(panel = this.getPanel(panel)){
34324             this.setActivePanel(panel);
34325         }
34326         return panel;
34327     },
34328     
34329     /**
34330      * Get the active panel for this region.
34331      * @return {Roo.ContentPanel} The active panel or null
34332      */
34333     getActivePanel : function(){
34334         return this.activePanel;
34335     },
34336     
34337     /**
34338      * Add the passed ContentPanel(s)
34339      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34340      * @return {Roo.ContentPanel} The panel added (if only one was added)
34341      */
34342     add : function(panel){
34343         if(arguments.length > 1){
34344             for(var i = 0, len = arguments.length; i < len; i++) {
34345                 this.add(arguments[i]);
34346             }
34347             return null;
34348         }
34349         if(this.hasPanel(panel)){
34350             this.showPanel(panel);
34351             return panel;
34352         }
34353         var el = panel.getEl();
34354         if(el.dom.parentNode != this.mgr.el.dom){
34355             this.mgr.el.dom.appendChild(el.dom);
34356         }
34357         if(panel.setRegion){
34358             panel.setRegion(this);
34359         }
34360         this.panels.add(panel);
34361         el.setStyle("position", "absolute");
34362         if(!panel.background){
34363             this.setActivePanel(panel);
34364             if(this.config.initialSize && this.panels.getCount()==1){
34365                 this.resizeTo(this.config.initialSize);
34366             }
34367         }
34368         this.fireEvent("paneladded", this, panel);
34369         return panel;
34370     },
34371     
34372     /**
34373      * Returns true if the panel is in this region.
34374      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34375      * @return {Boolean}
34376      */
34377     hasPanel : function(panel){
34378         if(typeof panel == "object"){ // must be panel obj
34379             panel = panel.getId();
34380         }
34381         return this.getPanel(panel) ? true : false;
34382     },
34383     
34384     /**
34385      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34386      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34387      * @param {Boolean} preservePanel Overrides the config preservePanel option
34388      * @return {Roo.ContentPanel} The panel that was removed
34389      */
34390     remove : function(panel, preservePanel){
34391         panel = this.getPanel(panel);
34392         if(!panel){
34393             return null;
34394         }
34395         var e = {};
34396         this.fireEvent("beforeremove", this, panel, e);
34397         if(e.cancel === true){
34398             return null;
34399         }
34400         var panelId = panel.getId();
34401         this.panels.removeKey(panelId);
34402         return panel;
34403     },
34404     
34405     /**
34406      * Returns the panel specified or null if it's not in this region.
34407      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34408      * @return {Roo.ContentPanel}
34409      */
34410     getPanel : function(id){
34411         if(typeof id == "object"){ // must be panel obj
34412             return id;
34413         }
34414         return this.panels.get(id);
34415     },
34416     
34417     /**
34418      * Returns this regions position (north/south/east/west/center).
34419      * @return {String} 
34420      */
34421     getPosition: function(){
34422         return this.position;    
34423     }
34424 });/*
34425  * Based on:
34426  * Ext JS Library 1.1.1
34427  * Copyright(c) 2006-2007, Ext JS, LLC.
34428  *
34429  * Originally Released Under LGPL - original licence link has changed is not relivant.
34430  *
34431  * Fork - LGPL
34432  * <script type="text/javascript">
34433  */
34434  
34435 /**
34436  * @class Roo.LayoutRegion
34437  * @extends Roo.BasicLayoutRegion
34438  * This class represents a region in a layout manager.
34439  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34440  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34441  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34442  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34443  * @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})
34444  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34445  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34446  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34447  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34448  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34449  * @cfg {String}    title           The title for the region (overrides panel titles)
34450  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34451  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34452  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34453  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34454  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34455  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34456  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34457  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34458  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34459  * @cfg {Boolean}   showPin         True to show a pin button
34460  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34461  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34462  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34463  * @cfg {Number}    width           For East/West panels
34464  * @cfg {Number}    height          For North/South panels
34465  * @cfg {Boolean}   split           To show the splitter
34466  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34467  */
34468 Roo.LayoutRegion = function(mgr, config, pos){
34469     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34470     var dh = Roo.DomHelper;
34471     /** This region's container element 
34472     * @type Roo.Element */
34473     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34474     /** This region's title element 
34475     * @type Roo.Element */
34476
34477     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34478         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34479         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34480     ]}, true);
34481     this.titleEl.enableDisplayMode();
34482     /** This region's title text element 
34483     * @type HTMLElement */
34484     this.titleTextEl = this.titleEl.dom.firstChild;
34485     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34486     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34487     this.closeBtn.enableDisplayMode();
34488     this.closeBtn.on("click", this.closeClicked, this);
34489     this.closeBtn.hide();
34490
34491     this.createBody(config);
34492     this.visible = true;
34493     this.collapsed = false;
34494
34495     if(config.hideWhenEmpty){
34496         this.hide();
34497         this.on("paneladded", this.validateVisibility, this);
34498         this.on("panelremoved", this.validateVisibility, this);
34499     }
34500     this.applyConfig(config);
34501 };
34502
34503 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34504
34505     createBody : function(){
34506         /** This region's body element 
34507         * @type Roo.Element */
34508         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34509     },
34510
34511     applyConfig : function(c){
34512         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34513             var dh = Roo.DomHelper;
34514             if(c.titlebar !== false){
34515                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34516                 this.collapseBtn.on("click", this.collapse, this);
34517                 this.collapseBtn.enableDisplayMode();
34518
34519                 if(c.showPin === true || this.showPin){
34520                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34521                     this.stickBtn.enableDisplayMode();
34522                     this.stickBtn.on("click", this.expand, this);
34523                     this.stickBtn.hide();
34524                 }
34525             }
34526             /** This region's collapsed element
34527             * @type Roo.Element */
34528             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34529                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34530             ]}, true);
34531             if(c.floatable !== false){
34532                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34533                this.collapsedEl.on("click", this.collapseClick, this);
34534             }
34535
34536             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34537                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34538                    id: "message", unselectable: "on", style:{"float":"left"}});
34539                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34540              }
34541             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34542             this.expandBtn.on("click", this.expand, this);
34543         }
34544         if(this.collapseBtn){
34545             this.collapseBtn.setVisible(c.collapsible == true);
34546         }
34547         this.cmargins = c.cmargins || this.cmargins ||
34548                          (this.position == "west" || this.position == "east" ?
34549                              {top: 0, left: 2, right:2, bottom: 0} :
34550                              {top: 2, left: 0, right:0, bottom: 2});
34551         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34552         this.bottomTabs = c.tabPosition != "top";
34553         this.autoScroll = c.autoScroll || false;
34554         if(this.autoScroll){
34555             this.bodyEl.setStyle("overflow", "auto");
34556         }else{
34557             this.bodyEl.setStyle("overflow", "hidden");
34558         }
34559         //if(c.titlebar !== false){
34560             if((!c.titlebar && !c.title) || c.titlebar === false){
34561                 this.titleEl.hide();
34562             }else{
34563                 this.titleEl.show();
34564                 if(c.title){
34565                     this.titleTextEl.innerHTML = c.title;
34566                 }
34567             }
34568         //}
34569         this.duration = c.duration || .30;
34570         this.slideDuration = c.slideDuration || .45;
34571         this.config = c;
34572         if(c.collapsed){
34573             this.collapse(true);
34574         }
34575         if(c.hidden){
34576             this.hide();
34577         }
34578     },
34579     /**
34580      * Returns true if this region is currently visible.
34581      * @return {Boolean}
34582      */
34583     isVisible : function(){
34584         return this.visible;
34585     },
34586
34587     /**
34588      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34589      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34590      */
34591     setCollapsedTitle : function(title){
34592         title = title || "&#160;";
34593         if(this.collapsedTitleTextEl){
34594             this.collapsedTitleTextEl.innerHTML = title;
34595         }
34596     },
34597
34598     getBox : function(){
34599         var b;
34600         if(!this.collapsed){
34601             b = this.el.getBox(false, true);
34602         }else{
34603             b = this.collapsedEl.getBox(false, true);
34604         }
34605         return b;
34606     },
34607
34608     getMargins : function(){
34609         return this.collapsed ? this.cmargins : this.margins;
34610     },
34611
34612     highlight : function(){
34613         this.el.addClass("x-layout-panel-dragover");
34614     },
34615
34616     unhighlight : function(){
34617         this.el.removeClass("x-layout-panel-dragover");
34618     },
34619
34620     updateBox : function(box){
34621         this.box = box;
34622         if(!this.collapsed){
34623             this.el.dom.style.left = box.x + "px";
34624             this.el.dom.style.top = box.y + "px";
34625             this.updateBody(box.width, box.height);
34626         }else{
34627             this.collapsedEl.dom.style.left = box.x + "px";
34628             this.collapsedEl.dom.style.top = box.y + "px";
34629             this.collapsedEl.setSize(box.width, box.height);
34630         }
34631         if(this.tabs){
34632             this.tabs.autoSizeTabs();
34633         }
34634     },
34635
34636     updateBody : function(w, h){
34637         if(w !== null){
34638             this.el.setWidth(w);
34639             w -= this.el.getBorderWidth("rl");
34640             if(this.config.adjustments){
34641                 w += this.config.adjustments[0];
34642             }
34643         }
34644         if(h !== null){
34645             this.el.setHeight(h);
34646             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34647             h -= this.el.getBorderWidth("tb");
34648             if(this.config.adjustments){
34649                 h += this.config.adjustments[1];
34650             }
34651             this.bodyEl.setHeight(h);
34652             if(this.tabs){
34653                 h = this.tabs.syncHeight(h);
34654             }
34655         }
34656         if(this.panelSize){
34657             w = w !== null ? w : this.panelSize.width;
34658             h = h !== null ? h : this.panelSize.height;
34659         }
34660         if(this.activePanel){
34661             var el = this.activePanel.getEl();
34662             w = w !== null ? w : el.getWidth();
34663             h = h !== null ? h : el.getHeight();
34664             this.panelSize = {width: w, height: h};
34665             this.activePanel.setSize(w, h);
34666         }
34667         if(Roo.isIE && this.tabs){
34668             this.tabs.el.repaint();
34669         }
34670     },
34671
34672     /**
34673      * Returns the container element for this region.
34674      * @return {Roo.Element}
34675      */
34676     getEl : function(){
34677         return this.el;
34678     },
34679
34680     /**
34681      * Hides this region.
34682      */
34683     hide : function(){
34684         if(!this.collapsed){
34685             this.el.dom.style.left = "-2000px";
34686             this.el.hide();
34687         }else{
34688             this.collapsedEl.dom.style.left = "-2000px";
34689             this.collapsedEl.hide();
34690         }
34691         this.visible = false;
34692         this.fireEvent("visibilitychange", this, false);
34693     },
34694
34695     /**
34696      * Shows this region if it was previously hidden.
34697      */
34698     show : function(){
34699         if(!this.collapsed){
34700             this.el.show();
34701         }else{
34702             this.collapsedEl.show();
34703         }
34704         this.visible = true;
34705         this.fireEvent("visibilitychange", this, true);
34706     },
34707
34708     closeClicked : function(){
34709         if(this.activePanel){
34710             this.remove(this.activePanel);
34711         }
34712     },
34713
34714     collapseClick : function(e){
34715         if(this.isSlid){
34716            e.stopPropagation();
34717            this.slideIn();
34718         }else{
34719            e.stopPropagation();
34720            this.slideOut();
34721         }
34722     },
34723
34724     /**
34725      * Collapses this region.
34726      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34727      */
34728     collapse : function(skipAnim, skipCheck){
34729         if(this.collapsed) {
34730             return;
34731         }
34732         
34733         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34734             
34735             this.collapsed = true;
34736             if(this.split){
34737                 this.split.el.hide();
34738             }
34739             if(this.config.animate && skipAnim !== true){
34740                 this.fireEvent("invalidated", this);
34741                 this.animateCollapse();
34742             }else{
34743                 this.el.setLocation(-20000,-20000);
34744                 this.el.hide();
34745                 this.collapsedEl.show();
34746                 this.fireEvent("collapsed", this);
34747                 this.fireEvent("invalidated", this);
34748             }
34749         }
34750         
34751     },
34752
34753     animateCollapse : function(){
34754         // overridden
34755     },
34756
34757     /**
34758      * Expands this region if it was previously collapsed.
34759      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34760      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34761      */
34762     expand : function(e, skipAnim){
34763         if(e) {
34764             e.stopPropagation();
34765         }
34766         if(!this.collapsed || this.el.hasActiveFx()) {
34767             return;
34768         }
34769         if(this.isSlid){
34770             this.afterSlideIn();
34771             skipAnim = true;
34772         }
34773         this.collapsed = false;
34774         if(this.config.animate && skipAnim !== true){
34775             this.animateExpand();
34776         }else{
34777             this.el.show();
34778             if(this.split){
34779                 this.split.el.show();
34780             }
34781             this.collapsedEl.setLocation(-2000,-2000);
34782             this.collapsedEl.hide();
34783             this.fireEvent("invalidated", this);
34784             this.fireEvent("expanded", this);
34785         }
34786     },
34787
34788     animateExpand : function(){
34789         // overridden
34790     },
34791
34792     initTabs : function()
34793     {
34794         this.bodyEl.setStyle("overflow", "hidden");
34795         var ts = new Roo.TabPanel(
34796                 this.bodyEl.dom,
34797                 {
34798                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34799                     disableTooltips: this.config.disableTabTips,
34800                     toolbar : this.config.toolbar
34801                 }
34802         );
34803         if(this.config.hideTabs){
34804             ts.stripWrap.setDisplayed(false);
34805         }
34806         this.tabs = ts;
34807         ts.resizeTabs = this.config.resizeTabs === true;
34808         ts.minTabWidth = this.config.minTabWidth || 40;
34809         ts.maxTabWidth = this.config.maxTabWidth || 250;
34810         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34811         ts.monitorResize = false;
34812         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34813         ts.bodyEl.addClass('x-layout-tabs-body');
34814         this.panels.each(this.initPanelAsTab, this);
34815     },
34816
34817     initPanelAsTab : function(panel){
34818         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34819                     this.config.closeOnTab && panel.isClosable());
34820         if(panel.tabTip !== undefined){
34821             ti.setTooltip(panel.tabTip);
34822         }
34823         ti.on("activate", function(){
34824               this.setActivePanel(panel);
34825         }, this);
34826         if(this.config.closeOnTab){
34827             ti.on("beforeclose", function(t, e){
34828                 e.cancel = true;
34829                 this.remove(panel);
34830             }, this);
34831         }
34832         return ti;
34833     },
34834
34835     updatePanelTitle : function(panel, title){
34836         if(this.activePanel == panel){
34837             this.updateTitle(title);
34838         }
34839         if(this.tabs){
34840             var ti = this.tabs.getTab(panel.getEl().id);
34841             ti.setText(title);
34842             if(panel.tabTip !== undefined){
34843                 ti.setTooltip(panel.tabTip);
34844             }
34845         }
34846     },
34847
34848     updateTitle : function(title){
34849         if(this.titleTextEl && !this.config.title){
34850             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34851         }
34852     },
34853
34854     setActivePanel : function(panel){
34855         panel = this.getPanel(panel);
34856         if(this.activePanel && this.activePanel != panel){
34857             this.activePanel.setActiveState(false);
34858         }
34859         this.activePanel = panel;
34860         panel.setActiveState(true);
34861         if(this.panelSize){
34862             panel.setSize(this.panelSize.width, this.panelSize.height);
34863         }
34864         if(this.closeBtn){
34865             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34866         }
34867         this.updateTitle(panel.getTitle());
34868         if(this.tabs){
34869             this.fireEvent("invalidated", this);
34870         }
34871         this.fireEvent("panelactivated", this, panel);
34872     },
34873
34874     /**
34875      * Shows the specified panel.
34876      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34877      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34878      */
34879     showPanel : function(panel)
34880     {
34881         panel = this.getPanel(panel);
34882         if(panel){
34883             if(this.tabs){
34884                 var tab = this.tabs.getTab(panel.getEl().id);
34885                 if(tab.isHidden()){
34886                     this.tabs.unhideTab(tab.id);
34887                 }
34888                 tab.activate();
34889             }else{
34890                 this.setActivePanel(panel);
34891             }
34892         }
34893         return panel;
34894     },
34895
34896     /**
34897      * Get the active panel for this region.
34898      * @return {Roo.ContentPanel} The active panel or null
34899      */
34900     getActivePanel : function(){
34901         return this.activePanel;
34902     },
34903
34904     validateVisibility : function(){
34905         if(this.panels.getCount() < 1){
34906             this.updateTitle("&#160;");
34907             this.closeBtn.hide();
34908             this.hide();
34909         }else{
34910             if(!this.isVisible()){
34911                 this.show();
34912             }
34913         }
34914     },
34915
34916     /**
34917      * Adds the passed ContentPanel(s) to this region.
34918      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34919      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34920      */
34921     add : function(panel){
34922         if(arguments.length > 1){
34923             for(var i = 0, len = arguments.length; i < len; i++) {
34924                 this.add(arguments[i]);
34925             }
34926             return null;
34927         }
34928         if(this.hasPanel(panel)){
34929             this.showPanel(panel);
34930             return panel;
34931         }
34932         panel.setRegion(this);
34933         this.panels.add(panel);
34934         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34935             this.bodyEl.dom.appendChild(panel.getEl().dom);
34936             if(panel.background !== true){
34937                 this.setActivePanel(panel);
34938             }
34939             this.fireEvent("paneladded", this, panel);
34940             return panel;
34941         }
34942         if(!this.tabs){
34943             this.initTabs();
34944         }else{
34945             this.initPanelAsTab(panel);
34946         }
34947         if(panel.background !== true){
34948             this.tabs.activate(panel.getEl().id);
34949         }
34950         this.fireEvent("paneladded", this, panel);
34951         return panel;
34952     },
34953
34954     /**
34955      * Hides the tab for the specified panel.
34956      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34957      */
34958     hidePanel : function(panel){
34959         if(this.tabs && (panel = this.getPanel(panel))){
34960             this.tabs.hideTab(panel.getEl().id);
34961         }
34962     },
34963
34964     /**
34965      * Unhides the tab for a previously hidden panel.
34966      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34967      */
34968     unhidePanel : function(panel){
34969         if(this.tabs && (panel = this.getPanel(panel))){
34970             this.tabs.unhideTab(panel.getEl().id);
34971         }
34972     },
34973
34974     clearPanels : function(){
34975         while(this.panels.getCount() > 0){
34976              this.remove(this.panels.first());
34977         }
34978     },
34979
34980     /**
34981      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34982      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34983      * @param {Boolean} preservePanel Overrides the config preservePanel option
34984      * @return {Roo.ContentPanel} The panel that was removed
34985      */
34986     remove : function(panel, preservePanel){
34987         panel = this.getPanel(panel);
34988         if(!panel){
34989             return null;
34990         }
34991         var e = {};
34992         this.fireEvent("beforeremove", this, panel, e);
34993         if(e.cancel === true){
34994             return null;
34995         }
34996         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34997         var panelId = panel.getId();
34998         this.panels.removeKey(panelId);
34999         if(preservePanel){
35000             document.body.appendChild(panel.getEl().dom);
35001         }
35002         if(this.tabs){
35003             this.tabs.removeTab(panel.getEl().id);
35004         }else if (!preservePanel){
35005             this.bodyEl.dom.removeChild(panel.getEl().dom);
35006         }
35007         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35008             var p = this.panels.first();
35009             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35010             tempEl.appendChild(p.getEl().dom);
35011             this.bodyEl.update("");
35012             this.bodyEl.dom.appendChild(p.getEl().dom);
35013             tempEl = null;
35014             this.updateTitle(p.getTitle());
35015             this.tabs = null;
35016             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35017             this.setActivePanel(p);
35018         }
35019         panel.setRegion(null);
35020         if(this.activePanel == panel){
35021             this.activePanel = null;
35022         }
35023         if(this.config.autoDestroy !== false && preservePanel !== true){
35024             try{panel.destroy();}catch(e){}
35025         }
35026         this.fireEvent("panelremoved", this, panel);
35027         return panel;
35028     },
35029
35030     /**
35031      * Returns the TabPanel component used by this region
35032      * @return {Roo.TabPanel}
35033      */
35034     getTabs : function(){
35035         return this.tabs;
35036     },
35037
35038     createTool : function(parentEl, className){
35039         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35040             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35041         btn.addClassOnOver("x-layout-tools-button-over");
35042         return btn;
35043     }
35044 });/*
35045  * Based on:
35046  * Ext JS Library 1.1.1
35047  * Copyright(c) 2006-2007, Ext JS, LLC.
35048  *
35049  * Originally Released Under LGPL - original licence link has changed is not relivant.
35050  *
35051  * Fork - LGPL
35052  * <script type="text/javascript">
35053  */
35054  
35055
35056
35057 /**
35058  * @class Roo.SplitLayoutRegion
35059  * @extends Roo.LayoutRegion
35060  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35061  */
35062 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35063     this.cursor = cursor;
35064     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35065 };
35066
35067 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35068     splitTip : "Drag to resize.",
35069     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35070     useSplitTips : false,
35071
35072     applyConfig : function(config){
35073         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35074         if(config.split){
35075             if(!this.split){
35076                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35077                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35078                 /** The SplitBar for this region 
35079                 * @type Roo.SplitBar */
35080                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35081                 this.split.on("moved", this.onSplitMove, this);
35082                 this.split.useShim = config.useShim === true;
35083                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35084                 if(this.useSplitTips){
35085                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35086                 }
35087                 if(config.collapsible){
35088                     this.split.el.on("dblclick", this.collapse,  this);
35089                 }
35090             }
35091             if(typeof config.minSize != "undefined"){
35092                 this.split.minSize = config.minSize;
35093             }
35094             if(typeof config.maxSize != "undefined"){
35095                 this.split.maxSize = config.maxSize;
35096             }
35097             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35098                 this.hideSplitter();
35099             }
35100         }
35101     },
35102
35103     getHMaxSize : function(){
35104          var cmax = this.config.maxSize || 10000;
35105          var center = this.mgr.getRegion("center");
35106          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35107     },
35108
35109     getVMaxSize : function(){
35110          var cmax = this.config.maxSize || 10000;
35111          var center = this.mgr.getRegion("center");
35112          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35113     },
35114
35115     onSplitMove : function(split, newSize){
35116         this.fireEvent("resized", this, newSize);
35117     },
35118     
35119     /** 
35120      * Returns the {@link Roo.SplitBar} for this region.
35121      * @return {Roo.SplitBar}
35122      */
35123     getSplitBar : function(){
35124         return this.split;
35125     },
35126     
35127     hide : function(){
35128         this.hideSplitter();
35129         Roo.SplitLayoutRegion.superclass.hide.call(this);
35130     },
35131
35132     hideSplitter : function(){
35133         if(this.split){
35134             this.split.el.setLocation(-2000,-2000);
35135             this.split.el.hide();
35136         }
35137     },
35138
35139     show : function(){
35140         if(this.split){
35141             this.split.el.show();
35142         }
35143         Roo.SplitLayoutRegion.superclass.show.call(this);
35144     },
35145     
35146     beforeSlide: function(){
35147         if(Roo.isGecko){// firefox overflow auto bug workaround
35148             this.bodyEl.clip();
35149             if(this.tabs) {
35150                 this.tabs.bodyEl.clip();
35151             }
35152             if(this.activePanel){
35153                 this.activePanel.getEl().clip();
35154                 
35155                 if(this.activePanel.beforeSlide){
35156                     this.activePanel.beforeSlide();
35157                 }
35158             }
35159         }
35160     },
35161     
35162     afterSlide : function(){
35163         if(Roo.isGecko){// firefox overflow auto bug workaround
35164             this.bodyEl.unclip();
35165             if(this.tabs) {
35166                 this.tabs.bodyEl.unclip();
35167             }
35168             if(this.activePanel){
35169                 this.activePanel.getEl().unclip();
35170                 if(this.activePanel.afterSlide){
35171                     this.activePanel.afterSlide();
35172                 }
35173             }
35174         }
35175     },
35176
35177     initAutoHide : function(){
35178         if(this.autoHide !== false){
35179             if(!this.autoHideHd){
35180                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35181                 this.autoHideHd = {
35182                     "mouseout": function(e){
35183                         if(!e.within(this.el, true)){
35184                             st.delay(500);
35185                         }
35186                     },
35187                     "mouseover" : function(e){
35188                         st.cancel();
35189                     },
35190                     scope : this
35191                 };
35192             }
35193             this.el.on(this.autoHideHd);
35194         }
35195     },
35196
35197     clearAutoHide : function(){
35198         if(this.autoHide !== false){
35199             this.el.un("mouseout", this.autoHideHd.mouseout);
35200             this.el.un("mouseover", this.autoHideHd.mouseover);
35201         }
35202     },
35203
35204     clearMonitor : function(){
35205         Roo.get(document).un("click", this.slideInIf, this);
35206     },
35207
35208     // these names are backwards but not changed for compat
35209     slideOut : function(){
35210         if(this.isSlid || this.el.hasActiveFx()){
35211             return;
35212         }
35213         this.isSlid = true;
35214         if(this.collapseBtn){
35215             this.collapseBtn.hide();
35216         }
35217         this.closeBtnState = this.closeBtn.getStyle('display');
35218         this.closeBtn.hide();
35219         if(this.stickBtn){
35220             this.stickBtn.show();
35221         }
35222         this.el.show();
35223         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35224         this.beforeSlide();
35225         this.el.setStyle("z-index", 10001);
35226         this.el.slideIn(this.getSlideAnchor(), {
35227             callback: function(){
35228                 this.afterSlide();
35229                 this.initAutoHide();
35230                 Roo.get(document).on("click", this.slideInIf, this);
35231                 this.fireEvent("slideshow", this);
35232             },
35233             scope: this,
35234             block: true
35235         });
35236     },
35237
35238     afterSlideIn : function(){
35239         this.clearAutoHide();
35240         this.isSlid = false;
35241         this.clearMonitor();
35242         this.el.setStyle("z-index", "");
35243         if(this.collapseBtn){
35244             this.collapseBtn.show();
35245         }
35246         this.closeBtn.setStyle('display', this.closeBtnState);
35247         if(this.stickBtn){
35248             this.stickBtn.hide();
35249         }
35250         this.fireEvent("slidehide", this);
35251     },
35252
35253     slideIn : function(cb){
35254         if(!this.isSlid || this.el.hasActiveFx()){
35255             Roo.callback(cb);
35256             return;
35257         }
35258         this.isSlid = false;
35259         this.beforeSlide();
35260         this.el.slideOut(this.getSlideAnchor(), {
35261             callback: function(){
35262                 this.el.setLeftTop(-10000, -10000);
35263                 this.afterSlide();
35264                 this.afterSlideIn();
35265                 Roo.callback(cb);
35266             },
35267             scope: this,
35268             block: true
35269         });
35270     },
35271     
35272     slideInIf : function(e){
35273         if(!e.within(this.el)){
35274             this.slideIn();
35275         }
35276     },
35277
35278     animateCollapse : function(){
35279         this.beforeSlide();
35280         this.el.setStyle("z-index", 20000);
35281         var anchor = this.getSlideAnchor();
35282         this.el.slideOut(anchor, {
35283             callback : function(){
35284                 this.el.setStyle("z-index", "");
35285                 this.collapsedEl.slideIn(anchor, {duration:.3});
35286                 this.afterSlide();
35287                 this.el.setLocation(-10000,-10000);
35288                 this.el.hide();
35289                 this.fireEvent("collapsed", this);
35290             },
35291             scope: this,
35292             block: true
35293         });
35294     },
35295
35296     animateExpand : function(){
35297         this.beforeSlide();
35298         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35299         this.el.setStyle("z-index", 20000);
35300         this.collapsedEl.hide({
35301             duration:.1
35302         });
35303         this.el.slideIn(this.getSlideAnchor(), {
35304             callback : function(){
35305                 this.el.setStyle("z-index", "");
35306                 this.afterSlide();
35307                 if(this.split){
35308                     this.split.el.show();
35309                 }
35310                 this.fireEvent("invalidated", this);
35311                 this.fireEvent("expanded", this);
35312             },
35313             scope: this,
35314             block: true
35315         });
35316     },
35317
35318     anchors : {
35319         "west" : "left",
35320         "east" : "right",
35321         "north" : "top",
35322         "south" : "bottom"
35323     },
35324
35325     sanchors : {
35326         "west" : "l",
35327         "east" : "r",
35328         "north" : "t",
35329         "south" : "b"
35330     },
35331
35332     canchors : {
35333         "west" : "tl-tr",
35334         "east" : "tr-tl",
35335         "north" : "tl-bl",
35336         "south" : "bl-tl"
35337     },
35338
35339     getAnchor : function(){
35340         return this.anchors[this.position];
35341     },
35342
35343     getCollapseAnchor : function(){
35344         return this.canchors[this.position];
35345     },
35346
35347     getSlideAnchor : function(){
35348         return this.sanchors[this.position];
35349     },
35350
35351     getAlignAdj : function(){
35352         var cm = this.cmargins;
35353         switch(this.position){
35354             case "west":
35355                 return [0, 0];
35356             break;
35357             case "east":
35358                 return [0, 0];
35359             break;
35360             case "north":
35361                 return [0, 0];
35362             break;
35363             case "south":
35364                 return [0, 0];
35365             break;
35366         }
35367     },
35368
35369     getExpandAdj : function(){
35370         var c = this.collapsedEl, cm = this.cmargins;
35371         switch(this.position){
35372             case "west":
35373                 return [-(cm.right+c.getWidth()+cm.left), 0];
35374             break;
35375             case "east":
35376                 return [cm.right+c.getWidth()+cm.left, 0];
35377             break;
35378             case "north":
35379                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35380             break;
35381             case "south":
35382                 return [0, cm.top+cm.bottom+c.getHeight()];
35383             break;
35384         }
35385     }
35386 });/*
35387  * Based on:
35388  * Ext JS Library 1.1.1
35389  * Copyright(c) 2006-2007, Ext JS, LLC.
35390  *
35391  * Originally Released Under LGPL - original licence link has changed is not relivant.
35392  *
35393  * Fork - LGPL
35394  * <script type="text/javascript">
35395  */
35396 /*
35397  * These classes are private internal classes
35398  */
35399 Roo.CenterLayoutRegion = function(mgr, config){
35400     Roo.LayoutRegion.call(this, mgr, config, "center");
35401     this.visible = true;
35402     this.minWidth = config.minWidth || 20;
35403     this.minHeight = config.minHeight || 20;
35404 };
35405
35406 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35407     hide : function(){
35408         // center panel can't be hidden
35409     },
35410     
35411     show : function(){
35412         // center panel can't be hidden
35413     },
35414     
35415     getMinWidth: function(){
35416         return this.minWidth;
35417     },
35418     
35419     getMinHeight: function(){
35420         return this.minHeight;
35421     }
35422 });
35423
35424
35425 Roo.NorthLayoutRegion = function(mgr, config){
35426     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35427     if(this.split){
35428         this.split.placement = Roo.SplitBar.TOP;
35429         this.split.orientation = Roo.SplitBar.VERTICAL;
35430         this.split.el.addClass("x-layout-split-v");
35431     }
35432     var size = config.initialSize || config.height;
35433     if(typeof size != "undefined"){
35434         this.el.setHeight(size);
35435     }
35436 };
35437 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35438     orientation: Roo.SplitBar.VERTICAL,
35439     getBox : function(){
35440         if(this.collapsed){
35441             return this.collapsedEl.getBox();
35442         }
35443         var box = this.el.getBox();
35444         if(this.split){
35445             box.height += this.split.el.getHeight();
35446         }
35447         return box;
35448     },
35449     
35450     updateBox : function(box){
35451         if(this.split && !this.collapsed){
35452             box.height -= this.split.el.getHeight();
35453             this.split.el.setLeft(box.x);
35454             this.split.el.setTop(box.y+box.height);
35455             this.split.el.setWidth(box.width);
35456         }
35457         if(this.collapsed){
35458             this.updateBody(box.width, null);
35459         }
35460         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35461     }
35462 });
35463
35464 Roo.SouthLayoutRegion = function(mgr, config){
35465     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35466     if(this.split){
35467         this.split.placement = Roo.SplitBar.BOTTOM;
35468         this.split.orientation = Roo.SplitBar.VERTICAL;
35469         this.split.el.addClass("x-layout-split-v");
35470     }
35471     var size = config.initialSize || config.height;
35472     if(typeof size != "undefined"){
35473         this.el.setHeight(size);
35474     }
35475 };
35476 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35477     orientation: Roo.SplitBar.VERTICAL,
35478     getBox : function(){
35479         if(this.collapsed){
35480             return this.collapsedEl.getBox();
35481         }
35482         var box = this.el.getBox();
35483         if(this.split){
35484             var sh = this.split.el.getHeight();
35485             box.height += sh;
35486             box.y -= sh;
35487         }
35488         return box;
35489     },
35490     
35491     updateBox : function(box){
35492         if(this.split && !this.collapsed){
35493             var sh = this.split.el.getHeight();
35494             box.height -= sh;
35495             box.y += sh;
35496             this.split.el.setLeft(box.x);
35497             this.split.el.setTop(box.y-sh);
35498             this.split.el.setWidth(box.width);
35499         }
35500         if(this.collapsed){
35501             this.updateBody(box.width, null);
35502         }
35503         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35504     }
35505 });
35506
35507 Roo.EastLayoutRegion = function(mgr, config){
35508     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35509     if(this.split){
35510         this.split.placement = Roo.SplitBar.RIGHT;
35511         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35512         this.split.el.addClass("x-layout-split-h");
35513     }
35514     var size = config.initialSize || config.width;
35515     if(typeof size != "undefined"){
35516         this.el.setWidth(size);
35517     }
35518 };
35519 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35520     orientation: Roo.SplitBar.HORIZONTAL,
35521     getBox : function(){
35522         if(this.collapsed){
35523             return this.collapsedEl.getBox();
35524         }
35525         var box = this.el.getBox();
35526         if(this.split){
35527             var sw = this.split.el.getWidth();
35528             box.width += sw;
35529             box.x -= sw;
35530         }
35531         return box;
35532     },
35533
35534     updateBox : function(box){
35535         if(this.split && !this.collapsed){
35536             var sw = this.split.el.getWidth();
35537             box.width -= sw;
35538             this.split.el.setLeft(box.x);
35539             this.split.el.setTop(box.y);
35540             this.split.el.setHeight(box.height);
35541             box.x += sw;
35542         }
35543         if(this.collapsed){
35544             this.updateBody(null, box.height);
35545         }
35546         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35547     }
35548 });
35549
35550 Roo.WestLayoutRegion = function(mgr, config){
35551     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35552     if(this.split){
35553         this.split.placement = Roo.SplitBar.LEFT;
35554         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35555         this.split.el.addClass("x-layout-split-h");
35556     }
35557     var size = config.initialSize || config.width;
35558     if(typeof size != "undefined"){
35559         this.el.setWidth(size);
35560     }
35561 };
35562 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35563     orientation: Roo.SplitBar.HORIZONTAL,
35564     getBox : function(){
35565         if(this.collapsed){
35566             return this.collapsedEl.getBox();
35567         }
35568         var box = this.el.getBox();
35569         if(this.split){
35570             box.width += this.split.el.getWidth();
35571         }
35572         return box;
35573     },
35574     
35575     updateBox : function(box){
35576         if(this.split && !this.collapsed){
35577             var sw = this.split.el.getWidth();
35578             box.width -= sw;
35579             this.split.el.setLeft(box.x+box.width);
35580             this.split.el.setTop(box.y);
35581             this.split.el.setHeight(box.height);
35582         }
35583         if(this.collapsed){
35584             this.updateBody(null, box.height);
35585         }
35586         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35587     }
35588 });
35589 /*
35590  * Based on:
35591  * Ext JS Library 1.1.1
35592  * Copyright(c) 2006-2007, Ext JS, LLC.
35593  *
35594  * Originally Released Under LGPL - original licence link has changed is not relivant.
35595  *
35596  * Fork - LGPL
35597  * <script type="text/javascript">
35598  */
35599  
35600  
35601 /*
35602  * Private internal class for reading and applying state
35603  */
35604 Roo.LayoutStateManager = function(layout){
35605      // default empty state
35606      this.state = {
35607         north: {},
35608         south: {},
35609         east: {},
35610         west: {}       
35611     };
35612 };
35613
35614 Roo.LayoutStateManager.prototype = {
35615     init : function(layout, provider){
35616         this.provider = provider;
35617         var state = provider.get(layout.id+"-layout-state");
35618         if(state){
35619             var wasUpdating = layout.isUpdating();
35620             if(!wasUpdating){
35621                 layout.beginUpdate();
35622             }
35623             for(var key in state){
35624                 if(typeof state[key] != "function"){
35625                     var rstate = state[key];
35626                     var r = layout.getRegion(key);
35627                     if(r && rstate){
35628                         if(rstate.size){
35629                             r.resizeTo(rstate.size);
35630                         }
35631                         if(rstate.collapsed == true){
35632                             r.collapse(true);
35633                         }else{
35634                             r.expand(null, true);
35635                         }
35636                     }
35637                 }
35638             }
35639             if(!wasUpdating){
35640                 layout.endUpdate();
35641             }
35642             this.state = state; 
35643         }
35644         this.layout = layout;
35645         layout.on("regionresized", this.onRegionResized, this);
35646         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35647         layout.on("regionexpanded", this.onRegionExpanded, this);
35648     },
35649     
35650     storeState : function(){
35651         this.provider.set(this.layout.id+"-layout-state", this.state);
35652     },
35653     
35654     onRegionResized : function(region, newSize){
35655         this.state[region.getPosition()].size = newSize;
35656         this.storeState();
35657     },
35658     
35659     onRegionCollapsed : function(region){
35660         this.state[region.getPosition()].collapsed = true;
35661         this.storeState();
35662     },
35663     
35664     onRegionExpanded : function(region){
35665         this.state[region.getPosition()].collapsed = false;
35666         this.storeState();
35667     }
35668 };/*
35669  * Based on:
35670  * Ext JS Library 1.1.1
35671  * Copyright(c) 2006-2007, Ext JS, LLC.
35672  *
35673  * Originally Released Under LGPL - original licence link has changed is not relivant.
35674  *
35675  * Fork - LGPL
35676  * <script type="text/javascript">
35677  */
35678 /**
35679  * @class Roo.ContentPanel
35680  * @extends Roo.util.Observable
35681  * @children Roo.form.Form Roo.JsonView Roo.View
35682  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35683  * A basic ContentPanel element.
35684  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35685  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35686  * @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
35687  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35688  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35689  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35690  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35691  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35692  * @cfg {String} title          The title for this panel
35693  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35694  * @cfg {String} url            Calls {@link #setUrl} with this value
35695  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35696  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35697  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35698  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35699  * @cfg {String}    style  Extra style to add to the content panel
35700  * @cfg {Roo.menu.Menu} menu  popup menu
35701
35702  * @constructor
35703  * Create a new ContentPanel.
35704  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35705  * @param {String/Object} config A string to set only the title or a config object
35706  * @param {String} content (optional) Set the HTML content for this panel
35707  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35708  */
35709 Roo.ContentPanel = function(el, config, content){
35710     
35711      
35712     /*
35713     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35714         config = el;
35715         el = Roo.id();
35716     }
35717     if (config && config.parentLayout) { 
35718         el = config.parentLayout.el.createChild(); 
35719     }
35720     */
35721     if(el.autoCreate){ // xtype is available if this is called from factory
35722         config = el;
35723         el = Roo.id();
35724     }
35725     this.el = Roo.get(el);
35726     if(!this.el && config && config.autoCreate){
35727         if(typeof config.autoCreate == "object"){
35728             if(!config.autoCreate.id){
35729                 config.autoCreate.id = config.id||el;
35730             }
35731             this.el = Roo.DomHelper.append(document.body,
35732                         config.autoCreate, true);
35733         }else{
35734             this.el = Roo.DomHelper.append(document.body,
35735                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35736         }
35737     }
35738     
35739     
35740     this.closable = false;
35741     this.loaded = false;
35742     this.active = false;
35743     if(typeof config == "string"){
35744         this.title = config;
35745     }else{
35746         Roo.apply(this, config);
35747     }
35748     
35749     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35750         this.wrapEl = this.el.wrap();
35751         this.toolbar.container = this.el.insertSibling(false, 'before');
35752         this.toolbar = new Roo.Toolbar(this.toolbar);
35753     }
35754     
35755     // xtype created footer. - not sure if will work as we normally have to render first..
35756     if (this.footer && !this.footer.el && this.footer.xtype) {
35757         if (!this.wrapEl) {
35758             this.wrapEl = this.el.wrap();
35759         }
35760     
35761         this.footer.container = this.wrapEl.createChild();
35762          
35763         this.footer = Roo.factory(this.footer, Roo);
35764         
35765     }
35766     
35767     if(this.resizeEl){
35768         this.resizeEl = Roo.get(this.resizeEl, true);
35769     }else{
35770         this.resizeEl = this.el;
35771     }
35772     // handle view.xtype
35773     
35774  
35775     
35776     
35777     this.addEvents({
35778         /**
35779          * @event activate
35780          * Fires when this panel is activated. 
35781          * @param {Roo.ContentPanel} this
35782          */
35783         "activate" : true,
35784         /**
35785          * @event deactivate
35786          * Fires when this panel is activated. 
35787          * @param {Roo.ContentPanel} this
35788          */
35789         "deactivate" : true,
35790
35791         /**
35792          * @event resize
35793          * Fires when this panel is resized if fitToFrame is true.
35794          * @param {Roo.ContentPanel} this
35795          * @param {Number} width The width after any component adjustments
35796          * @param {Number} height The height after any component adjustments
35797          */
35798         "resize" : true,
35799         
35800          /**
35801          * @event render
35802          * Fires when this tab is created
35803          * @param {Roo.ContentPanel} this
35804          */
35805         "render" : true
35806          
35807         
35808     });
35809     
35810
35811     
35812     
35813     if(this.autoScroll){
35814         this.resizeEl.setStyle("overflow", "auto");
35815     } else {
35816         // fix randome scrolling
35817         this.el.on('scroll', function() {
35818             Roo.log('fix random scolling');
35819             this.scrollTo('top',0); 
35820         });
35821     }
35822     content = content || this.content;
35823     if(content){
35824         this.setContent(content);
35825     }
35826     if(config && config.url){
35827         this.setUrl(this.url, this.params, this.loadOnce);
35828     }
35829     
35830     
35831     
35832     Roo.ContentPanel.superclass.constructor.call(this);
35833     
35834     if (this.view && typeof(this.view.xtype) != 'undefined') {
35835         this.view.el = this.el.appendChild(document.createElement("div"));
35836         this.view = Roo.factory(this.view); 
35837         this.view.render  &&  this.view.render(false, '');  
35838     }
35839     
35840     
35841     this.fireEvent('render', this);
35842 };
35843
35844 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35845     tabTip:'',
35846     setRegion : function(region){
35847         this.region = region;
35848         if(region){
35849            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35850         }else{
35851            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35852         } 
35853     },
35854     
35855     /**
35856      * Returns the toolbar for this Panel if one was configured. 
35857      * @return {Roo.Toolbar} 
35858      */
35859     getToolbar : function(){
35860         return this.toolbar;
35861     },
35862     
35863     setActiveState : function(active){
35864         this.active = active;
35865         if(!active){
35866             this.fireEvent("deactivate", this);
35867         }else{
35868             this.fireEvent("activate", this);
35869         }
35870     },
35871     /**
35872      * Updates this panel's element
35873      * @param {String} content The new content
35874      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35875     */
35876     setContent : function(content, loadScripts){
35877         this.el.update(content, loadScripts);
35878     },
35879
35880     ignoreResize : function(w, h){
35881         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35882             return true;
35883         }else{
35884             this.lastSize = {width: w, height: h};
35885             return false;
35886         }
35887     },
35888     /**
35889      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35890      * @return {Roo.UpdateManager} The UpdateManager
35891      */
35892     getUpdateManager : function(){
35893         return this.el.getUpdateManager();
35894     },
35895      /**
35896      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35897      * @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:
35898 <pre><code>
35899 panel.load({
35900     url: "your-url.php",
35901     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35902     callback: yourFunction,
35903     scope: yourObject, //(optional scope)
35904     discardUrl: false,
35905     nocache: false,
35906     text: "Loading...",
35907     timeout: 30,
35908     scripts: false
35909 });
35910 </code></pre>
35911      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35912      * 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.
35913      * @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}
35914      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35915      * @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.
35916      * @return {Roo.ContentPanel} this
35917      */
35918     load : function(){
35919         var um = this.el.getUpdateManager();
35920         um.update.apply(um, arguments);
35921         return this;
35922     },
35923
35924
35925     /**
35926      * 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.
35927      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35928      * @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)
35929      * @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)
35930      * @return {Roo.UpdateManager} The UpdateManager
35931      */
35932     setUrl : function(url, params, loadOnce){
35933         if(this.refreshDelegate){
35934             this.removeListener("activate", this.refreshDelegate);
35935         }
35936         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35937         this.on("activate", this.refreshDelegate);
35938         return this.el.getUpdateManager();
35939     },
35940     
35941     _handleRefresh : function(url, params, loadOnce){
35942         if(!loadOnce || !this.loaded){
35943             var updater = this.el.getUpdateManager();
35944             updater.update(url, params, this._setLoaded.createDelegate(this));
35945         }
35946     },
35947     
35948     _setLoaded : function(){
35949         this.loaded = true;
35950     }, 
35951     
35952     /**
35953      * Returns this panel's id
35954      * @return {String} 
35955      */
35956     getId : function(){
35957         return this.el.id;
35958     },
35959     
35960     /** 
35961      * Returns this panel's element - used by regiosn to add.
35962      * @return {Roo.Element} 
35963      */
35964     getEl : function(){
35965         return this.wrapEl || this.el;
35966     },
35967     
35968     adjustForComponents : function(width, height)
35969     {
35970         //Roo.log('adjustForComponents ');
35971         if(this.resizeEl != this.el){
35972             width -= this.el.getFrameWidth('lr');
35973             height -= this.el.getFrameWidth('tb');
35974         }
35975         if(this.toolbar){
35976             var te = this.toolbar.getEl();
35977             height -= te.getHeight();
35978             te.setWidth(width);
35979         }
35980         if(this.footer){
35981             var te = this.footer.getEl();
35982             //Roo.log("footer:" + te.getHeight());
35983             
35984             height -= te.getHeight();
35985             te.setWidth(width);
35986         }
35987         
35988         
35989         if(this.adjustments){
35990             width += this.adjustments[0];
35991             height += this.adjustments[1];
35992         }
35993         return {"width": width, "height": height};
35994     },
35995     
35996     setSize : function(width, height){
35997         if(this.fitToFrame && !this.ignoreResize(width, height)){
35998             if(this.fitContainer && this.resizeEl != this.el){
35999                 this.el.setSize(width, height);
36000             }
36001             var size = this.adjustForComponents(width, height);
36002             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36003             this.fireEvent('resize', this, size.width, size.height);
36004         }
36005     },
36006     
36007     /**
36008      * Returns this panel's title
36009      * @return {String} 
36010      */
36011     getTitle : function(){
36012         return this.title;
36013     },
36014     
36015     /**
36016      * Set this panel's title
36017      * @param {String} title
36018      */
36019     setTitle : function(title){
36020         this.title = title;
36021         if(this.region){
36022             this.region.updatePanelTitle(this, title);
36023         }
36024     },
36025     
36026     /**
36027      * Returns true is this panel was configured to be closable
36028      * @return {Boolean} 
36029      */
36030     isClosable : function(){
36031         return this.closable;
36032     },
36033     
36034     beforeSlide : function(){
36035         this.el.clip();
36036         this.resizeEl.clip();
36037     },
36038     
36039     afterSlide : function(){
36040         this.el.unclip();
36041         this.resizeEl.unclip();
36042     },
36043     
36044     /**
36045      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36046      *   Will fail silently if the {@link #setUrl} method has not been called.
36047      *   This does not activate the panel, just updates its content.
36048      */
36049     refresh : function(){
36050         if(this.refreshDelegate){
36051            this.loaded = false;
36052            this.refreshDelegate();
36053         }
36054     },
36055     
36056     /**
36057      * Destroys this panel
36058      */
36059     destroy : function(){
36060         this.el.removeAllListeners();
36061         var tempEl = document.createElement("span");
36062         tempEl.appendChild(this.el.dom);
36063         tempEl.innerHTML = "";
36064         this.el.remove();
36065         this.el = null;
36066     },
36067     
36068     /**
36069      * form - if the content panel contains a form - this is a reference to it.
36070      * @type {Roo.form.Form}
36071      */
36072     form : false,
36073     /**
36074      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36075      *    This contains a reference to it.
36076      * @type {Roo.View}
36077      */
36078     view : false,
36079     
36080       /**
36081      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36082      * <pre><code>
36083
36084 layout.addxtype({
36085        xtype : 'Form',
36086        items: [ .... ]
36087    }
36088 );
36089
36090 </code></pre>
36091      * @param {Object} cfg Xtype definition of item to add.
36092      */
36093     
36094     addxtype : function(cfg) {
36095         // add form..
36096         if (cfg.xtype.match(/^Form$/)) {
36097             
36098             var el;
36099             //if (this.footer) {
36100             //    el = this.footer.container.insertSibling(false, 'before');
36101             //} else {
36102                 el = this.el.createChild();
36103             //}
36104
36105             this.form = new  Roo.form.Form(cfg);
36106             
36107             
36108             if ( this.form.allItems.length) {
36109                 this.form.render(el.dom);
36110             }
36111             return this.form;
36112         }
36113         // should only have one of theses..
36114         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36115             // views.. should not be just added - used named prop 'view''
36116             
36117             cfg.el = this.el.appendChild(document.createElement("div"));
36118             // factory?
36119             
36120             var ret = new Roo.factory(cfg);
36121              
36122              ret.render && ret.render(false, ''); // render blank..
36123             this.view = ret;
36124             return ret;
36125         }
36126         return false;
36127     }
36128 });
36129
36130 /**
36131  * @class Roo.GridPanel
36132  * @extends Roo.ContentPanel
36133  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36134  * @constructor
36135  * Create a new GridPanel.
36136  * @cfg {Roo.grid.Grid} grid The grid for this panel
36137  */
36138 Roo.GridPanel = function(grid, config){
36139     
36140     // universal ctor...
36141     if (typeof(grid.grid) != 'undefined') {
36142         config = grid;
36143         grid = config.grid;
36144     }
36145     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36146         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36147         
36148     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36149     
36150     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36151     
36152     if(this.toolbar){
36153         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36154     }
36155     // xtype created footer. - not sure if will work as we normally have to render first..
36156     if (this.footer && !this.footer.el && this.footer.xtype) {
36157         
36158         this.footer.container = this.grid.getView().getFooterPanel(true);
36159         this.footer.dataSource = this.grid.dataSource;
36160         this.footer = Roo.factory(this.footer, Roo);
36161         
36162     }
36163     
36164     grid.monitorWindowResize = false; // turn off autosizing
36165     grid.autoHeight = false;
36166     grid.autoWidth = false;
36167     this.grid = grid;
36168     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36169 };
36170
36171 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36172     getId : function(){
36173         return this.grid.id;
36174     },
36175     
36176     /**
36177      * Returns the grid for this panel
36178      * @return {Roo.grid.Grid} 
36179      */
36180     getGrid : function(){
36181         return this.grid;    
36182     },
36183     
36184     setSize : function(width, height){
36185         if(!this.ignoreResize(width, height)){
36186             var grid = this.grid;
36187             var size = this.adjustForComponents(width, height);
36188             grid.getGridEl().setSize(size.width, size.height);
36189             grid.autoSize();
36190         }
36191     },
36192     
36193     beforeSlide : function(){
36194         this.grid.getView().scroller.clip();
36195     },
36196     
36197     afterSlide : function(){
36198         this.grid.getView().scroller.unclip();
36199     },
36200     
36201     destroy : function(){
36202         this.grid.destroy();
36203         delete this.grid;
36204         Roo.GridPanel.superclass.destroy.call(this); 
36205     }
36206 });
36207
36208
36209 /**
36210  * @class Roo.NestedLayoutPanel
36211  * @extends Roo.ContentPanel
36212  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36213  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
36214  *
36215  * 
36216  * @constructor
36217  * Create a new NestedLayoutPanel.
36218  * 
36219  * 
36220  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36221  * @param {String/Object} config A string to set only the title or a config object
36222  */
36223 Roo.NestedLayoutPanel = function(layout, config)
36224 {
36225     // construct with only one argument..
36226     /* FIXME - implement nicer consturctors
36227     if (layout.layout) {
36228         config = layout;
36229         layout = config.layout;
36230         delete config.layout;
36231     }
36232     if (layout.xtype && !layout.getEl) {
36233         // then layout needs constructing..
36234         layout = Roo.factory(layout, Roo);
36235     }
36236     */
36237     
36238     
36239     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36240     
36241     layout.monitorWindowResize = false; // turn off autosizing
36242     this.layout = layout;
36243     this.layout.getEl().addClass("x-layout-nested-layout");
36244     
36245     
36246     
36247     
36248 };
36249
36250 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36251
36252     setSize : function(width, height){
36253         if(!this.ignoreResize(width, height)){
36254             var size = this.adjustForComponents(width, height);
36255             var el = this.layout.getEl();
36256             el.setSize(size.width, size.height);
36257             var touch = el.dom.offsetWidth;
36258             this.layout.layout();
36259             // ie requires a double layout on the first pass
36260             if(Roo.isIE && !this.initialized){
36261                 this.initialized = true;
36262                 this.layout.layout();
36263             }
36264         }
36265     },
36266     
36267     // activate all subpanels if not currently active..
36268     
36269     setActiveState : function(active){
36270         this.active = active;
36271         if(!active){
36272             this.fireEvent("deactivate", this);
36273             return;
36274         }
36275         
36276         this.fireEvent("activate", this);
36277         // not sure if this should happen before or after..
36278         if (!this.layout) {
36279             return; // should not happen..
36280         }
36281         var reg = false;
36282         for (var r in this.layout.regions) {
36283             reg = this.layout.getRegion(r);
36284             if (reg.getActivePanel()) {
36285                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36286                 reg.setActivePanel(reg.getActivePanel());
36287                 continue;
36288             }
36289             if (!reg.panels.length) {
36290                 continue;
36291             }
36292             reg.showPanel(reg.getPanel(0));
36293         }
36294         
36295         
36296         
36297         
36298     },
36299     
36300     /**
36301      * Returns the nested BorderLayout for this panel
36302      * @return {Roo.BorderLayout} 
36303      */
36304     getLayout : function(){
36305         return this.layout;
36306     },
36307     
36308      /**
36309      * Adds a xtype elements to the layout of the nested panel
36310      * <pre><code>
36311
36312 panel.addxtype({
36313        xtype : 'ContentPanel',
36314        region: 'west',
36315        items: [ .... ]
36316    }
36317 );
36318
36319 panel.addxtype({
36320         xtype : 'NestedLayoutPanel',
36321         region: 'west',
36322         layout: {
36323            center: { },
36324            west: { }   
36325         },
36326         items : [ ... list of content panels or nested layout panels.. ]
36327    }
36328 );
36329 </code></pre>
36330      * @param {Object} cfg Xtype definition of item to add.
36331      */
36332     addxtype : function(cfg) {
36333         return this.layout.addxtype(cfg);
36334     
36335     }
36336 });
36337
36338 Roo.ScrollPanel = function(el, config, content){
36339     config = config || {};
36340     config.fitToFrame = true;
36341     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36342     
36343     this.el.dom.style.overflow = "hidden";
36344     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36345     this.el.removeClass("x-layout-inactive-content");
36346     this.el.on("mousewheel", this.onWheel, this);
36347
36348     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36349     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36350     up.unselectable(); down.unselectable();
36351     up.on("click", this.scrollUp, this);
36352     down.on("click", this.scrollDown, this);
36353     up.addClassOnOver("x-scroller-btn-over");
36354     down.addClassOnOver("x-scroller-btn-over");
36355     up.addClassOnClick("x-scroller-btn-click");
36356     down.addClassOnClick("x-scroller-btn-click");
36357     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36358
36359     this.resizeEl = this.el;
36360     this.el = wrap; this.up = up; this.down = down;
36361 };
36362
36363 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36364     increment : 100,
36365     wheelIncrement : 5,
36366     scrollUp : function(){
36367         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36368     },
36369
36370     scrollDown : function(){
36371         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36372     },
36373
36374     afterScroll : function(){
36375         var el = this.resizeEl;
36376         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36377         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36378         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36379     },
36380
36381     setSize : function(){
36382         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36383         this.afterScroll();
36384     },
36385
36386     onWheel : function(e){
36387         var d = e.getWheelDelta();
36388         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36389         this.afterScroll();
36390         e.stopEvent();
36391     },
36392
36393     setContent : function(content, loadScripts){
36394         this.resizeEl.update(content, loadScripts);
36395     }
36396
36397 });
36398
36399
36400
36401 /**
36402  * @class Roo.TreePanel
36403  * @extends Roo.ContentPanel
36404  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36405  * Treepanel component
36406  * 
36407  * @constructor
36408  * Create a new TreePanel. - defaults to fit/scoll contents.
36409  * @param {String/Object} config A string to set only the panel's title, or a config object
36410  */
36411 Roo.TreePanel = function(config){
36412     var el = config.el;
36413     var tree = config.tree;
36414     delete config.tree; 
36415     delete config.el; // hopefull!
36416     
36417     // wrapper for IE7 strict & safari scroll issue
36418     
36419     var treeEl = el.createChild();
36420     config.resizeEl = treeEl;
36421     
36422     
36423     
36424     Roo.TreePanel.superclass.constructor.call(this, el, config);
36425  
36426  
36427     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36428     //console.log(tree);
36429     this.on('activate', function()
36430     {
36431         if (this.tree.rendered) {
36432             return;
36433         }
36434         //console.log('render tree');
36435         this.tree.render();
36436     });
36437     // this should not be needed.. - it's actually the 'el' that resizes?
36438     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36439     
36440     //this.on('resize',  function (cp, w, h) {
36441     //        this.tree.innerCt.setWidth(w);
36442     //        this.tree.innerCt.setHeight(h);
36443     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36444     //});
36445
36446         
36447     
36448 };
36449
36450 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36451     fitToFrame : true,
36452     autoScroll : true,
36453     /*
36454      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36455      */
36456     tree : false
36457
36458 });
36459
36460
36461
36462
36463
36464
36465
36466
36467
36468
36469
36470 /*
36471  * Based on:
36472  * Ext JS Library 1.1.1
36473  * Copyright(c) 2006-2007, Ext JS, LLC.
36474  *
36475  * Originally Released Under LGPL - original licence link has changed is not relivant.
36476  *
36477  * Fork - LGPL
36478  * <script type="text/javascript">
36479  */
36480  
36481
36482 /**
36483  * @class Roo.ReaderLayout
36484  * @extends Roo.BorderLayout
36485  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36486  * center region containing two nested regions (a top one for a list view and one for item preview below),
36487  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36488  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36489  * expedites the setup of the overall layout and regions for this common application style.
36490  * Example:
36491  <pre><code>
36492 var reader = new Roo.ReaderLayout();
36493 var CP = Roo.ContentPanel;  // shortcut for adding
36494
36495 reader.beginUpdate();
36496 reader.add("north", new CP("north", "North"));
36497 reader.add("west", new CP("west", {title: "West"}));
36498 reader.add("east", new CP("east", {title: "East"}));
36499
36500 reader.regions.listView.add(new CP("listView", "List"));
36501 reader.regions.preview.add(new CP("preview", "Preview"));
36502 reader.endUpdate();
36503 </code></pre>
36504 * @constructor
36505 * Create a new ReaderLayout
36506 * @param {Object} config Configuration options
36507 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36508 * document.body if omitted)
36509 */
36510 Roo.ReaderLayout = function(config, renderTo){
36511     var c = config || {size:{}};
36512     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36513         north: c.north !== false ? Roo.apply({
36514             split:false,
36515             initialSize: 32,
36516             titlebar: false
36517         }, c.north) : false,
36518         west: c.west !== false ? Roo.apply({
36519             split:true,
36520             initialSize: 200,
36521             minSize: 175,
36522             maxSize: 400,
36523             titlebar: true,
36524             collapsible: true,
36525             animate: true,
36526             margins:{left:5,right:0,bottom:5,top:5},
36527             cmargins:{left:5,right:5,bottom:5,top:5}
36528         }, c.west) : false,
36529         east: c.east !== false ? Roo.apply({
36530             split:true,
36531             initialSize: 200,
36532             minSize: 175,
36533             maxSize: 400,
36534             titlebar: true,
36535             collapsible: true,
36536             animate: true,
36537             margins:{left:0,right:5,bottom:5,top:5},
36538             cmargins:{left:5,right:5,bottom:5,top:5}
36539         }, c.east) : false,
36540         center: Roo.apply({
36541             tabPosition: 'top',
36542             autoScroll:false,
36543             closeOnTab: true,
36544             titlebar:false,
36545             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36546         }, c.center)
36547     });
36548
36549     this.el.addClass('x-reader');
36550
36551     this.beginUpdate();
36552
36553     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36554         south: c.preview !== false ? Roo.apply({
36555             split:true,
36556             initialSize: 200,
36557             minSize: 100,
36558             autoScroll:true,
36559             collapsible:true,
36560             titlebar: true,
36561             cmargins:{top:5,left:0, right:0, bottom:0}
36562         }, c.preview) : false,
36563         center: Roo.apply({
36564             autoScroll:false,
36565             titlebar:false,
36566             minHeight:200
36567         }, c.listView)
36568     });
36569     this.add('center', new Roo.NestedLayoutPanel(inner,
36570             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36571
36572     this.endUpdate();
36573
36574     this.regions.preview = inner.getRegion('south');
36575     this.regions.listView = inner.getRegion('center');
36576 };
36577
36578 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36579  * Based on:
36580  * Ext JS Library 1.1.1
36581  * Copyright(c) 2006-2007, Ext JS, LLC.
36582  *
36583  * Originally Released Under LGPL - original licence link has changed is not relivant.
36584  *
36585  * Fork - LGPL
36586  * <script type="text/javascript">
36587  */
36588  
36589 /**
36590  * @class Roo.grid.Grid
36591  * @extends Roo.util.Observable
36592  * This class represents the primary interface of a component based grid control.
36593  * <br><br>Usage:<pre><code>
36594  var grid = new Roo.grid.Grid("my-container-id", {
36595      ds: myDataStore,
36596      cm: myColModel,
36597      selModel: mySelectionModel,
36598      autoSizeColumns: true,
36599      monitorWindowResize: false,
36600      trackMouseOver: true
36601  });
36602  // set any options
36603  grid.render();
36604  * </code></pre>
36605  * <b>Common Problems:</b><br/>
36606  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36607  * element will correct this<br/>
36608  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36609  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36610  * are unpredictable.<br/>
36611  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36612  * grid to calculate dimensions/offsets.<br/>
36613   * @constructor
36614  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36615  * The container MUST have some type of size defined for the grid to fill. The container will be
36616  * automatically set to position relative if it isn't already.
36617  * @param {Object} config A config object that sets properties on this grid.
36618  */
36619 Roo.grid.Grid = function(container, config){
36620         // initialize the container
36621         this.container = Roo.get(container);
36622         this.container.update("");
36623         this.container.setStyle("overflow", "hidden");
36624     this.container.addClass('x-grid-container');
36625
36626     this.id = this.container.id;
36627
36628     Roo.apply(this, config);
36629     // check and correct shorthanded configs
36630     if(this.ds){
36631         this.dataSource = this.ds;
36632         delete this.ds;
36633     }
36634     if(this.cm){
36635         this.colModel = this.cm;
36636         delete this.cm;
36637     }
36638     if(this.sm){
36639         this.selModel = this.sm;
36640         delete this.sm;
36641     }
36642
36643     if (this.selModel) {
36644         this.selModel = Roo.factory(this.selModel, Roo.grid);
36645         this.sm = this.selModel;
36646         this.sm.xmodule = this.xmodule || false;
36647     }
36648     if (typeof(this.colModel.config) == 'undefined') {
36649         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36650         this.cm = this.colModel;
36651         this.cm.xmodule = this.xmodule || false;
36652     }
36653     if (this.dataSource) {
36654         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36655         this.ds = this.dataSource;
36656         this.ds.xmodule = this.xmodule || false;
36657          
36658     }
36659     
36660     
36661     
36662     if(this.width){
36663         this.container.setWidth(this.width);
36664     }
36665
36666     if(this.height){
36667         this.container.setHeight(this.height);
36668     }
36669     /** @private */
36670         this.addEvents({
36671         // raw events
36672         /**
36673          * @event click
36674          * The raw click event for the entire grid.
36675          * @param {Roo.EventObject} e
36676          */
36677         "click" : true,
36678         /**
36679          * @event dblclick
36680          * The raw dblclick event for the entire grid.
36681          * @param {Roo.EventObject} e
36682          */
36683         "dblclick" : true,
36684         /**
36685          * @event contextmenu
36686          * The raw contextmenu event for the entire grid.
36687          * @param {Roo.EventObject} e
36688          */
36689         "contextmenu" : true,
36690         /**
36691          * @event mousedown
36692          * The raw mousedown event for the entire grid.
36693          * @param {Roo.EventObject} e
36694          */
36695         "mousedown" : true,
36696         /**
36697          * @event mouseup
36698          * The raw mouseup event for the entire grid.
36699          * @param {Roo.EventObject} e
36700          */
36701         "mouseup" : true,
36702         /**
36703          * @event mouseover
36704          * The raw mouseover event for the entire grid.
36705          * @param {Roo.EventObject} e
36706          */
36707         "mouseover" : true,
36708         /**
36709          * @event mouseout
36710          * The raw mouseout event for the entire grid.
36711          * @param {Roo.EventObject} e
36712          */
36713         "mouseout" : true,
36714         /**
36715          * @event keypress
36716          * The raw keypress event for the entire grid.
36717          * @param {Roo.EventObject} e
36718          */
36719         "keypress" : true,
36720         /**
36721          * @event keydown
36722          * The raw keydown event for the entire grid.
36723          * @param {Roo.EventObject} e
36724          */
36725         "keydown" : true,
36726
36727         // custom events
36728
36729         /**
36730          * @event cellclick
36731          * Fires when a cell is clicked
36732          * @param {Grid} this
36733          * @param {Number} rowIndex
36734          * @param {Number} columnIndex
36735          * @param {Roo.EventObject} e
36736          */
36737         "cellclick" : true,
36738         /**
36739          * @event celldblclick
36740          * Fires when a cell is double clicked
36741          * @param {Grid} this
36742          * @param {Number} rowIndex
36743          * @param {Number} columnIndex
36744          * @param {Roo.EventObject} e
36745          */
36746         "celldblclick" : true,
36747         /**
36748          * @event rowclick
36749          * Fires when a row is clicked
36750          * @param {Grid} this
36751          * @param {Number} rowIndex
36752          * @param {Roo.EventObject} e
36753          */
36754         "rowclick" : true,
36755         /**
36756          * @event rowdblclick
36757          * Fires when a row is double clicked
36758          * @param {Grid} this
36759          * @param {Number} rowIndex
36760          * @param {Roo.EventObject} e
36761          */
36762         "rowdblclick" : true,
36763         /**
36764          * @event headerclick
36765          * Fires when a header is clicked
36766          * @param {Grid} this
36767          * @param {Number} columnIndex
36768          * @param {Roo.EventObject} e
36769          */
36770         "headerclick" : true,
36771         /**
36772          * @event headerdblclick
36773          * Fires when a header cell is double clicked
36774          * @param {Grid} this
36775          * @param {Number} columnIndex
36776          * @param {Roo.EventObject} e
36777          */
36778         "headerdblclick" : true,
36779         /**
36780          * @event rowcontextmenu
36781          * Fires when a row is right clicked
36782          * @param {Grid} this
36783          * @param {Number} rowIndex
36784          * @param {Roo.EventObject} e
36785          */
36786         "rowcontextmenu" : true,
36787         /**
36788          * @event cellcontextmenu
36789          * Fires when a cell is right clicked
36790          * @param {Grid} this
36791          * @param {Number} rowIndex
36792          * @param {Number} cellIndex
36793          * @param {Roo.EventObject} e
36794          */
36795          "cellcontextmenu" : true,
36796         /**
36797          * @event headercontextmenu
36798          * Fires when a header is right clicked
36799          * @param {Grid} this
36800          * @param {Number} columnIndex
36801          * @param {Roo.EventObject} e
36802          */
36803         "headercontextmenu" : true,
36804         /**
36805          * @event bodyscroll
36806          * Fires when the body element is scrolled
36807          * @param {Number} scrollLeft
36808          * @param {Number} scrollTop
36809          */
36810         "bodyscroll" : true,
36811         /**
36812          * @event columnresize
36813          * Fires when the user resizes a column
36814          * @param {Number} columnIndex
36815          * @param {Number} newSize
36816          */
36817         "columnresize" : true,
36818         /**
36819          * @event columnmove
36820          * Fires when the user moves a column
36821          * @param {Number} oldIndex
36822          * @param {Number} newIndex
36823          */
36824         "columnmove" : true,
36825         /**
36826          * @event startdrag
36827          * Fires when row(s) start being dragged
36828          * @param {Grid} this
36829          * @param {Roo.GridDD} dd The drag drop object
36830          * @param {event} e The raw browser event
36831          */
36832         "startdrag" : true,
36833         /**
36834          * @event enddrag
36835          * Fires when a drag operation is complete
36836          * @param {Grid} this
36837          * @param {Roo.GridDD} dd The drag drop object
36838          * @param {event} e The raw browser event
36839          */
36840         "enddrag" : true,
36841         /**
36842          * @event dragdrop
36843          * Fires when dragged row(s) are dropped on a valid DD target
36844          * @param {Grid} this
36845          * @param {Roo.GridDD} dd The drag drop object
36846          * @param {String} targetId The target drag drop object
36847          * @param {event} e The raw browser event
36848          */
36849         "dragdrop" : true,
36850         /**
36851          * @event dragover
36852          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36853          * @param {Grid} this
36854          * @param {Roo.GridDD} dd The drag drop object
36855          * @param {String} targetId The target drag drop object
36856          * @param {event} e The raw browser event
36857          */
36858         "dragover" : true,
36859         /**
36860          * @event dragenter
36861          *  Fires when the dragged row(s) first cross another DD target while being dragged
36862          * @param {Grid} this
36863          * @param {Roo.GridDD} dd The drag drop object
36864          * @param {String} targetId The target drag drop object
36865          * @param {event} e The raw browser event
36866          */
36867         "dragenter" : true,
36868         /**
36869          * @event dragout
36870          * Fires when the dragged row(s) leave another DD target while being dragged
36871          * @param {Grid} this
36872          * @param {Roo.GridDD} dd The drag drop object
36873          * @param {String} targetId The target drag drop object
36874          * @param {event} e The raw browser event
36875          */
36876         "dragout" : true,
36877         /**
36878          * @event rowclass
36879          * Fires when a row is rendered, so you can change add a style to it.
36880          * @param {GridView} gridview   The grid view
36881          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36882          */
36883         'rowclass' : true,
36884
36885         /**
36886          * @event render
36887          * Fires when the grid is rendered
36888          * @param {Grid} grid
36889          */
36890         'render' : true
36891     });
36892
36893     Roo.grid.Grid.superclass.constructor.call(this);
36894 };
36895 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36896     
36897     /**
36898          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36899          */
36900         /**
36901          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36902          */
36903         /**
36904          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36905          */
36906         /**
36907          * @cfg {Roo.grid.Store} ds The data store for the grid
36908          */
36909         /**
36910          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36911          */
36912         /**
36913      * @cfg {String} ddGroup - drag drop group.
36914      */
36915       /**
36916      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36917      */
36918
36919     /**
36920      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36921      */
36922     minColumnWidth : 25,
36923
36924     /**
36925      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36926      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36927      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36928      */
36929     autoSizeColumns : false,
36930
36931     /**
36932      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36933      */
36934     autoSizeHeaders : true,
36935
36936     /**
36937      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36938      */
36939     monitorWindowResize : true,
36940
36941     /**
36942      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36943      * rows measured to get a columns size. Default is 0 (all rows).
36944      */
36945     maxRowsToMeasure : 0,
36946
36947     /**
36948      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36949      */
36950     trackMouseOver : true,
36951
36952     /**
36953     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36954     */
36955       /**
36956     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
36957     */
36958     
36959     /**
36960     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36961     */
36962     enableDragDrop : false,
36963     
36964     /**
36965     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36966     */
36967     enableColumnMove : true,
36968     
36969     /**
36970     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36971     */
36972     enableColumnHide : true,
36973     
36974     /**
36975     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36976     */
36977     enableRowHeightSync : false,
36978     
36979     /**
36980     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36981     */
36982     stripeRows : true,
36983     
36984     /**
36985     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36986     */
36987     autoHeight : false,
36988
36989     /**
36990      * @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.
36991      */
36992     autoExpandColumn : false,
36993
36994     /**
36995     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36996     * Default is 50.
36997     */
36998     autoExpandMin : 50,
36999
37000     /**
37001     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37002     */
37003     autoExpandMax : 1000,
37004
37005     /**
37006     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37007     */
37008     view : null,
37009
37010     /**
37011     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37012     */
37013     loadMask : false,
37014     /**
37015     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37016     */
37017     dropTarget: false,
37018      /**
37019     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37020     */ 
37021     sortColMenu : false,
37022     
37023     // private
37024     rendered : false,
37025
37026     /**
37027     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37028     * of a fixed width. Default is false.
37029     */
37030     /**
37031     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37032     */
37033     
37034     
37035     /**
37036     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37037     * %0 is replaced with the number of selected rows.
37038     */
37039     ddText : "{0} selected row{1}",
37040     
37041     
37042     /**
37043      * Called once after all setup has been completed and the grid is ready to be rendered.
37044      * @return {Roo.grid.Grid} this
37045      */
37046     render : function()
37047     {
37048         var c = this.container;
37049         // try to detect autoHeight/width mode
37050         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37051             this.autoHeight = true;
37052         }
37053         var view = this.getView();
37054         view.init(this);
37055
37056         c.on("click", this.onClick, this);
37057         c.on("dblclick", this.onDblClick, this);
37058         c.on("contextmenu", this.onContextMenu, this);
37059         c.on("keydown", this.onKeyDown, this);
37060         if (Roo.isTouch) {
37061             c.on("touchstart", this.onTouchStart, this);
37062         }
37063
37064         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37065
37066         this.getSelectionModel().init(this);
37067
37068         view.render();
37069
37070         if(this.loadMask){
37071             this.loadMask = new Roo.LoadMask(this.container,
37072                     Roo.apply({store:this.dataSource}, this.loadMask));
37073         }
37074         
37075         
37076         if (this.toolbar && this.toolbar.xtype) {
37077             this.toolbar.container = this.getView().getHeaderPanel(true);
37078             this.toolbar = new Roo.Toolbar(this.toolbar);
37079         }
37080         if (this.footer && this.footer.xtype) {
37081             this.footer.dataSource = this.getDataSource();
37082             this.footer.container = this.getView().getFooterPanel(true);
37083             this.footer = Roo.factory(this.footer, Roo);
37084         }
37085         if (this.dropTarget && this.dropTarget.xtype) {
37086             delete this.dropTarget.xtype;
37087             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37088         }
37089         
37090         
37091         this.rendered = true;
37092         this.fireEvent('render', this);
37093         return this;
37094     },
37095
37096     /**
37097      * Reconfigures the grid to use a different Store and Column Model.
37098      * The View will be bound to the new objects and refreshed.
37099      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37100      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37101      */
37102     reconfigure : function(dataSource, colModel){
37103         if(this.loadMask){
37104             this.loadMask.destroy();
37105             this.loadMask = new Roo.LoadMask(this.container,
37106                     Roo.apply({store:dataSource}, this.loadMask));
37107         }
37108         this.view.bind(dataSource, colModel);
37109         this.dataSource = dataSource;
37110         this.colModel = colModel;
37111         this.view.refresh(true);
37112     },
37113     /**
37114      * addColumns
37115      * Add's a column, default at the end..
37116      
37117      * @param {int} position to add (default end)
37118      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37119      */
37120     addColumns : function(pos, ar)
37121     {
37122         
37123         for (var i =0;i< ar.length;i++) {
37124             var cfg = ar[i];
37125             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37126             this.cm.lookup[cfg.id] = cfg;
37127         }
37128         
37129         
37130         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37131             pos = this.cm.config.length; //this.cm.config.push(cfg);
37132         } 
37133         pos = Math.max(0,pos);
37134         ar.unshift(0);
37135         ar.unshift(pos);
37136         this.cm.config.splice.apply(this.cm.config, ar);
37137         
37138         
37139         
37140         this.view.generateRules(this.cm);
37141         this.view.refresh(true);
37142         
37143     },
37144     
37145     
37146     
37147     
37148     // private
37149     onKeyDown : function(e){
37150         this.fireEvent("keydown", e);
37151     },
37152
37153     /**
37154      * Destroy this grid.
37155      * @param {Boolean} removeEl True to remove the element
37156      */
37157     destroy : function(removeEl, keepListeners){
37158         if(this.loadMask){
37159             this.loadMask.destroy();
37160         }
37161         var c = this.container;
37162         c.removeAllListeners();
37163         this.view.destroy();
37164         this.colModel.purgeListeners();
37165         if(!keepListeners){
37166             this.purgeListeners();
37167         }
37168         c.update("");
37169         if(removeEl === true){
37170             c.remove();
37171         }
37172     },
37173
37174     // private
37175     processEvent : function(name, e){
37176         // does this fire select???
37177         //Roo.log('grid:processEvent '  + name);
37178         
37179         if (name != 'touchstart' ) {
37180             this.fireEvent(name, e);    
37181         }
37182         
37183         var t = e.getTarget();
37184         var v = this.view;
37185         var header = v.findHeaderIndex(t);
37186         if(header !== false){
37187             var ename = name == 'touchstart' ? 'click' : name;
37188              
37189             this.fireEvent("header" + ename, this, header, e);
37190         }else{
37191             var row = v.findRowIndex(t);
37192             var cell = v.findCellIndex(t);
37193             if (name == 'touchstart') {
37194                 // first touch is always a click.
37195                 // hopefull this happens after selection is updated.?
37196                 name = false;
37197                 
37198                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37199                     var cs = this.selModel.getSelectedCell();
37200                     if (row == cs[0] && cell == cs[1]){
37201                         name = 'dblclick';
37202                     }
37203                 }
37204                 if (typeof(this.selModel.getSelections) != 'undefined') {
37205                     var cs = this.selModel.getSelections();
37206                     var ds = this.dataSource;
37207                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37208                         name = 'dblclick';
37209                     }
37210                 }
37211                 if (!name) {
37212                     return;
37213                 }
37214             }
37215             
37216             
37217             if(row !== false){
37218                 this.fireEvent("row" + name, this, row, e);
37219                 if(cell !== false){
37220                     this.fireEvent("cell" + name, this, row, cell, e);
37221                 }
37222             }
37223         }
37224     },
37225
37226     // private
37227     onClick : function(e){
37228         this.processEvent("click", e);
37229     },
37230    // private
37231     onTouchStart : function(e){
37232         this.processEvent("touchstart", e);
37233     },
37234
37235     // private
37236     onContextMenu : function(e, t){
37237         this.processEvent("contextmenu", e);
37238     },
37239
37240     // private
37241     onDblClick : function(e){
37242         this.processEvent("dblclick", e);
37243     },
37244
37245     // private
37246     walkCells : function(row, col, step, fn, scope){
37247         var cm = this.colModel, clen = cm.getColumnCount();
37248         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37249         if(step < 0){
37250             if(col < 0){
37251                 row--;
37252                 first = false;
37253             }
37254             while(row >= 0){
37255                 if(!first){
37256                     col = clen-1;
37257                 }
37258                 first = false;
37259                 while(col >= 0){
37260                     if(fn.call(scope || this, row, col, cm) === true){
37261                         return [row, col];
37262                     }
37263                     col--;
37264                 }
37265                 row--;
37266             }
37267         } else {
37268             if(col >= clen){
37269                 row++;
37270                 first = false;
37271             }
37272             while(row < rlen){
37273                 if(!first){
37274                     col = 0;
37275                 }
37276                 first = false;
37277                 while(col < clen){
37278                     if(fn.call(scope || this, row, col, cm) === true){
37279                         return [row, col];
37280                     }
37281                     col++;
37282                 }
37283                 row++;
37284             }
37285         }
37286         return null;
37287     },
37288
37289     // private
37290     getSelections : function(){
37291         return this.selModel.getSelections();
37292     },
37293
37294     /**
37295      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37296      * but if manual update is required this method will initiate it.
37297      */
37298     autoSize : function(){
37299         if(this.rendered){
37300             this.view.layout();
37301             if(this.view.adjustForScroll){
37302                 this.view.adjustForScroll();
37303             }
37304         }
37305     },
37306
37307     /**
37308      * Returns the grid's underlying element.
37309      * @return {Element} The element
37310      */
37311     getGridEl : function(){
37312         return this.container;
37313     },
37314
37315     // private for compatibility, overridden by editor grid
37316     stopEditing : function(){},
37317
37318     /**
37319      * Returns the grid's SelectionModel.
37320      * @return {SelectionModel}
37321      */
37322     getSelectionModel : function(){
37323         if(!this.selModel){
37324             this.selModel = new Roo.grid.RowSelectionModel();
37325         }
37326         return this.selModel;
37327     },
37328
37329     /**
37330      * Returns the grid's DataSource.
37331      * @return {DataSource}
37332      */
37333     getDataSource : function(){
37334         return this.dataSource;
37335     },
37336
37337     /**
37338      * Returns the grid's ColumnModel.
37339      * @return {ColumnModel}
37340      */
37341     getColumnModel : function(){
37342         return this.colModel;
37343     },
37344
37345     /**
37346      * Returns the grid's GridView object.
37347      * @return {GridView}
37348      */
37349     getView : function(){
37350         if(!this.view){
37351             this.view = new Roo.grid.GridView(this.viewConfig);
37352             this.relayEvents(this.view, [
37353                 "beforerowremoved", "beforerowsinserted",
37354                 "beforerefresh", "rowremoved",
37355                 "rowsinserted", "rowupdated" ,"refresh"
37356             ]);
37357         }
37358         return this.view;
37359     },
37360     /**
37361      * Called to get grid's drag proxy text, by default returns this.ddText.
37362      * Override this to put something different in the dragged text.
37363      * @return {String}
37364      */
37365     getDragDropText : function(){
37366         var count = this.selModel.getCount();
37367         return String.format(this.ddText, count, count == 1 ? '' : 's');
37368     }
37369 });
37370 /*
37371  * Based on:
37372  * Ext JS Library 1.1.1
37373  * Copyright(c) 2006-2007, Ext JS, LLC.
37374  *
37375  * Originally Released Under LGPL - original licence link has changed is not relivant.
37376  *
37377  * Fork - LGPL
37378  * <script type="text/javascript">
37379  */
37380  /**
37381  * @class Roo.grid.AbstractGridView
37382  * @extends Roo.util.Observable
37383  * @abstract
37384  * Abstract base class for grid Views
37385  * @constructor
37386  */
37387 Roo.grid.AbstractGridView = function(){
37388         this.grid = null;
37389         
37390         this.events = {
37391             "beforerowremoved" : true,
37392             "beforerowsinserted" : true,
37393             "beforerefresh" : true,
37394             "rowremoved" : true,
37395             "rowsinserted" : true,
37396             "rowupdated" : true,
37397             "refresh" : true
37398         };
37399     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37400 };
37401
37402 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37403     rowClass : "x-grid-row",
37404     cellClass : "x-grid-cell",
37405     tdClass : "x-grid-td",
37406     hdClass : "x-grid-hd",
37407     splitClass : "x-grid-hd-split",
37408     
37409     init: function(grid){
37410         this.grid = grid;
37411                 var cid = this.grid.getGridEl().id;
37412         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37413         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37414         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37415         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37416         },
37417         
37418     getColumnRenderers : function(){
37419         var renderers = [];
37420         var cm = this.grid.colModel;
37421         var colCount = cm.getColumnCount();
37422         for(var i = 0; i < colCount; i++){
37423             renderers[i] = cm.getRenderer(i);
37424         }
37425         return renderers;
37426     },
37427     
37428     getColumnIds : function(){
37429         var ids = [];
37430         var cm = this.grid.colModel;
37431         var colCount = cm.getColumnCount();
37432         for(var i = 0; i < colCount; i++){
37433             ids[i] = cm.getColumnId(i);
37434         }
37435         return ids;
37436     },
37437     
37438     getDataIndexes : function(){
37439         if(!this.indexMap){
37440             this.indexMap = this.buildIndexMap();
37441         }
37442         return this.indexMap.colToData;
37443     },
37444     
37445     getColumnIndexByDataIndex : function(dataIndex){
37446         if(!this.indexMap){
37447             this.indexMap = this.buildIndexMap();
37448         }
37449         return this.indexMap.dataToCol[dataIndex];
37450     },
37451     
37452     /**
37453      * Set a css style for a column dynamically. 
37454      * @param {Number} colIndex The index of the column
37455      * @param {String} name The css property name
37456      * @param {String} value The css value
37457      */
37458     setCSSStyle : function(colIndex, name, value){
37459         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37460         Roo.util.CSS.updateRule(selector, name, value);
37461     },
37462     
37463     generateRules : function(cm){
37464         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37465         Roo.util.CSS.removeStyleSheet(rulesId);
37466         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37467             var cid = cm.getColumnId(i);
37468             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37469                          this.tdSelector, cid, " {\n}\n",
37470                          this.hdSelector, cid, " {\n}\n",
37471                          this.splitSelector, cid, " {\n}\n");
37472         }
37473         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37474     }
37475 });/*
37476  * Based on:
37477  * Ext JS Library 1.1.1
37478  * Copyright(c) 2006-2007, Ext JS, LLC.
37479  *
37480  * Originally Released Under LGPL - original licence link has changed is not relivant.
37481  *
37482  * Fork - LGPL
37483  * <script type="text/javascript">
37484  */
37485
37486 // private
37487 // This is a support class used internally by the Grid components
37488 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37489     this.grid = grid;
37490     this.view = grid.getView();
37491     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37492     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37493     if(hd2){
37494         this.setHandleElId(Roo.id(hd));
37495         this.setOuterHandleElId(Roo.id(hd2));
37496     }
37497     this.scroll = false;
37498 };
37499 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37500     maxDragWidth: 120,
37501     getDragData : function(e){
37502         var t = Roo.lib.Event.getTarget(e);
37503         var h = this.view.findHeaderCell(t);
37504         if(h){
37505             return {ddel: h.firstChild, header:h};
37506         }
37507         return false;
37508     },
37509
37510     onInitDrag : function(e){
37511         this.view.headersDisabled = true;
37512         var clone = this.dragData.ddel.cloneNode(true);
37513         clone.id = Roo.id();
37514         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37515         this.proxy.update(clone);
37516         return true;
37517     },
37518
37519     afterValidDrop : function(){
37520         var v = this.view;
37521         setTimeout(function(){
37522             v.headersDisabled = false;
37523         }, 50);
37524     },
37525
37526     afterInvalidDrop : function(){
37527         var v = this.view;
37528         setTimeout(function(){
37529             v.headersDisabled = false;
37530         }, 50);
37531     }
37532 });
37533 /*
37534  * Based on:
37535  * Ext JS Library 1.1.1
37536  * Copyright(c) 2006-2007, Ext JS, LLC.
37537  *
37538  * Originally Released Under LGPL - original licence link has changed is not relivant.
37539  *
37540  * Fork - LGPL
37541  * <script type="text/javascript">
37542  */
37543 // private
37544 // This is a support class used internally by the Grid components
37545 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37546     this.grid = grid;
37547     this.view = grid.getView();
37548     // split the proxies so they don't interfere with mouse events
37549     this.proxyTop = Roo.DomHelper.append(document.body, {
37550         cls:"col-move-top", html:"&#160;"
37551     }, true);
37552     this.proxyBottom = Roo.DomHelper.append(document.body, {
37553         cls:"col-move-bottom", html:"&#160;"
37554     }, true);
37555     this.proxyTop.hide = this.proxyBottom.hide = function(){
37556         this.setLeftTop(-100,-100);
37557         this.setStyle("visibility", "hidden");
37558     };
37559     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37560     // temporarily disabled
37561     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37562     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37563 };
37564 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37565     proxyOffsets : [-4, -9],
37566     fly: Roo.Element.fly,
37567
37568     getTargetFromEvent : function(e){
37569         var t = Roo.lib.Event.getTarget(e);
37570         var cindex = this.view.findCellIndex(t);
37571         if(cindex !== false){
37572             return this.view.getHeaderCell(cindex);
37573         }
37574         return null;
37575     },
37576
37577     nextVisible : function(h){
37578         var v = this.view, cm = this.grid.colModel;
37579         h = h.nextSibling;
37580         while(h){
37581             if(!cm.isHidden(v.getCellIndex(h))){
37582                 return h;
37583             }
37584             h = h.nextSibling;
37585         }
37586         return null;
37587     },
37588
37589     prevVisible : function(h){
37590         var v = this.view, cm = this.grid.colModel;
37591         h = h.prevSibling;
37592         while(h){
37593             if(!cm.isHidden(v.getCellIndex(h))){
37594                 return h;
37595             }
37596             h = h.prevSibling;
37597         }
37598         return null;
37599     },
37600
37601     positionIndicator : function(h, n, e){
37602         var x = Roo.lib.Event.getPageX(e);
37603         var r = Roo.lib.Dom.getRegion(n.firstChild);
37604         var px, pt, py = r.top + this.proxyOffsets[1];
37605         if((r.right - x) <= (r.right-r.left)/2){
37606             px = r.right+this.view.borderWidth;
37607             pt = "after";
37608         }else{
37609             px = r.left;
37610             pt = "before";
37611         }
37612         var oldIndex = this.view.getCellIndex(h);
37613         var newIndex = this.view.getCellIndex(n);
37614
37615         if(this.grid.colModel.isFixed(newIndex)){
37616             return false;
37617         }
37618
37619         var locked = this.grid.colModel.isLocked(newIndex);
37620
37621         if(pt == "after"){
37622             newIndex++;
37623         }
37624         if(oldIndex < newIndex){
37625             newIndex--;
37626         }
37627         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37628             return false;
37629         }
37630         px +=  this.proxyOffsets[0];
37631         this.proxyTop.setLeftTop(px, py);
37632         this.proxyTop.show();
37633         if(!this.bottomOffset){
37634             this.bottomOffset = this.view.mainHd.getHeight();
37635         }
37636         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37637         this.proxyBottom.show();
37638         return pt;
37639     },
37640
37641     onNodeEnter : function(n, dd, e, data){
37642         if(data.header != n){
37643             this.positionIndicator(data.header, n, e);
37644         }
37645     },
37646
37647     onNodeOver : function(n, dd, e, data){
37648         var result = false;
37649         if(data.header != n){
37650             result = this.positionIndicator(data.header, n, e);
37651         }
37652         if(!result){
37653             this.proxyTop.hide();
37654             this.proxyBottom.hide();
37655         }
37656         return result ? this.dropAllowed : this.dropNotAllowed;
37657     },
37658
37659     onNodeOut : function(n, dd, e, data){
37660         this.proxyTop.hide();
37661         this.proxyBottom.hide();
37662     },
37663
37664     onNodeDrop : function(n, dd, e, data){
37665         var h = data.header;
37666         if(h != n){
37667             var cm = this.grid.colModel;
37668             var x = Roo.lib.Event.getPageX(e);
37669             var r = Roo.lib.Dom.getRegion(n.firstChild);
37670             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37671             var oldIndex = this.view.getCellIndex(h);
37672             var newIndex = this.view.getCellIndex(n);
37673             var locked = cm.isLocked(newIndex);
37674             if(pt == "after"){
37675                 newIndex++;
37676             }
37677             if(oldIndex < newIndex){
37678                 newIndex--;
37679             }
37680             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37681                 return false;
37682             }
37683             cm.setLocked(oldIndex, locked, true);
37684             cm.moveColumn(oldIndex, newIndex);
37685             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37686             return true;
37687         }
37688         return false;
37689     }
37690 });
37691 /*
37692  * Based on:
37693  * Ext JS Library 1.1.1
37694  * Copyright(c) 2006-2007, Ext JS, LLC.
37695  *
37696  * Originally Released Under LGPL - original licence link has changed is not relivant.
37697  *
37698  * Fork - LGPL
37699  * <script type="text/javascript">
37700  */
37701   
37702 /**
37703  * @class Roo.grid.GridView
37704  * @extends Roo.util.Observable
37705  *
37706  * @constructor
37707  * @param {Object} config
37708  */
37709 Roo.grid.GridView = function(config){
37710     Roo.grid.GridView.superclass.constructor.call(this);
37711     this.el = null;
37712
37713     Roo.apply(this, config);
37714 };
37715
37716 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37717
37718     unselectable :  'unselectable="on"',
37719     unselectableCls :  'x-unselectable',
37720     
37721     
37722     rowClass : "x-grid-row",
37723
37724     cellClass : "x-grid-col",
37725
37726     tdClass : "x-grid-td",
37727
37728     hdClass : "x-grid-hd",
37729
37730     splitClass : "x-grid-split",
37731
37732     sortClasses : ["sort-asc", "sort-desc"],
37733
37734     enableMoveAnim : false,
37735
37736     hlColor: "C3DAF9",
37737
37738     dh : Roo.DomHelper,
37739
37740     fly : Roo.Element.fly,
37741
37742     css : Roo.util.CSS,
37743
37744     borderWidth: 1,
37745
37746     splitOffset: 3,
37747
37748     scrollIncrement : 22,
37749
37750     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37751
37752     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37753
37754     bind : function(ds, cm){
37755         if(this.ds){
37756             this.ds.un("load", this.onLoad, this);
37757             this.ds.un("datachanged", this.onDataChange, this);
37758             this.ds.un("add", this.onAdd, this);
37759             this.ds.un("remove", this.onRemove, this);
37760             this.ds.un("update", this.onUpdate, this);
37761             this.ds.un("clear", this.onClear, this);
37762         }
37763         if(ds){
37764             ds.on("load", this.onLoad, this);
37765             ds.on("datachanged", this.onDataChange, this);
37766             ds.on("add", this.onAdd, this);
37767             ds.on("remove", this.onRemove, this);
37768             ds.on("update", this.onUpdate, this);
37769             ds.on("clear", this.onClear, this);
37770         }
37771         this.ds = ds;
37772
37773         if(this.cm){
37774             this.cm.un("widthchange", this.onColWidthChange, this);
37775             this.cm.un("headerchange", this.onHeaderChange, this);
37776             this.cm.un("hiddenchange", this.onHiddenChange, this);
37777             this.cm.un("columnmoved", this.onColumnMove, this);
37778             this.cm.un("columnlockchange", this.onColumnLock, this);
37779         }
37780         if(cm){
37781             this.generateRules(cm);
37782             cm.on("widthchange", this.onColWidthChange, this);
37783             cm.on("headerchange", this.onHeaderChange, this);
37784             cm.on("hiddenchange", this.onHiddenChange, this);
37785             cm.on("columnmoved", this.onColumnMove, this);
37786             cm.on("columnlockchange", this.onColumnLock, this);
37787         }
37788         this.cm = cm;
37789     },
37790
37791     init: function(grid){
37792         Roo.grid.GridView.superclass.init.call(this, grid);
37793
37794         this.bind(grid.dataSource, grid.colModel);
37795
37796         grid.on("headerclick", this.handleHeaderClick, this);
37797
37798         if(grid.trackMouseOver){
37799             grid.on("mouseover", this.onRowOver, this);
37800             grid.on("mouseout", this.onRowOut, this);
37801         }
37802         grid.cancelTextSelection = function(){};
37803         this.gridId = grid.id;
37804
37805         var tpls = this.templates || {};
37806
37807         if(!tpls.master){
37808             tpls.master = new Roo.Template(
37809                '<div class="x-grid" hidefocus="true">',
37810                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37811                   '<div class="x-grid-topbar"></div>',
37812                   '<div class="x-grid-scroller"><div></div></div>',
37813                   '<div class="x-grid-locked">',
37814                       '<div class="x-grid-header">{lockedHeader}</div>',
37815                       '<div class="x-grid-body">{lockedBody}</div>',
37816                   "</div>",
37817                   '<div class="x-grid-viewport">',
37818                       '<div class="x-grid-header">{header}</div>',
37819                       '<div class="x-grid-body">{body}</div>',
37820                   "</div>",
37821                   '<div class="x-grid-bottombar"></div>',
37822                  
37823                   '<div class="x-grid-resize-proxy">&#160;</div>',
37824                "</div>"
37825             );
37826             tpls.master.disableformats = true;
37827         }
37828
37829         if(!tpls.header){
37830             tpls.header = new Roo.Template(
37831                '<table border="0" cellspacing="0" cellpadding="0">',
37832                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37833                "</table>{splits}"
37834             );
37835             tpls.header.disableformats = true;
37836         }
37837         tpls.header.compile();
37838
37839         if(!tpls.hcell){
37840             tpls.hcell = new Roo.Template(
37841                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37842                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37843                 "</div></td>"
37844              );
37845              tpls.hcell.disableFormats = true;
37846         }
37847         tpls.hcell.compile();
37848
37849         if(!tpls.hsplit){
37850             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37851                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37852             tpls.hsplit.disableFormats = true;
37853         }
37854         tpls.hsplit.compile();
37855
37856         if(!tpls.body){
37857             tpls.body = new Roo.Template(
37858                '<table border="0" cellspacing="0" cellpadding="0">',
37859                "<tbody>{rows}</tbody>",
37860                "</table>"
37861             );
37862             tpls.body.disableFormats = true;
37863         }
37864         tpls.body.compile();
37865
37866         if(!tpls.row){
37867             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37868             tpls.row.disableFormats = true;
37869         }
37870         tpls.row.compile();
37871
37872         if(!tpls.cell){
37873             tpls.cell = new Roo.Template(
37874                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37875                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37876                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37877                 "</td>"
37878             );
37879             tpls.cell.disableFormats = true;
37880         }
37881         tpls.cell.compile();
37882
37883         this.templates = tpls;
37884     },
37885
37886     // remap these for backwards compat
37887     onColWidthChange : function(){
37888         this.updateColumns.apply(this, arguments);
37889     },
37890     onHeaderChange : function(){
37891         this.updateHeaders.apply(this, arguments);
37892     }, 
37893     onHiddenChange : function(){
37894         this.handleHiddenChange.apply(this, arguments);
37895     },
37896     onColumnMove : function(){
37897         this.handleColumnMove.apply(this, arguments);
37898     },
37899     onColumnLock : function(){
37900         this.handleLockChange.apply(this, arguments);
37901     },
37902
37903     onDataChange : function(){
37904         this.refresh();
37905         this.updateHeaderSortState();
37906     },
37907
37908     onClear : function(){
37909         this.refresh();
37910     },
37911
37912     onUpdate : function(ds, record){
37913         this.refreshRow(record);
37914     },
37915
37916     refreshRow : function(record){
37917         var ds = this.ds, index;
37918         if(typeof record == 'number'){
37919             index = record;
37920             record = ds.getAt(index);
37921         }else{
37922             index = ds.indexOf(record);
37923         }
37924         this.insertRows(ds, index, index, true);
37925         this.onRemove(ds, record, index+1, true);
37926         this.syncRowHeights(index, index);
37927         this.layout();
37928         this.fireEvent("rowupdated", this, index, record);
37929     },
37930
37931     onAdd : function(ds, records, index){
37932         this.insertRows(ds, index, index + (records.length-1));
37933     },
37934
37935     onRemove : function(ds, record, index, isUpdate){
37936         if(isUpdate !== true){
37937             this.fireEvent("beforerowremoved", this, index, record);
37938         }
37939         var bt = this.getBodyTable(), lt = this.getLockedTable();
37940         if(bt.rows[index]){
37941             bt.firstChild.removeChild(bt.rows[index]);
37942         }
37943         if(lt.rows[index]){
37944             lt.firstChild.removeChild(lt.rows[index]);
37945         }
37946         if(isUpdate !== true){
37947             this.stripeRows(index);
37948             this.syncRowHeights(index, index);
37949             this.layout();
37950             this.fireEvent("rowremoved", this, index, record);
37951         }
37952     },
37953
37954     onLoad : function(){
37955         this.scrollToTop();
37956     },
37957
37958     /**
37959      * Scrolls the grid to the top
37960      */
37961     scrollToTop : function(){
37962         if(this.scroller){
37963             this.scroller.dom.scrollTop = 0;
37964             this.syncScroll();
37965         }
37966     },
37967
37968     /**
37969      * Gets a panel in the header of the grid that can be used for toolbars etc.
37970      * After modifying the contents of this panel a call to grid.autoSize() may be
37971      * required to register any changes in size.
37972      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37973      * @return Roo.Element
37974      */
37975     getHeaderPanel : function(doShow){
37976         if(doShow){
37977             this.headerPanel.show();
37978         }
37979         return this.headerPanel;
37980     },
37981
37982     /**
37983      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37984      * After modifying the contents of this panel a call to grid.autoSize() may be
37985      * required to register any changes in size.
37986      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37987      * @return Roo.Element
37988      */
37989     getFooterPanel : function(doShow){
37990         if(doShow){
37991             this.footerPanel.show();
37992         }
37993         return this.footerPanel;
37994     },
37995
37996     initElements : function(){
37997         var E = Roo.Element;
37998         var el = this.grid.getGridEl().dom.firstChild;
37999         var cs = el.childNodes;
38000
38001         this.el = new E(el);
38002         
38003          this.focusEl = new E(el.firstChild);
38004         this.focusEl.swallowEvent("click", true);
38005         
38006         this.headerPanel = new E(cs[1]);
38007         this.headerPanel.enableDisplayMode("block");
38008
38009         this.scroller = new E(cs[2]);
38010         this.scrollSizer = new E(this.scroller.dom.firstChild);
38011
38012         this.lockedWrap = new E(cs[3]);
38013         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38014         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38015
38016         this.mainWrap = new E(cs[4]);
38017         this.mainHd = new E(this.mainWrap.dom.firstChild);
38018         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38019
38020         this.footerPanel = new E(cs[5]);
38021         this.footerPanel.enableDisplayMode("block");
38022
38023         this.resizeProxy = new E(cs[6]);
38024
38025         this.headerSelector = String.format(
38026            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38027            this.lockedHd.id, this.mainHd.id
38028         );
38029
38030         this.splitterSelector = String.format(
38031            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38032            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38033         );
38034     },
38035     idToCssName : function(s)
38036     {
38037         return s.replace(/[^a-z0-9]+/ig, '-');
38038     },
38039
38040     getHeaderCell : function(index){
38041         return Roo.DomQuery.select(this.headerSelector)[index];
38042     },
38043
38044     getHeaderCellMeasure : function(index){
38045         return this.getHeaderCell(index).firstChild;
38046     },
38047
38048     getHeaderCellText : function(index){
38049         return this.getHeaderCell(index).firstChild.firstChild;
38050     },
38051
38052     getLockedTable : function(){
38053         return this.lockedBody.dom.firstChild;
38054     },
38055
38056     getBodyTable : function(){
38057         return this.mainBody.dom.firstChild;
38058     },
38059
38060     getLockedRow : function(index){
38061         return this.getLockedTable().rows[index];
38062     },
38063
38064     getRow : function(index){
38065         return this.getBodyTable().rows[index];
38066     },
38067
38068     getRowComposite : function(index){
38069         if(!this.rowEl){
38070             this.rowEl = new Roo.CompositeElementLite();
38071         }
38072         var els = [], lrow, mrow;
38073         if(lrow = this.getLockedRow(index)){
38074             els.push(lrow);
38075         }
38076         if(mrow = this.getRow(index)){
38077             els.push(mrow);
38078         }
38079         this.rowEl.elements = els;
38080         return this.rowEl;
38081     },
38082     /**
38083      * Gets the 'td' of the cell
38084      * 
38085      * @param {Integer} rowIndex row to select
38086      * @param {Integer} colIndex column to select
38087      * 
38088      * @return {Object} 
38089      */
38090     getCell : function(rowIndex, colIndex){
38091         var locked = this.cm.getLockedCount();
38092         var source;
38093         if(colIndex < locked){
38094             source = this.lockedBody.dom.firstChild;
38095         }else{
38096             source = this.mainBody.dom.firstChild;
38097             colIndex -= locked;
38098         }
38099         return source.rows[rowIndex].childNodes[colIndex];
38100     },
38101
38102     getCellText : function(rowIndex, colIndex){
38103         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38104     },
38105
38106     getCellBox : function(cell){
38107         var b = this.fly(cell).getBox();
38108         if(Roo.isOpera){ // opera fails to report the Y
38109             b.y = cell.offsetTop + this.mainBody.getY();
38110         }
38111         return b;
38112     },
38113
38114     getCellIndex : function(cell){
38115         var id = String(cell.className).match(this.cellRE);
38116         if(id){
38117             return parseInt(id[1], 10);
38118         }
38119         return 0;
38120     },
38121
38122     findHeaderIndex : function(n){
38123         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38124         return r ? this.getCellIndex(r) : false;
38125     },
38126
38127     findHeaderCell : function(n){
38128         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38129         return r ? r : false;
38130     },
38131
38132     findRowIndex : function(n){
38133         if(!n){
38134             return false;
38135         }
38136         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38137         return r ? r.rowIndex : false;
38138     },
38139
38140     findCellIndex : function(node){
38141         var stop = this.el.dom;
38142         while(node && node != stop){
38143             if(this.findRE.test(node.className)){
38144                 return this.getCellIndex(node);
38145             }
38146             node = node.parentNode;
38147         }
38148         return false;
38149     },
38150
38151     getColumnId : function(index){
38152         return this.cm.getColumnId(index);
38153     },
38154
38155     getSplitters : function()
38156     {
38157         if(this.splitterSelector){
38158            return Roo.DomQuery.select(this.splitterSelector);
38159         }else{
38160             return null;
38161       }
38162     },
38163
38164     getSplitter : function(index){
38165         return this.getSplitters()[index];
38166     },
38167
38168     onRowOver : function(e, t){
38169         var row;
38170         if((row = this.findRowIndex(t)) !== false){
38171             this.getRowComposite(row).addClass("x-grid-row-over");
38172         }
38173     },
38174
38175     onRowOut : function(e, t){
38176         var row;
38177         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38178             this.getRowComposite(row).removeClass("x-grid-row-over");
38179         }
38180     },
38181
38182     renderHeaders : function(){
38183         var cm = this.cm;
38184         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38185         var cb = [], lb = [], sb = [], lsb = [], p = {};
38186         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38187             p.cellId = "x-grid-hd-0-" + i;
38188             p.splitId = "x-grid-csplit-0-" + i;
38189             p.id = cm.getColumnId(i);
38190             p.value = cm.getColumnHeader(i) || "";
38191             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38192             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38193             if(!cm.isLocked(i)){
38194                 cb[cb.length] = ct.apply(p);
38195                 sb[sb.length] = st.apply(p);
38196             }else{
38197                 lb[lb.length] = ct.apply(p);
38198                 lsb[lsb.length] = st.apply(p);
38199             }
38200         }
38201         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38202                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38203     },
38204
38205     updateHeaders : function(){
38206         var html = this.renderHeaders();
38207         this.lockedHd.update(html[0]);
38208         this.mainHd.update(html[1]);
38209     },
38210
38211     /**
38212      * Focuses the specified row.
38213      * @param {Number} row The row index
38214      */
38215     focusRow : function(row)
38216     {
38217         //Roo.log('GridView.focusRow');
38218         var x = this.scroller.dom.scrollLeft;
38219         this.focusCell(row, 0, false);
38220         this.scroller.dom.scrollLeft = x;
38221     },
38222
38223     /**
38224      * Focuses the specified cell.
38225      * @param {Number} row The row index
38226      * @param {Number} col The column index
38227      * @param {Boolean} hscroll false to disable horizontal scrolling
38228      */
38229     focusCell : function(row, col, hscroll)
38230     {
38231         //Roo.log('GridView.focusCell');
38232         var el = this.ensureVisible(row, col, hscroll);
38233         this.focusEl.alignTo(el, "tl-tl");
38234         if(Roo.isGecko){
38235             this.focusEl.focus();
38236         }else{
38237             this.focusEl.focus.defer(1, this.focusEl);
38238         }
38239     },
38240
38241     /**
38242      * Scrolls the specified cell into view
38243      * @param {Number} row The row index
38244      * @param {Number} col The column index
38245      * @param {Boolean} hscroll false to disable horizontal scrolling
38246      */
38247     ensureVisible : function(row, col, hscroll)
38248     {
38249         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38250         //return null; //disable for testing.
38251         if(typeof row != "number"){
38252             row = row.rowIndex;
38253         }
38254         if(row < 0 && row >= this.ds.getCount()){
38255             return  null;
38256         }
38257         col = (col !== undefined ? col : 0);
38258         var cm = this.grid.colModel;
38259         while(cm.isHidden(col)){
38260             col++;
38261         }
38262
38263         var el = this.getCell(row, col);
38264         if(!el){
38265             return null;
38266         }
38267         var c = this.scroller.dom;
38268
38269         var ctop = parseInt(el.offsetTop, 10);
38270         var cleft = parseInt(el.offsetLeft, 10);
38271         var cbot = ctop + el.offsetHeight;
38272         var cright = cleft + el.offsetWidth;
38273         
38274         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38275         var stop = parseInt(c.scrollTop, 10);
38276         var sleft = parseInt(c.scrollLeft, 10);
38277         var sbot = stop + ch;
38278         var sright = sleft + c.clientWidth;
38279         /*
38280         Roo.log('GridView.ensureVisible:' +
38281                 ' ctop:' + ctop +
38282                 ' c.clientHeight:' + c.clientHeight +
38283                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38284                 ' stop:' + stop +
38285                 ' cbot:' + cbot +
38286                 ' sbot:' + sbot +
38287                 ' ch:' + ch  
38288                 );
38289         */
38290         if(ctop < stop){
38291             c.scrollTop = ctop;
38292             //Roo.log("set scrolltop to ctop DISABLE?");
38293         }else if(cbot > sbot){
38294             //Roo.log("set scrolltop to cbot-ch");
38295             c.scrollTop = cbot-ch;
38296         }
38297         
38298         if(hscroll !== false){
38299             if(cleft < sleft){
38300                 c.scrollLeft = cleft;
38301             }else if(cright > sright){
38302                 c.scrollLeft = cright-c.clientWidth;
38303             }
38304         }
38305          
38306         return el;
38307     },
38308
38309     updateColumns : function(){
38310         this.grid.stopEditing();
38311         var cm = this.grid.colModel, colIds = this.getColumnIds();
38312         //var totalWidth = cm.getTotalWidth();
38313         var pos = 0;
38314         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38315             //if(cm.isHidden(i)) continue;
38316             var w = cm.getColumnWidth(i);
38317             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38318             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38319         }
38320         this.updateSplitters();
38321     },
38322
38323     generateRules : function(cm){
38324         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38325         Roo.util.CSS.removeStyleSheet(rulesId);
38326         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38327             var cid = cm.getColumnId(i);
38328             var align = '';
38329             if(cm.config[i].align){
38330                 align = 'text-align:'+cm.config[i].align+';';
38331             }
38332             var hidden = '';
38333             if(cm.isHidden(i)){
38334                 hidden = 'display:none;';
38335             }
38336             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38337             ruleBuf.push(
38338                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38339                     this.hdSelector, cid, " {\n", align, width, "}\n",
38340                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38341                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38342         }
38343         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38344     },
38345
38346     updateSplitters : function(){
38347         var cm = this.cm, s = this.getSplitters();
38348         if(s){ // splitters not created yet
38349             var pos = 0, locked = true;
38350             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38351                 if(cm.isHidden(i)) {
38352                     continue;
38353                 }
38354                 var w = cm.getColumnWidth(i); // make sure it's a number
38355                 if(!cm.isLocked(i) && locked){
38356                     pos = 0;
38357                     locked = false;
38358                 }
38359                 pos += w;
38360                 s[i].style.left = (pos-this.splitOffset) + "px";
38361             }
38362         }
38363     },
38364
38365     handleHiddenChange : function(colModel, colIndex, hidden){
38366         if(hidden){
38367             this.hideColumn(colIndex);
38368         }else{
38369             this.unhideColumn(colIndex);
38370         }
38371     },
38372
38373     hideColumn : function(colIndex){
38374         var cid = this.getColumnId(colIndex);
38375         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38376         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38377         if(Roo.isSafari){
38378             this.updateHeaders();
38379         }
38380         this.updateSplitters();
38381         this.layout();
38382     },
38383
38384     unhideColumn : function(colIndex){
38385         var cid = this.getColumnId(colIndex);
38386         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38387         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38388
38389         if(Roo.isSafari){
38390             this.updateHeaders();
38391         }
38392         this.updateSplitters();
38393         this.layout();
38394     },
38395
38396     insertRows : function(dm, firstRow, lastRow, isUpdate){
38397         if(firstRow == 0 && lastRow == dm.getCount()-1){
38398             this.refresh();
38399         }else{
38400             if(!isUpdate){
38401                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38402             }
38403             var s = this.getScrollState();
38404             var markup = this.renderRows(firstRow, lastRow);
38405             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38406             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38407             this.restoreScroll(s);
38408             if(!isUpdate){
38409                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38410                 this.syncRowHeights(firstRow, lastRow);
38411                 this.stripeRows(firstRow);
38412                 this.layout();
38413             }
38414         }
38415     },
38416
38417     bufferRows : function(markup, target, index){
38418         var before = null, trows = target.rows, tbody = target.tBodies[0];
38419         if(index < trows.length){
38420             before = trows[index];
38421         }
38422         var b = document.createElement("div");
38423         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38424         var rows = b.firstChild.rows;
38425         for(var i = 0, len = rows.length; i < len; i++){
38426             if(before){
38427                 tbody.insertBefore(rows[0], before);
38428             }else{
38429                 tbody.appendChild(rows[0]);
38430             }
38431         }
38432         b.innerHTML = "";
38433         b = null;
38434     },
38435
38436     deleteRows : function(dm, firstRow, lastRow){
38437         if(dm.getRowCount()<1){
38438             this.fireEvent("beforerefresh", this);
38439             this.mainBody.update("");
38440             this.lockedBody.update("");
38441             this.fireEvent("refresh", this);
38442         }else{
38443             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38444             var bt = this.getBodyTable();
38445             var tbody = bt.firstChild;
38446             var rows = bt.rows;
38447             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38448                 tbody.removeChild(rows[firstRow]);
38449             }
38450             this.stripeRows(firstRow);
38451             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38452         }
38453     },
38454
38455     updateRows : function(dataSource, firstRow, lastRow){
38456         var s = this.getScrollState();
38457         this.refresh();
38458         this.restoreScroll(s);
38459     },
38460
38461     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38462         if(!noRefresh){
38463            this.refresh();
38464         }
38465         this.updateHeaderSortState();
38466     },
38467
38468     getScrollState : function(){
38469         
38470         var sb = this.scroller.dom;
38471         return {left: sb.scrollLeft, top: sb.scrollTop};
38472     },
38473
38474     stripeRows : function(startRow){
38475         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38476             return;
38477         }
38478         startRow = startRow || 0;
38479         var rows = this.getBodyTable().rows;
38480         var lrows = this.getLockedTable().rows;
38481         var cls = ' x-grid-row-alt ';
38482         for(var i = startRow, len = rows.length; i < len; i++){
38483             var row = rows[i], lrow = lrows[i];
38484             var isAlt = ((i+1) % 2 == 0);
38485             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38486             if(isAlt == hasAlt){
38487                 continue;
38488             }
38489             if(isAlt){
38490                 row.className += " x-grid-row-alt";
38491             }else{
38492                 row.className = row.className.replace("x-grid-row-alt", "");
38493             }
38494             if(lrow){
38495                 lrow.className = row.className;
38496             }
38497         }
38498     },
38499
38500     restoreScroll : function(state){
38501         //Roo.log('GridView.restoreScroll');
38502         var sb = this.scroller.dom;
38503         sb.scrollLeft = state.left;
38504         sb.scrollTop = state.top;
38505         this.syncScroll();
38506     },
38507
38508     syncScroll : function(){
38509         //Roo.log('GridView.syncScroll');
38510         var sb = this.scroller.dom;
38511         var sh = this.mainHd.dom;
38512         var bs = this.mainBody.dom;
38513         var lv = this.lockedBody.dom;
38514         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38515         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38516     },
38517
38518     handleScroll : function(e){
38519         this.syncScroll();
38520         var sb = this.scroller.dom;
38521         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38522         e.stopEvent();
38523     },
38524
38525     handleWheel : function(e){
38526         var d = e.getWheelDelta();
38527         this.scroller.dom.scrollTop -= d*22;
38528         // set this here to prevent jumpy scrolling on large tables
38529         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38530         e.stopEvent();
38531     },
38532
38533     renderRows : function(startRow, endRow){
38534         // pull in all the crap needed to render rows
38535         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38536         var colCount = cm.getColumnCount();
38537
38538         if(ds.getCount() < 1){
38539             return ["", ""];
38540         }
38541
38542         // build a map for all the columns
38543         var cs = [];
38544         for(var i = 0; i < colCount; i++){
38545             var name = cm.getDataIndex(i);
38546             cs[i] = {
38547                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38548                 renderer : cm.getRenderer(i),
38549                 id : cm.getColumnId(i),
38550                 locked : cm.isLocked(i),
38551                 has_editor : cm.isCellEditable(i)
38552             };
38553         }
38554
38555         startRow = startRow || 0;
38556         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38557
38558         // records to render
38559         var rs = ds.getRange(startRow, endRow);
38560
38561         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38562     },
38563
38564     // As much as I hate to duplicate code, this was branched because FireFox really hates
38565     // [].join("") on strings. The performance difference was substantial enough to
38566     // branch this function
38567     doRender : Roo.isGecko ?
38568             function(cs, rs, ds, startRow, colCount, stripe){
38569                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38570                 // buffers
38571                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38572                 
38573                 var hasListener = this.grid.hasListener('rowclass');
38574                 var rowcfg = {};
38575                 for(var j = 0, len = rs.length; j < len; j++){
38576                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38577                     for(var i = 0; i < colCount; i++){
38578                         c = cs[i];
38579                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38580                         p.id = c.id;
38581                         p.css = p.attr = "";
38582                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38583                         if(p.value == undefined || p.value === "") {
38584                             p.value = "&#160;";
38585                         }
38586                         if(c.has_editor){
38587                             p.css += ' x-grid-editable-cell';
38588                         }
38589                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38590                             p.css +=  ' x-grid-dirty-cell';
38591                         }
38592                         var markup = ct.apply(p);
38593                         if(!c.locked){
38594                             cb+= markup;
38595                         }else{
38596                             lcb+= markup;
38597                         }
38598                     }
38599                     var alt = [];
38600                     if(stripe && ((rowIndex+1) % 2 == 0)){
38601                         alt.push("x-grid-row-alt")
38602                     }
38603                     if(r.dirty){
38604                         alt.push(  " x-grid-dirty-row");
38605                     }
38606                     rp.cells = lcb;
38607                     if(this.getRowClass){
38608                         alt.push(this.getRowClass(r, rowIndex));
38609                     }
38610                     if (hasListener) {
38611                         rowcfg = {
38612                              
38613                             record: r,
38614                             rowIndex : rowIndex,
38615                             rowClass : ''
38616                         };
38617                         this.grid.fireEvent('rowclass', this, rowcfg);
38618                         alt.push(rowcfg.rowClass);
38619                     }
38620                     rp.alt = alt.join(" ");
38621                     lbuf+= rt.apply(rp);
38622                     rp.cells = cb;
38623                     buf+=  rt.apply(rp);
38624                 }
38625                 return [lbuf, buf];
38626             } :
38627             function(cs, rs, ds, startRow, colCount, stripe){
38628                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38629                 // buffers
38630                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38631                 var hasListener = this.grid.hasListener('rowclass');
38632  
38633                 var rowcfg = {};
38634                 for(var j = 0, len = rs.length; j < len; j++){
38635                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38636                     for(var i = 0; i < colCount; i++){
38637                         c = cs[i];
38638                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38639                         p.id = c.id;
38640                         p.css = p.attr = "";
38641                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38642                         if(p.value == undefined || p.value === "") {
38643                             p.value = "&#160;";
38644                         }
38645                         //Roo.log(c);
38646                          if(c.has_editor){
38647                             p.css += ' x-grid-editable-cell';
38648                         }
38649                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38650                             p.css += ' x-grid-dirty-cell' 
38651                         }
38652                         
38653                         var markup = ct.apply(p);
38654                         if(!c.locked){
38655                             cb[cb.length] = markup;
38656                         }else{
38657                             lcb[lcb.length] = markup;
38658                         }
38659                     }
38660                     var alt = [];
38661                     if(stripe && ((rowIndex+1) % 2 == 0)){
38662                         alt.push( "x-grid-row-alt");
38663                     }
38664                     if(r.dirty){
38665                         alt.push(" x-grid-dirty-row");
38666                     }
38667                     rp.cells = lcb;
38668                     if(this.getRowClass){
38669                         alt.push( this.getRowClass(r, rowIndex));
38670                     }
38671                     if (hasListener) {
38672                         rowcfg = {
38673                              
38674                             record: r,
38675                             rowIndex : rowIndex,
38676                             rowClass : ''
38677                         };
38678                         this.grid.fireEvent('rowclass', this, rowcfg);
38679                         alt.push(rowcfg.rowClass);
38680                     }
38681                     
38682                     rp.alt = alt.join(" ");
38683                     rp.cells = lcb.join("");
38684                     lbuf[lbuf.length] = rt.apply(rp);
38685                     rp.cells = cb.join("");
38686                     buf[buf.length] =  rt.apply(rp);
38687                 }
38688                 return [lbuf.join(""), buf.join("")];
38689             },
38690
38691     renderBody : function(){
38692         var markup = this.renderRows();
38693         var bt = this.templates.body;
38694         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38695     },
38696
38697     /**
38698      * Refreshes the grid
38699      * @param {Boolean} headersToo
38700      */
38701     refresh : function(headersToo){
38702         this.fireEvent("beforerefresh", this);
38703         this.grid.stopEditing();
38704         var result = this.renderBody();
38705         this.lockedBody.update(result[0]);
38706         this.mainBody.update(result[1]);
38707         if(headersToo === true){
38708             this.updateHeaders();
38709             this.updateColumns();
38710             this.updateSplitters();
38711             this.updateHeaderSortState();
38712         }
38713         this.syncRowHeights();
38714         this.layout();
38715         this.fireEvent("refresh", this);
38716     },
38717
38718     handleColumnMove : function(cm, oldIndex, newIndex){
38719         this.indexMap = null;
38720         var s = this.getScrollState();
38721         this.refresh(true);
38722         this.restoreScroll(s);
38723         this.afterMove(newIndex);
38724     },
38725
38726     afterMove : function(colIndex){
38727         if(this.enableMoveAnim && Roo.enableFx){
38728             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38729         }
38730         // if multisort - fix sortOrder, and reload..
38731         if (this.grid.dataSource.multiSort) {
38732             // the we can call sort again..
38733             var dm = this.grid.dataSource;
38734             var cm = this.grid.colModel;
38735             var so = [];
38736             for(var i = 0; i < cm.config.length; i++ ) {
38737                 
38738                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38739                     continue; // dont' bother, it's not in sort list or being set.
38740                 }
38741                 
38742                 so.push(cm.config[i].dataIndex);
38743             };
38744             dm.sortOrder = so;
38745             dm.load(dm.lastOptions);
38746             
38747             
38748         }
38749         
38750     },
38751
38752     updateCell : function(dm, rowIndex, dataIndex){
38753         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38754         if(typeof colIndex == "undefined"){ // not present in grid
38755             return;
38756         }
38757         var cm = this.grid.colModel;
38758         var cell = this.getCell(rowIndex, colIndex);
38759         var cellText = this.getCellText(rowIndex, colIndex);
38760
38761         var p = {
38762             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38763             id : cm.getColumnId(colIndex),
38764             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38765         };
38766         var renderer = cm.getRenderer(colIndex);
38767         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38768         if(typeof val == "undefined" || val === "") {
38769             val = "&#160;";
38770         }
38771         cellText.innerHTML = val;
38772         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38773         this.syncRowHeights(rowIndex, rowIndex);
38774     },
38775
38776     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38777         var maxWidth = 0;
38778         if(this.grid.autoSizeHeaders){
38779             var h = this.getHeaderCellMeasure(colIndex);
38780             maxWidth = Math.max(maxWidth, h.scrollWidth);
38781         }
38782         var tb, index;
38783         if(this.cm.isLocked(colIndex)){
38784             tb = this.getLockedTable();
38785             index = colIndex;
38786         }else{
38787             tb = this.getBodyTable();
38788             index = colIndex - this.cm.getLockedCount();
38789         }
38790         if(tb && tb.rows){
38791             var rows = tb.rows;
38792             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38793             for(var i = 0; i < stopIndex; i++){
38794                 var cell = rows[i].childNodes[index].firstChild;
38795                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38796             }
38797         }
38798         return maxWidth + /*margin for error in IE*/ 5;
38799     },
38800     /**
38801      * Autofit a column to its content.
38802      * @param {Number} colIndex
38803      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38804      */
38805      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38806          if(this.cm.isHidden(colIndex)){
38807              return; // can't calc a hidden column
38808          }
38809         if(forceMinSize){
38810             var cid = this.cm.getColumnId(colIndex);
38811             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38812            if(this.grid.autoSizeHeaders){
38813                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38814            }
38815         }
38816         var newWidth = this.calcColumnWidth(colIndex);
38817         this.cm.setColumnWidth(colIndex,
38818             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38819         if(!suppressEvent){
38820             this.grid.fireEvent("columnresize", colIndex, newWidth);
38821         }
38822     },
38823
38824     /**
38825      * Autofits all columns to their content and then expands to fit any extra space in the grid
38826      */
38827      autoSizeColumns : function(){
38828         var cm = this.grid.colModel;
38829         var colCount = cm.getColumnCount();
38830         for(var i = 0; i < colCount; i++){
38831             this.autoSizeColumn(i, true, true);
38832         }
38833         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38834             this.fitColumns();
38835         }else{
38836             this.updateColumns();
38837             this.layout();
38838         }
38839     },
38840
38841     /**
38842      * Autofits all columns to the grid's width proportionate with their current size
38843      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38844      */
38845     fitColumns : function(reserveScrollSpace){
38846         var cm = this.grid.colModel;
38847         var colCount = cm.getColumnCount();
38848         var cols = [];
38849         var width = 0;
38850         var i, w;
38851         for (i = 0; i < colCount; i++){
38852             if(!cm.isHidden(i) && !cm.isFixed(i)){
38853                 w = cm.getColumnWidth(i);
38854                 cols.push(i);
38855                 cols.push(w);
38856                 width += w;
38857             }
38858         }
38859         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38860         if(reserveScrollSpace){
38861             avail -= 17;
38862         }
38863         var frac = (avail - cm.getTotalWidth())/width;
38864         while (cols.length){
38865             w = cols.pop();
38866             i = cols.pop();
38867             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38868         }
38869         this.updateColumns();
38870         this.layout();
38871     },
38872
38873     onRowSelect : function(rowIndex){
38874         var row = this.getRowComposite(rowIndex);
38875         row.addClass("x-grid-row-selected");
38876     },
38877
38878     onRowDeselect : function(rowIndex){
38879         var row = this.getRowComposite(rowIndex);
38880         row.removeClass("x-grid-row-selected");
38881     },
38882
38883     onCellSelect : function(row, col){
38884         var cell = this.getCell(row, col);
38885         if(cell){
38886             Roo.fly(cell).addClass("x-grid-cell-selected");
38887         }
38888     },
38889
38890     onCellDeselect : function(row, col){
38891         var cell = this.getCell(row, col);
38892         if(cell){
38893             Roo.fly(cell).removeClass("x-grid-cell-selected");
38894         }
38895     },
38896
38897     updateHeaderSortState : function(){
38898         
38899         // sort state can be single { field: xxx, direction : yyy}
38900         // or   { xxx=>ASC , yyy : DESC ..... }
38901         
38902         var mstate = {};
38903         if (!this.ds.multiSort) { 
38904             var state = this.ds.getSortState();
38905             if(!state){
38906                 return;
38907             }
38908             mstate[state.field] = state.direction;
38909             // FIXME... - this is not used here.. but might be elsewhere..
38910             this.sortState = state;
38911             
38912         } else {
38913             mstate = this.ds.sortToggle;
38914         }
38915         //remove existing sort classes..
38916         
38917         var sc = this.sortClasses;
38918         var hds = this.el.select(this.headerSelector).removeClass(sc);
38919         
38920         for(var f in mstate) {
38921         
38922             var sortColumn = this.cm.findColumnIndex(f);
38923             
38924             if(sortColumn != -1){
38925                 var sortDir = mstate[f];        
38926                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38927             }
38928         }
38929         
38930          
38931         
38932     },
38933
38934
38935     handleHeaderClick : function(g, index,e){
38936         
38937         Roo.log("header click");
38938         
38939         if (Roo.isTouch) {
38940             // touch events on header are handled by context
38941             this.handleHdCtx(g,index,e);
38942             return;
38943         }
38944         
38945         
38946         if(this.headersDisabled){
38947             return;
38948         }
38949         var dm = g.dataSource, cm = g.colModel;
38950         if(!cm.isSortable(index)){
38951             return;
38952         }
38953         g.stopEditing();
38954         
38955         if (dm.multiSort) {
38956             // update the sortOrder
38957             var so = [];
38958             for(var i = 0; i < cm.config.length; i++ ) {
38959                 
38960                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38961                     continue; // dont' bother, it's not in sort list or being set.
38962                 }
38963                 
38964                 so.push(cm.config[i].dataIndex);
38965             };
38966             dm.sortOrder = so;
38967         }
38968         
38969         
38970         dm.sort(cm.getDataIndex(index));
38971     },
38972
38973
38974     destroy : function(){
38975         if(this.colMenu){
38976             this.colMenu.removeAll();
38977             Roo.menu.MenuMgr.unregister(this.colMenu);
38978             this.colMenu.getEl().remove();
38979             delete this.colMenu;
38980         }
38981         if(this.hmenu){
38982             this.hmenu.removeAll();
38983             Roo.menu.MenuMgr.unregister(this.hmenu);
38984             this.hmenu.getEl().remove();
38985             delete this.hmenu;
38986         }
38987         if(this.grid.enableColumnMove){
38988             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38989             if(dds){
38990                 for(var dd in dds){
38991                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38992                         var elid = dds[dd].dragElId;
38993                         dds[dd].unreg();
38994                         Roo.get(elid).remove();
38995                     } else if(dds[dd].config.isTarget){
38996                         dds[dd].proxyTop.remove();
38997                         dds[dd].proxyBottom.remove();
38998                         dds[dd].unreg();
38999                     }
39000                     if(Roo.dd.DDM.locationCache[dd]){
39001                         delete Roo.dd.DDM.locationCache[dd];
39002                     }
39003                 }
39004                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39005             }
39006         }
39007         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39008         this.bind(null, null);
39009         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39010     },
39011
39012     handleLockChange : function(){
39013         this.refresh(true);
39014     },
39015
39016     onDenyColumnLock : function(){
39017
39018     },
39019
39020     onDenyColumnHide : function(){
39021
39022     },
39023
39024     handleHdMenuClick : function(item){
39025         var index = this.hdCtxIndex;
39026         var cm = this.cm, ds = this.ds;
39027         switch(item.id){
39028             case "asc":
39029                 ds.sort(cm.getDataIndex(index), "ASC");
39030                 break;
39031             case "desc":
39032                 ds.sort(cm.getDataIndex(index), "DESC");
39033                 break;
39034             case "lock":
39035                 var lc = cm.getLockedCount();
39036                 if(cm.getColumnCount(true) <= lc+1){
39037                     this.onDenyColumnLock();
39038                     return;
39039                 }
39040                 if(lc != index){
39041                     cm.setLocked(index, true, true);
39042                     cm.moveColumn(index, lc);
39043                     this.grid.fireEvent("columnmove", index, lc);
39044                 }else{
39045                     cm.setLocked(index, true);
39046                 }
39047             break;
39048             case "unlock":
39049                 var lc = cm.getLockedCount();
39050                 if((lc-1) != index){
39051                     cm.setLocked(index, false, true);
39052                     cm.moveColumn(index, lc-1);
39053                     this.grid.fireEvent("columnmove", index, lc-1);
39054                 }else{
39055                     cm.setLocked(index, false);
39056                 }
39057             break;
39058             case 'wider': // used to expand cols on touch..
39059             case 'narrow':
39060                 var cw = cm.getColumnWidth(index);
39061                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39062                 cw = Math.max(0, cw);
39063                 cw = Math.min(cw,4000);
39064                 cm.setColumnWidth(index, cw);
39065                 break;
39066                 
39067             default:
39068                 index = cm.getIndexById(item.id.substr(4));
39069                 if(index != -1){
39070                     if(item.checked && cm.getColumnCount(true) <= 1){
39071                         this.onDenyColumnHide();
39072                         return false;
39073                     }
39074                     cm.setHidden(index, item.checked);
39075                 }
39076         }
39077         return true;
39078     },
39079
39080     beforeColMenuShow : function(){
39081         var cm = this.cm,  colCount = cm.getColumnCount();
39082         this.colMenu.removeAll();
39083         
39084         var items = [];
39085         for(var i = 0; i < colCount; i++){
39086             items.push({
39087                 id: "col-"+cm.getColumnId(i),
39088                 text: cm.getColumnHeader(i),
39089                 checked: !cm.isHidden(i),
39090                 hideOnClick:false
39091             });
39092         }
39093         
39094         if (this.grid.sortColMenu) {
39095             items.sort(function(a,b) {
39096                 if (a.text == b.text) {
39097                     return 0;
39098                 }
39099                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39100             });
39101         }
39102         
39103         for(var i = 0; i < colCount; i++){
39104             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39105         }
39106     },
39107
39108     handleHdCtx : function(g, index, e){
39109         e.stopEvent();
39110         var hd = this.getHeaderCell(index);
39111         this.hdCtxIndex = index;
39112         var ms = this.hmenu.items, cm = this.cm;
39113         ms.get("asc").setDisabled(!cm.isSortable(index));
39114         ms.get("desc").setDisabled(!cm.isSortable(index));
39115         if(this.grid.enableColLock !== false){
39116             ms.get("lock").setDisabled(cm.isLocked(index));
39117             ms.get("unlock").setDisabled(!cm.isLocked(index));
39118         }
39119         this.hmenu.show(hd, "tl-bl");
39120     },
39121
39122     handleHdOver : function(e){
39123         var hd = this.findHeaderCell(e.getTarget());
39124         if(hd && !this.headersDisabled){
39125             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39126                this.fly(hd).addClass("x-grid-hd-over");
39127             }
39128         }
39129     },
39130
39131     handleHdOut : function(e){
39132         var hd = this.findHeaderCell(e.getTarget());
39133         if(hd){
39134             this.fly(hd).removeClass("x-grid-hd-over");
39135         }
39136     },
39137
39138     handleSplitDblClick : function(e, t){
39139         var i = this.getCellIndex(t);
39140         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39141             this.autoSizeColumn(i, true);
39142             this.layout();
39143         }
39144     },
39145
39146     render : function(){
39147
39148         var cm = this.cm;
39149         var colCount = cm.getColumnCount();
39150
39151         if(this.grid.monitorWindowResize === true){
39152             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39153         }
39154         var header = this.renderHeaders();
39155         var body = this.templates.body.apply({rows:""});
39156         var html = this.templates.master.apply({
39157             lockedBody: body,
39158             body: body,
39159             lockedHeader: header[0],
39160             header: header[1]
39161         });
39162
39163         //this.updateColumns();
39164
39165         this.grid.getGridEl().dom.innerHTML = html;
39166
39167         this.initElements();
39168         
39169         // a kludge to fix the random scolling effect in webkit
39170         this.el.on("scroll", function() {
39171             this.el.dom.scrollTop=0; // hopefully not recursive..
39172         },this);
39173
39174         this.scroller.on("scroll", this.handleScroll, this);
39175         this.lockedBody.on("mousewheel", this.handleWheel, this);
39176         this.mainBody.on("mousewheel", this.handleWheel, this);
39177
39178         this.mainHd.on("mouseover", this.handleHdOver, this);
39179         this.mainHd.on("mouseout", this.handleHdOut, this);
39180         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39181                 {delegate: "."+this.splitClass});
39182
39183         this.lockedHd.on("mouseover", this.handleHdOver, this);
39184         this.lockedHd.on("mouseout", this.handleHdOut, this);
39185         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39186                 {delegate: "."+this.splitClass});
39187
39188         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39189             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39190         }
39191
39192         this.updateSplitters();
39193
39194         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39195             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39196             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39197         }
39198
39199         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39200             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39201             this.hmenu.add(
39202                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39203                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39204             );
39205             if(this.grid.enableColLock !== false){
39206                 this.hmenu.add('-',
39207                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39208                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39209                 );
39210             }
39211             if (Roo.isTouch) {
39212                  this.hmenu.add('-',
39213                     {id:"wider", text: this.columnsWiderText},
39214                     {id:"narrow", text: this.columnsNarrowText }
39215                 );
39216                 
39217                  
39218             }
39219             
39220             if(this.grid.enableColumnHide !== false){
39221
39222                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39223                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39224                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39225
39226                 this.hmenu.add('-',
39227                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39228                 );
39229             }
39230             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39231
39232             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39233         }
39234
39235         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39236             this.dd = new Roo.grid.GridDragZone(this.grid, {
39237                 ddGroup : this.grid.ddGroup || 'GridDD'
39238             });
39239             
39240         }
39241
39242         /*
39243         for(var i = 0; i < colCount; i++){
39244             if(cm.isHidden(i)){
39245                 this.hideColumn(i);
39246             }
39247             if(cm.config[i].align){
39248                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39249                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39250             }
39251         }*/
39252         
39253         this.updateHeaderSortState();
39254
39255         this.beforeInitialResize();
39256         this.layout(true);
39257
39258         // two part rendering gives faster view to the user
39259         this.renderPhase2.defer(1, this);
39260     },
39261
39262     renderPhase2 : function(){
39263         // render the rows now
39264         this.refresh();
39265         if(this.grid.autoSizeColumns){
39266             this.autoSizeColumns();
39267         }
39268     },
39269
39270     beforeInitialResize : function(){
39271
39272     },
39273
39274     onColumnSplitterMoved : function(i, w){
39275         this.userResized = true;
39276         var cm = this.grid.colModel;
39277         cm.setColumnWidth(i, w, true);
39278         var cid = cm.getColumnId(i);
39279         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39280         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39281         this.updateSplitters();
39282         this.layout();
39283         this.grid.fireEvent("columnresize", i, w);
39284     },
39285
39286     syncRowHeights : function(startIndex, endIndex){
39287         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39288             startIndex = startIndex || 0;
39289             var mrows = this.getBodyTable().rows;
39290             var lrows = this.getLockedTable().rows;
39291             var len = mrows.length-1;
39292             endIndex = Math.min(endIndex || len, len);
39293             for(var i = startIndex; i <= endIndex; i++){
39294                 var m = mrows[i], l = lrows[i];
39295                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39296                 m.style.height = l.style.height = h + "px";
39297             }
39298         }
39299     },
39300
39301     layout : function(initialRender, is2ndPass)
39302     {
39303         var g = this.grid;
39304         var auto = g.autoHeight;
39305         var scrollOffset = 16;
39306         var c = g.getGridEl(), cm = this.cm,
39307                 expandCol = g.autoExpandColumn,
39308                 gv = this;
39309         //c.beginMeasure();
39310
39311         if(!c.dom.offsetWidth){ // display:none?
39312             if(initialRender){
39313                 this.lockedWrap.show();
39314                 this.mainWrap.show();
39315             }
39316             return;
39317         }
39318
39319         var hasLock = this.cm.isLocked(0);
39320
39321         var tbh = this.headerPanel.getHeight();
39322         var bbh = this.footerPanel.getHeight();
39323
39324         if(auto){
39325             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39326             var newHeight = ch + c.getBorderWidth("tb");
39327             if(g.maxHeight){
39328                 newHeight = Math.min(g.maxHeight, newHeight);
39329             }
39330             c.setHeight(newHeight);
39331         }
39332
39333         if(g.autoWidth){
39334             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39335         }
39336
39337         var s = this.scroller;
39338
39339         var csize = c.getSize(true);
39340
39341         this.el.setSize(csize.width, csize.height);
39342
39343         this.headerPanel.setWidth(csize.width);
39344         this.footerPanel.setWidth(csize.width);
39345
39346         var hdHeight = this.mainHd.getHeight();
39347         var vw = csize.width;
39348         var vh = csize.height - (tbh + bbh);
39349
39350         s.setSize(vw, vh);
39351
39352         var bt = this.getBodyTable();
39353         
39354         if(cm.getLockedCount() == cm.config.length){
39355             bt = this.getLockedTable();
39356         }
39357         
39358         var ltWidth = hasLock ?
39359                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39360
39361         var scrollHeight = bt.offsetHeight;
39362         var scrollWidth = ltWidth + bt.offsetWidth;
39363         var vscroll = false, hscroll = false;
39364
39365         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39366
39367         var lw = this.lockedWrap, mw = this.mainWrap;
39368         var lb = this.lockedBody, mb = this.mainBody;
39369
39370         setTimeout(function(){
39371             var t = s.dom.offsetTop;
39372             var w = s.dom.clientWidth,
39373                 h = s.dom.clientHeight;
39374
39375             lw.setTop(t);
39376             lw.setSize(ltWidth, h);
39377
39378             mw.setLeftTop(ltWidth, t);
39379             mw.setSize(w-ltWidth, h);
39380
39381             lb.setHeight(h-hdHeight);
39382             mb.setHeight(h-hdHeight);
39383
39384             if(is2ndPass !== true && !gv.userResized && expandCol){
39385                 // high speed resize without full column calculation
39386                 
39387                 var ci = cm.getIndexById(expandCol);
39388                 if (ci < 0) {
39389                     ci = cm.findColumnIndex(expandCol);
39390                 }
39391                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39392                 var expandId = cm.getColumnId(ci);
39393                 var  tw = cm.getTotalWidth(false);
39394                 var currentWidth = cm.getColumnWidth(ci);
39395                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39396                 if(currentWidth != cw){
39397                     cm.setColumnWidth(ci, cw, true);
39398                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39399                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39400                     gv.updateSplitters();
39401                     gv.layout(false, true);
39402                 }
39403             }
39404
39405             if(initialRender){
39406                 lw.show();
39407                 mw.show();
39408             }
39409             //c.endMeasure();
39410         }, 10);
39411     },
39412
39413     onWindowResize : function(){
39414         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39415             return;
39416         }
39417         this.layout();
39418     },
39419
39420     appendFooter : function(parentEl){
39421         return null;
39422     },
39423
39424     sortAscText : "Sort Ascending",
39425     sortDescText : "Sort Descending",
39426     lockText : "Lock Column",
39427     unlockText : "Unlock Column",
39428     columnsText : "Columns",
39429  
39430     columnsWiderText : "Wider",
39431     columnsNarrowText : "Thinner"
39432 });
39433
39434
39435 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39436     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39437     this.proxy.el.addClass('x-grid3-col-dd');
39438 };
39439
39440 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39441     handleMouseDown : function(e){
39442
39443     },
39444
39445     callHandleMouseDown : function(e){
39446         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39447     }
39448 });
39449 /*
39450  * Based on:
39451  * Ext JS Library 1.1.1
39452  * Copyright(c) 2006-2007, Ext JS, LLC.
39453  *
39454  * Originally Released Under LGPL - original licence link has changed is not relivant.
39455  *
39456  * Fork - LGPL
39457  * <script type="text/javascript">
39458  */
39459  /**
39460  * @extends Roo.dd.DDProxy
39461  * @class Roo.grid.SplitDragZone
39462  * Support for Column Header resizing
39463  * @constructor
39464  * @param {Object} config
39465  */
39466 // private
39467 // This is a support class used internally by the Grid components
39468 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39469     this.grid = grid;
39470     this.view = grid.getView();
39471     this.proxy = this.view.resizeProxy;
39472     Roo.grid.SplitDragZone.superclass.constructor.call(
39473         this,
39474         hd, // ID
39475         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39476         {  // CONFIG
39477             dragElId : Roo.id(this.proxy.dom),
39478             resizeFrame:false
39479         }
39480     );
39481     
39482     this.setHandleElId(Roo.id(hd));
39483     if (hd2 !== false) {
39484         this.setOuterHandleElId(Roo.id(hd2));
39485     }
39486     
39487     this.scroll = false;
39488 };
39489 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39490     fly: Roo.Element.fly,
39491
39492     b4StartDrag : function(x, y){
39493         this.view.headersDisabled = true;
39494         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39495                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39496         );
39497         this.proxy.setHeight(h);
39498         
39499         // for old system colWidth really stored the actual width?
39500         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39501         // which in reality did not work.. - it worked only for fixed sizes
39502         // for resizable we need to use actual sizes.
39503         var w = this.cm.getColumnWidth(this.cellIndex);
39504         if (!this.view.mainWrap) {
39505             // bootstrap.
39506             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39507         }
39508         
39509         
39510         
39511         // this was w-this.grid.minColumnWidth;
39512         // doesnt really make sense? - w = thie curren width or the rendered one?
39513         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39514         this.resetConstraints();
39515         this.setXConstraint(minw, 1000);
39516         this.setYConstraint(0, 0);
39517         this.minX = x - minw;
39518         this.maxX = x + 1000;
39519         this.startPos = x;
39520         if (!this.view.mainWrap) { // this is Bootstrap code..
39521             this.getDragEl().style.display='block';
39522         }
39523         
39524         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39525     },
39526
39527
39528     handleMouseDown : function(e){
39529         ev = Roo.EventObject.setEvent(e);
39530         var t = this.fly(ev.getTarget());
39531         if(t.hasClass("x-grid-split")){
39532             this.cellIndex = this.view.getCellIndex(t.dom);
39533             this.split = t.dom;
39534             this.cm = this.grid.colModel;
39535             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39536                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39537             }
39538         }
39539     },
39540
39541     endDrag : function(e){
39542         this.view.headersDisabled = false;
39543         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39544         var diff = endX - this.startPos;
39545         // 
39546         var w = this.cm.getColumnWidth(this.cellIndex);
39547         if (!this.view.mainWrap) {
39548             w = 0;
39549         }
39550         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39551     },
39552
39553     autoOffset : function(){
39554         this.setDelta(0,0);
39555     }
39556 });/*
39557  * Based on:
39558  * Ext JS Library 1.1.1
39559  * Copyright(c) 2006-2007, Ext JS, LLC.
39560  *
39561  * Originally Released Under LGPL - original licence link has changed is not relivant.
39562  *
39563  * Fork - LGPL
39564  * <script type="text/javascript">
39565  */
39566  
39567 // private
39568 // This is a support class used internally by the Grid components
39569 Roo.grid.GridDragZone = function(grid, config){
39570     this.view = grid.getView();
39571     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39572     if(this.view.lockedBody){
39573         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39574         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39575     }
39576     this.scroll = false;
39577     this.grid = grid;
39578     this.ddel = document.createElement('div');
39579     this.ddel.className = 'x-grid-dd-wrap';
39580 };
39581
39582 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39583     ddGroup : "GridDD",
39584
39585     getDragData : function(e){
39586         var t = Roo.lib.Event.getTarget(e);
39587         var rowIndex = this.view.findRowIndex(t);
39588         var sm = this.grid.selModel;
39589             
39590         //Roo.log(rowIndex);
39591         
39592         if (sm.getSelectedCell) {
39593             // cell selection..
39594             if (!sm.getSelectedCell()) {
39595                 return false;
39596             }
39597             if (rowIndex != sm.getSelectedCell()[0]) {
39598                 return false;
39599             }
39600         
39601         }
39602         if (sm.getSelections && sm.getSelections().length < 1) {
39603             return false;
39604         }
39605         
39606         
39607         // before it used to all dragging of unseleted... - now we dont do that.
39608         if(rowIndex !== false){
39609             
39610             // if editorgrid.. 
39611             
39612             
39613             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39614                
39615             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39616               //  
39617             //}
39618             if (e.hasModifier()){
39619                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39620             }
39621             
39622             Roo.log("getDragData");
39623             
39624             return {
39625                 grid: this.grid,
39626                 ddel: this.ddel,
39627                 rowIndex: rowIndex,
39628                 selections: sm.getSelections ? sm.getSelections() : (
39629                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39630             };
39631         }
39632         return false;
39633     },
39634     
39635     
39636     onInitDrag : function(e){
39637         var data = this.dragData;
39638         this.ddel.innerHTML = this.grid.getDragDropText();
39639         this.proxy.update(this.ddel);
39640         // fire start drag?
39641     },
39642
39643     afterRepair : function(){
39644         this.dragging = false;
39645     },
39646
39647     getRepairXY : function(e, data){
39648         return false;
39649     },
39650
39651     onEndDrag : function(data, e){
39652         // fire end drag?
39653     },
39654
39655     onValidDrop : function(dd, e, id){
39656         // fire drag drop?
39657         this.hideProxy();
39658     },
39659
39660     beforeInvalidDrop : function(e, id){
39661
39662     }
39663 });/*
39664  * Based on:
39665  * Ext JS Library 1.1.1
39666  * Copyright(c) 2006-2007, Ext JS, LLC.
39667  *
39668  * Originally Released Under LGPL - original licence link has changed is not relivant.
39669  *
39670  * Fork - LGPL
39671  * <script type="text/javascript">
39672  */
39673  
39674
39675 /**
39676  * @class Roo.grid.ColumnModel
39677  * @extends Roo.util.Observable
39678  * This is the default implementation of a ColumnModel used by the Grid. It defines
39679  * the columns in the grid.
39680  * <br>Usage:<br>
39681  <pre><code>
39682  var colModel = new Roo.grid.ColumnModel([
39683         {header: "Ticker", width: 60, sortable: true, locked: true},
39684         {header: "Company Name", width: 150, sortable: true},
39685         {header: "Market Cap.", width: 100, sortable: true},
39686         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39687         {header: "Employees", width: 100, sortable: true, resizable: false}
39688  ]);
39689  </code></pre>
39690  * <p>
39691  
39692  * The config options listed for this class are options which may appear in each
39693  * individual column definition.
39694  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39695  * @constructor
39696  * @param {Object} config An Array of column config objects. See this class's
39697  * config objects for details.
39698 */
39699 Roo.grid.ColumnModel = function(config){
39700         /**
39701      * The config passed into the constructor
39702      */
39703     this.config = []; //config;
39704     this.lookup = {};
39705
39706     // if no id, create one
39707     // if the column does not have a dataIndex mapping,
39708     // map it to the order it is in the config
39709     for(var i = 0, len = config.length; i < len; i++){
39710         this.addColumn(config[i]);
39711         
39712     }
39713
39714     /**
39715      * The width of columns which have no width specified (defaults to 100)
39716      * @type Number
39717      */
39718     this.defaultWidth = 100;
39719
39720     /**
39721      * Default sortable of columns which have no sortable specified (defaults to false)
39722      * @type Boolean
39723      */
39724     this.defaultSortable = false;
39725
39726     this.addEvents({
39727         /**
39728              * @event widthchange
39729              * Fires when the width of a column changes.
39730              * @param {ColumnModel} this
39731              * @param {Number} columnIndex The column index
39732              * @param {Number} newWidth The new width
39733              */
39734             "widthchange": true,
39735         /**
39736              * @event headerchange
39737              * Fires when the text of a header changes.
39738              * @param {ColumnModel} this
39739              * @param {Number} columnIndex The column index
39740              * @param {Number} newText The new header text
39741              */
39742             "headerchange": true,
39743         /**
39744              * @event hiddenchange
39745              * Fires when a column is hidden or "unhidden".
39746              * @param {ColumnModel} this
39747              * @param {Number} columnIndex The column index
39748              * @param {Boolean} hidden true if hidden, false otherwise
39749              */
39750             "hiddenchange": true,
39751             /**
39752          * @event columnmoved
39753          * Fires when a column is moved.
39754          * @param {ColumnModel} this
39755          * @param {Number} oldIndex
39756          * @param {Number} newIndex
39757          */
39758         "columnmoved" : true,
39759         /**
39760          * @event columlockchange
39761          * Fires when a column's locked state is changed
39762          * @param {ColumnModel} this
39763          * @param {Number} colIndex
39764          * @param {Boolean} locked true if locked
39765          */
39766         "columnlockchange" : true
39767     });
39768     Roo.grid.ColumnModel.superclass.constructor.call(this);
39769 };
39770 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39771     /**
39772      * @cfg {String} header The header text to display in the Grid view.
39773      */
39774         /**
39775      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39776      */
39777         /**
39778      * @cfg {String} smHeader Header at Bootsrap Small width
39779      */
39780         /**
39781      * @cfg {String} mdHeader Header at Bootsrap Medium width
39782      */
39783         /**
39784      * @cfg {String} lgHeader Header at Bootsrap Large width
39785      */
39786         /**
39787      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39788      */
39789     /**
39790      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39791      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39792      * specified, the column's index is used as an index into the Record's data Array.
39793      */
39794     /**
39795      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39796      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39797      */
39798     /**
39799      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39800      * Defaults to the value of the {@link #defaultSortable} property.
39801      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39802      */
39803     /**
39804      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39805      */
39806     /**
39807      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39808      */
39809     /**
39810      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39811      */
39812     /**
39813      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39814      */
39815     /**
39816      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39817      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39818      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39819      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39820      */
39821        /**
39822      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39823      */
39824     /**
39825      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39826      */
39827     /**
39828      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
39829      */
39830     /**
39831      * @cfg {String} cursor (Optional)
39832      */
39833     /**
39834      * @cfg {String} tooltip (Optional)
39835      */
39836     /**
39837      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
39838      */
39839     /**
39840      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
39841      */
39842     /**
39843      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
39844      */
39845     /**
39846      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
39847      */
39848         /**
39849      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
39850      */
39851     /**
39852      * Returns the id of the column at the specified index.
39853      * @param {Number} index The column index
39854      * @return {String} the id
39855      */
39856     getColumnId : function(index){
39857         return this.config[index].id;
39858     },
39859
39860     /**
39861      * Returns the column for a specified id.
39862      * @param {String} id The column id
39863      * @return {Object} the column
39864      */
39865     getColumnById : function(id){
39866         return this.lookup[id];
39867     },
39868
39869     
39870     /**
39871      * Returns the column Object for a specified dataIndex.
39872      * @param {String} dataIndex The column dataIndex
39873      * @return {Object|Boolean} the column or false if not found
39874      */
39875     getColumnByDataIndex: function(dataIndex){
39876         var index = this.findColumnIndex(dataIndex);
39877         return index > -1 ? this.config[index] : false;
39878     },
39879     
39880     /**
39881      * Returns the index for a specified column id.
39882      * @param {String} id The column id
39883      * @return {Number} the index, or -1 if not found
39884      */
39885     getIndexById : function(id){
39886         for(var i = 0, len = this.config.length; i < len; i++){
39887             if(this.config[i].id == id){
39888                 return i;
39889             }
39890         }
39891         return -1;
39892     },
39893     
39894     /**
39895      * Returns the index for a specified column dataIndex.
39896      * @param {String} dataIndex The column dataIndex
39897      * @return {Number} the index, or -1 if not found
39898      */
39899     
39900     findColumnIndex : function(dataIndex){
39901         for(var i = 0, len = this.config.length; i < len; i++){
39902             if(this.config[i].dataIndex == dataIndex){
39903                 return i;
39904             }
39905         }
39906         return -1;
39907     },
39908     
39909     
39910     moveColumn : function(oldIndex, newIndex){
39911         var c = this.config[oldIndex];
39912         this.config.splice(oldIndex, 1);
39913         this.config.splice(newIndex, 0, c);
39914         this.dataMap = null;
39915         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39916     },
39917
39918     isLocked : function(colIndex){
39919         return this.config[colIndex].locked === true;
39920     },
39921
39922     setLocked : function(colIndex, value, suppressEvent){
39923         if(this.isLocked(colIndex) == value){
39924             return;
39925         }
39926         this.config[colIndex].locked = value;
39927         if(!suppressEvent){
39928             this.fireEvent("columnlockchange", this, colIndex, value);
39929         }
39930     },
39931
39932     getTotalLockedWidth : function(){
39933         var totalWidth = 0;
39934         for(var i = 0; i < this.config.length; i++){
39935             if(this.isLocked(i) && !this.isHidden(i)){
39936                 this.totalWidth += this.getColumnWidth(i);
39937             }
39938         }
39939         return totalWidth;
39940     },
39941
39942     getLockedCount : function(){
39943         for(var i = 0, len = this.config.length; i < len; i++){
39944             if(!this.isLocked(i)){
39945                 return i;
39946             }
39947         }
39948         
39949         return this.config.length;
39950     },
39951
39952     /**
39953      * Returns the number of columns.
39954      * @return {Number}
39955      */
39956     getColumnCount : function(visibleOnly){
39957         if(visibleOnly === true){
39958             var c = 0;
39959             for(var i = 0, len = this.config.length; i < len; i++){
39960                 if(!this.isHidden(i)){
39961                     c++;
39962                 }
39963             }
39964             return c;
39965         }
39966         return this.config.length;
39967     },
39968
39969     /**
39970      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39971      * @param {Function} fn
39972      * @param {Object} scope (optional)
39973      * @return {Array} result
39974      */
39975     getColumnsBy : function(fn, scope){
39976         var r = [];
39977         for(var i = 0, len = this.config.length; i < len; i++){
39978             var c = this.config[i];
39979             if(fn.call(scope||this, c, i) === true){
39980                 r[r.length] = c;
39981             }
39982         }
39983         return r;
39984     },
39985
39986     /**
39987      * Returns true if the specified column is sortable.
39988      * @param {Number} col The column index
39989      * @return {Boolean}
39990      */
39991     isSortable : function(col){
39992         if(typeof this.config[col].sortable == "undefined"){
39993             return this.defaultSortable;
39994         }
39995         return this.config[col].sortable;
39996     },
39997
39998     /**
39999      * Returns the rendering (formatting) function defined for the column.
40000      * @param {Number} col The column index.
40001      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40002      */
40003     getRenderer : function(col){
40004         if(!this.config[col].renderer){
40005             return Roo.grid.ColumnModel.defaultRenderer;
40006         }
40007         return this.config[col].renderer;
40008     },
40009
40010     /**
40011      * Sets the rendering (formatting) function for a column.
40012      * @param {Number} col The column index
40013      * @param {Function} fn The function to use to process the cell's raw data
40014      * to return HTML markup for the grid view. The render function is called with
40015      * the following parameters:<ul>
40016      * <li>Data value.</li>
40017      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40018      * <li>css A CSS style string to apply to the table cell.</li>
40019      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40020      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40021      * <li>Row index</li>
40022      * <li>Column index</li>
40023      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40024      */
40025     setRenderer : function(col, fn){
40026         this.config[col].renderer = fn;
40027     },
40028
40029     /**
40030      * Returns the width for the specified column.
40031      * @param {Number} col The column index
40032      * @param (optional) {String} gridSize bootstrap width size.
40033      * @return {Number}
40034      */
40035     getColumnWidth : function(col, gridSize)
40036         {
40037                 var cfg = this.config[col];
40038                 
40039                 if (typeof(gridSize) == 'undefined') {
40040                         return cfg.width * 1 || this.defaultWidth;
40041                 }
40042                 if (gridSize === false) { // if we set it..
40043                         return cfg.width || false;
40044                 }
40045                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40046                 
40047                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40048                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40049                                 continue;
40050                         }
40051                         return cfg[ sizes[i] ];
40052                 }
40053                 return 1;
40054                 
40055     },
40056
40057     /**
40058      * Sets the width for a column.
40059      * @param {Number} col The column index
40060      * @param {Number} width The new width
40061      */
40062     setColumnWidth : function(col, width, suppressEvent){
40063         this.config[col].width = width;
40064         this.totalWidth = null;
40065         if(!suppressEvent){
40066              this.fireEvent("widthchange", this, col, width);
40067         }
40068     },
40069
40070     /**
40071      * Returns the total width of all columns.
40072      * @param {Boolean} includeHidden True to include hidden column widths
40073      * @return {Number}
40074      */
40075     getTotalWidth : function(includeHidden){
40076         if(!this.totalWidth){
40077             this.totalWidth = 0;
40078             for(var i = 0, len = this.config.length; i < len; i++){
40079                 if(includeHidden || !this.isHidden(i)){
40080                     this.totalWidth += this.getColumnWidth(i);
40081                 }
40082             }
40083         }
40084         return this.totalWidth;
40085     },
40086
40087     /**
40088      * Returns the header for the specified column.
40089      * @param {Number} col The column index
40090      * @return {String}
40091      */
40092     getColumnHeader : function(col){
40093         return this.config[col].header;
40094     },
40095
40096     /**
40097      * Sets the header for a column.
40098      * @param {Number} col The column index
40099      * @param {String} header The new header
40100      */
40101     setColumnHeader : function(col, header){
40102         this.config[col].header = header;
40103         this.fireEvent("headerchange", this, col, header);
40104     },
40105
40106     /**
40107      * Returns the tooltip for the specified column.
40108      * @param {Number} col The column index
40109      * @return {String}
40110      */
40111     getColumnTooltip : function(col){
40112             return this.config[col].tooltip;
40113     },
40114     /**
40115      * Sets the tooltip for a column.
40116      * @param {Number} col The column index
40117      * @param {String} tooltip The new tooltip
40118      */
40119     setColumnTooltip : function(col, tooltip){
40120             this.config[col].tooltip = tooltip;
40121     },
40122
40123     /**
40124      * Returns the dataIndex for the specified column.
40125      * @param {Number} col The column index
40126      * @return {Number}
40127      */
40128     getDataIndex : function(col){
40129         return this.config[col].dataIndex;
40130     },
40131
40132     /**
40133      * Sets the dataIndex for a column.
40134      * @param {Number} col The column index
40135      * @param {Number} dataIndex The new dataIndex
40136      */
40137     setDataIndex : function(col, dataIndex){
40138         this.config[col].dataIndex = dataIndex;
40139     },
40140
40141     
40142     
40143     /**
40144      * Returns true if the cell is editable.
40145      * @param {Number} colIndex The column index
40146      * @param {Number} rowIndex The row index - this is nto actually used..?
40147      * @return {Boolean}
40148      */
40149     isCellEditable : function(colIndex, rowIndex){
40150         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40151     },
40152
40153     /**
40154      * Returns the editor defined for the cell/column.
40155      * return false or null to disable editing.
40156      * @param {Number} colIndex The column index
40157      * @param {Number} rowIndex The row index
40158      * @return {Object}
40159      */
40160     getCellEditor : function(colIndex, rowIndex){
40161         return this.config[colIndex].editor;
40162     },
40163
40164     /**
40165      * Sets if a column is editable.
40166      * @param {Number} col The column index
40167      * @param {Boolean} editable True if the column is editable
40168      */
40169     setEditable : function(col, editable){
40170         this.config[col].editable = editable;
40171     },
40172
40173
40174     /**
40175      * Returns true if the column is hidden.
40176      * @param {Number} colIndex The column index
40177      * @return {Boolean}
40178      */
40179     isHidden : function(colIndex){
40180         return this.config[colIndex].hidden;
40181     },
40182
40183
40184     /**
40185      * Returns true if the column width cannot be changed
40186      */
40187     isFixed : function(colIndex){
40188         return this.config[colIndex].fixed;
40189     },
40190
40191     /**
40192      * Returns true if the column can be resized
40193      * @return {Boolean}
40194      */
40195     isResizable : function(colIndex){
40196         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40197     },
40198     /**
40199      * Sets if a column is hidden.
40200      * @param {Number} colIndex The column index
40201      * @param {Boolean} hidden True if the column is hidden
40202      */
40203     setHidden : function(colIndex, hidden){
40204         this.config[colIndex].hidden = hidden;
40205         this.totalWidth = null;
40206         this.fireEvent("hiddenchange", this, colIndex, hidden);
40207     },
40208
40209     /**
40210      * Sets the editor for a column.
40211      * @param {Number} col The column index
40212      * @param {Object} editor The editor object
40213      */
40214     setEditor : function(col, editor){
40215         this.config[col].editor = editor;
40216     },
40217     /**
40218      * Add a column (experimental...) - defaults to adding to the end..
40219      * @param {Object} config 
40220     */
40221     addColumn : function(c)
40222     {
40223     
40224         var i = this.config.length;
40225         this.config[i] = c;
40226         
40227         if(typeof c.dataIndex == "undefined"){
40228             c.dataIndex = i;
40229         }
40230         if(typeof c.renderer == "string"){
40231             c.renderer = Roo.util.Format[c.renderer];
40232         }
40233         if(typeof c.id == "undefined"){
40234             c.id = Roo.id();
40235         }
40236         if(c.editor && c.editor.xtype){
40237             c.editor  = Roo.factory(c.editor, Roo.grid);
40238         }
40239         if(c.editor && c.editor.isFormField){
40240             c.editor = new Roo.grid.GridEditor(c.editor);
40241         }
40242         this.lookup[c.id] = c;
40243     }
40244     
40245 });
40246
40247 Roo.grid.ColumnModel.defaultRenderer = function(value)
40248 {
40249     if(typeof value == "object") {
40250         return value;
40251     }
40252         if(typeof value == "string" && value.length < 1){
40253             return "&#160;";
40254         }
40255     
40256         return String.format("{0}", value);
40257 };
40258
40259 // Alias for backwards compatibility
40260 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40261 /*
40262  * Based on:
40263  * Ext JS Library 1.1.1
40264  * Copyright(c) 2006-2007, Ext JS, LLC.
40265  *
40266  * Originally Released Under LGPL - original licence link has changed is not relivant.
40267  *
40268  * Fork - LGPL
40269  * <script type="text/javascript">
40270  */
40271
40272 /**
40273  * @class Roo.grid.AbstractSelectionModel
40274  * @extends Roo.util.Observable
40275  * @abstract
40276  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40277  * implemented by descendant classes.  This class should not be directly instantiated.
40278  * @constructor
40279  */
40280 Roo.grid.AbstractSelectionModel = function(){
40281     this.locked = false;
40282     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40283 };
40284
40285 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40286     /** @ignore Called by the grid automatically. Do not call directly. */
40287     init : function(grid){
40288         this.grid = grid;
40289         this.initEvents();
40290     },
40291
40292     /**
40293      * Locks the selections.
40294      */
40295     lock : function(){
40296         this.locked = true;
40297     },
40298
40299     /**
40300      * Unlocks the selections.
40301      */
40302     unlock : function(){
40303         this.locked = false;
40304     },
40305
40306     /**
40307      * Returns true if the selections are locked.
40308      * @return {Boolean}
40309      */
40310     isLocked : function(){
40311         return this.locked;
40312     }
40313 });/*
40314  * Based on:
40315  * Ext JS Library 1.1.1
40316  * Copyright(c) 2006-2007, Ext JS, LLC.
40317  *
40318  * Originally Released Under LGPL - original licence link has changed is not relivant.
40319  *
40320  * Fork - LGPL
40321  * <script type="text/javascript">
40322  */
40323 /**
40324  * @extends Roo.grid.AbstractSelectionModel
40325  * @class Roo.grid.RowSelectionModel
40326  * The default SelectionModel used by {@link Roo.grid.Grid}.
40327  * It supports multiple selections and keyboard selection/navigation. 
40328  * @constructor
40329  * @param {Object} config
40330  */
40331 Roo.grid.RowSelectionModel = function(config){
40332     Roo.apply(this, config);
40333     this.selections = new Roo.util.MixedCollection(false, function(o){
40334         return o.id;
40335     });
40336
40337     this.last = false;
40338     this.lastActive = false;
40339
40340     this.addEvents({
40341         /**
40342         * @event selectionchange
40343         * Fires when the selection changes
40344         * @param {SelectionModel} this
40345         */
40346        "selectionchange" : true,
40347        /**
40348         * @event afterselectionchange
40349         * Fires after the selection changes (eg. by key press or clicking)
40350         * @param {SelectionModel} this
40351         */
40352        "afterselectionchange" : true,
40353        /**
40354         * @event beforerowselect
40355         * Fires when a row is selected being selected, return false to cancel.
40356         * @param {SelectionModel} this
40357         * @param {Number} rowIndex The selected index
40358         * @param {Boolean} keepExisting False if other selections will be cleared
40359         */
40360        "beforerowselect" : true,
40361        /**
40362         * @event rowselect
40363         * Fires when a row is selected.
40364         * @param {SelectionModel} this
40365         * @param {Number} rowIndex The selected index
40366         * @param {Roo.data.Record} r The record
40367         */
40368        "rowselect" : true,
40369        /**
40370         * @event rowdeselect
40371         * Fires when a row is deselected.
40372         * @param {SelectionModel} this
40373         * @param {Number} rowIndex The selected index
40374         */
40375         "rowdeselect" : true
40376     });
40377     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40378     this.locked = false;
40379 };
40380
40381 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40382     /**
40383      * @cfg {Boolean} singleSelect
40384      * True to allow selection of only one row at a time (defaults to false)
40385      */
40386     singleSelect : false,
40387
40388     // private
40389     initEvents : function(){
40390
40391         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40392             this.grid.on("mousedown", this.handleMouseDown, this);
40393         }else{ // allow click to work like normal
40394             this.grid.on("rowclick", this.handleDragableRowClick, this);
40395         }
40396         // bootstrap does not have a view..
40397         var view = this.grid.view ? this.grid.view : this.grid;
40398         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40399             "up" : function(e){
40400                 if(!e.shiftKey){
40401                     this.selectPrevious(e.shiftKey);
40402                 }else if(this.last !== false && this.lastActive !== false){
40403                     var last = this.last;
40404                     this.selectRange(this.last,  this.lastActive-1);
40405                     view.focusRow(this.lastActive);
40406                     if(last !== false){
40407                         this.last = last;
40408                     }
40409                 }else{
40410                     this.selectFirstRow();
40411                 }
40412                 this.fireEvent("afterselectionchange", this);
40413             },
40414             "down" : function(e){
40415                 if(!e.shiftKey){
40416                     this.selectNext(e.shiftKey);
40417                 }else if(this.last !== false && this.lastActive !== false){
40418                     var last = this.last;
40419                     this.selectRange(this.last,  this.lastActive+1);
40420                     view.focusRow(this.lastActive);
40421                     if(last !== false){
40422                         this.last = last;
40423                     }
40424                 }else{
40425                     this.selectFirstRow();
40426                 }
40427                 this.fireEvent("afterselectionchange", this);
40428             },
40429             scope: this
40430         });
40431
40432          
40433         view.on("refresh", this.onRefresh, this);
40434         view.on("rowupdated", this.onRowUpdated, this);
40435         view.on("rowremoved", this.onRemove, this);
40436     },
40437
40438     // private
40439     onRefresh : function(){
40440         var ds = this.grid.ds, i, v = this.grid.view;
40441         var s = this.selections;
40442         s.each(function(r){
40443             if((i = ds.indexOfId(r.id)) != -1){
40444                 v.onRowSelect(i);
40445                 s.add(ds.getAt(i)); // updating the selection relate data
40446             }else{
40447                 s.remove(r);
40448             }
40449         });
40450     },
40451
40452     // private
40453     onRemove : function(v, index, r){
40454         this.selections.remove(r);
40455     },
40456
40457     // private
40458     onRowUpdated : function(v, index, r){
40459         if(this.isSelected(r)){
40460             v.onRowSelect(index);
40461         }
40462     },
40463
40464     /**
40465      * Select records.
40466      * @param {Array} records The records to select
40467      * @param {Boolean} keepExisting (optional) True to keep existing selections
40468      */
40469     selectRecords : function(records, keepExisting){
40470         if(!keepExisting){
40471             this.clearSelections();
40472         }
40473         var ds = this.grid.ds;
40474         for(var i = 0, len = records.length; i < len; i++){
40475             this.selectRow(ds.indexOf(records[i]), true);
40476         }
40477     },
40478
40479     /**
40480      * Gets the number of selected rows.
40481      * @return {Number}
40482      */
40483     getCount : function(){
40484         return this.selections.length;
40485     },
40486
40487     /**
40488      * Selects the first row in the grid.
40489      */
40490     selectFirstRow : function(){
40491         this.selectRow(0);
40492     },
40493
40494     /**
40495      * Select the last row.
40496      * @param {Boolean} keepExisting (optional) True to keep existing selections
40497      */
40498     selectLastRow : function(keepExisting){
40499         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40500     },
40501
40502     /**
40503      * Selects the row immediately following the last selected row.
40504      * @param {Boolean} keepExisting (optional) True to keep existing selections
40505      */
40506     selectNext : function(keepExisting){
40507         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40508             this.selectRow(this.last+1, keepExisting);
40509             var view = this.grid.view ? this.grid.view : this.grid;
40510             view.focusRow(this.last);
40511         }
40512     },
40513
40514     /**
40515      * Selects the row that precedes the last selected row.
40516      * @param {Boolean} keepExisting (optional) True to keep existing selections
40517      */
40518     selectPrevious : function(keepExisting){
40519         if(this.last){
40520             this.selectRow(this.last-1, keepExisting);
40521             var view = this.grid.view ? this.grid.view : this.grid;
40522             view.focusRow(this.last);
40523         }
40524     },
40525
40526     /**
40527      * Returns the selected records
40528      * @return {Array} Array of selected records
40529      */
40530     getSelections : function(){
40531         return [].concat(this.selections.items);
40532     },
40533
40534     /**
40535      * Returns the first selected record.
40536      * @return {Record}
40537      */
40538     getSelected : function(){
40539         return this.selections.itemAt(0);
40540     },
40541
40542
40543     /**
40544      * Clears all selections.
40545      */
40546     clearSelections : function(fast){
40547         if(this.locked) {
40548             return;
40549         }
40550         if(fast !== true){
40551             var ds = this.grid.ds;
40552             var s = this.selections;
40553             s.each(function(r){
40554                 this.deselectRow(ds.indexOfId(r.id));
40555             }, this);
40556             s.clear();
40557         }else{
40558             this.selections.clear();
40559         }
40560         this.last = false;
40561     },
40562
40563
40564     /**
40565      * Selects all rows.
40566      */
40567     selectAll : function(){
40568         if(this.locked) {
40569             return;
40570         }
40571         this.selections.clear();
40572         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40573             this.selectRow(i, true);
40574         }
40575     },
40576
40577     /**
40578      * Returns True if there is a selection.
40579      * @return {Boolean}
40580      */
40581     hasSelection : function(){
40582         return this.selections.length > 0;
40583     },
40584
40585     /**
40586      * Returns True if the specified row is selected.
40587      * @param {Number/Record} record The record or index of the record to check
40588      * @return {Boolean}
40589      */
40590     isSelected : function(index){
40591         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40592         return (r && this.selections.key(r.id) ? true : false);
40593     },
40594
40595     /**
40596      * Returns True if the specified record id is selected.
40597      * @param {String} id The id of record to check
40598      * @return {Boolean}
40599      */
40600     isIdSelected : function(id){
40601         return (this.selections.key(id) ? true : false);
40602     },
40603
40604     // private
40605     handleMouseDown : function(e, t)
40606     {
40607         var view = this.grid.view ? this.grid.view : this.grid;
40608         var rowIndex;
40609         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40610             return;
40611         };
40612         if(e.shiftKey && this.last !== false){
40613             var last = this.last;
40614             this.selectRange(last, rowIndex, e.ctrlKey);
40615             this.last = last; // reset the last
40616             view.focusRow(rowIndex);
40617         }else{
40618             var isSelected = this.isSelected(rowIndex);
40619             if(e.button !== 0 && isSelected){
40620                 view.focusRow(rowIndex);
40621             }else if(e.ctrlKey && isSelected){
40622                 this.deselectRow(rowIndex);
40623             }else if(!isSelected){
40624                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40625                 view.focusRow(rowIndex);
40626             }
40627         }
40628         this.fireEvent("afterselectionchange", this);
40629     },
40630     // private
40631     handleDragableRowClick :  function(grid, rowIndex, e) 
40632     {
40633         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40634             this.selectRow(rowIndex, false);
40635             var view = this.grid.view ? this.grid.view : this.grid;
40636             view.focusRow(rowIndex);
40637              this.fireEvent("afterselectionchange", this);
40638         }
40639     },
40640     
40641     /**
40642      * Selects multiple rows.
40643      * @param {Array} rows Array of the indexes of the row to select
40644      * @param {Boolean} keepExisting (optional) True to keep existing selections
40645      */
40646     selectRows : function(rows, keepExisting){
40647         if(!keepExisting){
40648             this.clearSelections();
40649         }
40650         for(var i = 0, len = rows.length; i < len; i++){
40651             this.selectRow(rows[i], true);
40652         }
40653     },
40654
40655     /**
40656      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40657      * @param {Number} startRow The index of the first row in the range
40658      * @param {Number} endRow The index of the last row in the range
40659      * @param {Boolean} keepExisting (optional) True to retain existing selections
40660      */
40661     selectRange : function(startRow, endRow, keepExisting){
40662         if(this.locked) {
40663             return;
40664         }
40665         if(!keepExisting){
40666             this.clearSelections();
40667         }
40668         if(startRow <= endRow){
40669             for(var i = startRow; i <= endRow; i++){
40670                 this.selectRow(i, true);
40671             }
40672         }else{
40673             for(var i = startRow; i >= endRow; i--){
40674                 this.selectRow(i, true);
40675             }
40676         }
40677     },
40678
40679     /**
40680      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40681      * @param {Number} startRow The index of the first row in the range
40682      * @param {Number} endRow The index of the last row in the range
40683      */
40684     deselectRange : function(startRow, endRow, preventViewNotify){
40685         if(this.locked) {
40686             return;
40687         }
40688         for(var i = startRow; i <= endRow; i++){
40689             this.deselectRow(i, preventViewNotify);
40690         }
40691     },
40692
40693     /**
40694      * Selects a row.
40695      * @param {Number} row The index of the row to select
40696      * @param {Boolean} keepExisting (optional) True to keep existing selections
40697      */
40698     selectRow : function(index, keepExisting, preventViewNotify){
40699         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40700             return;
40701         }
40702         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40703             if(!keepExisting || this.singleSelect){
40704                 this.clearSelections();
40705             }
40706             var r = this.grid.ds.getAt(index);
40707             this.selections.add(r);
40708             this.last = this.lastActive = index;
40709             if(!preventViewNotify){
40710                 var view = this.grid.view ? this.grid.view : this.grid;
40711                 view.onRowSelect(index);
40712             }
40713             this.fireEvent("rowselect", this, index, r);
40714             this.fireEvent("selectionchange", this);
40715         }
40716     },
40717
40718     /**
40719      * Deselects a row.
40720      * @param {Number} row The index of the row to deselect
40721      */
40722     deselectRow : function(index, preventViewNotify){
40723         if(this.locked) {
40724             return;
40725         }
40726         if(this.last == index){
40727             this.last = false;
40728         }
40729         if(this.lastActive == index){
40730             this.lastActive = false;
40731         }
40732         var r = this.grid.ds.getAt(index);
40733         this.selections.remove(r);
40734         if(!preventViewNotify){
40735             var view = this.grid.view ? this.grid.view : this.grid;
40736             view.onRowDeselect(index);
40737         }
40738         this.fireEvent("rowdeselect", this, index);
40739         this.fireEvent("selectionchange", this);
40740     },
40741
40742     // private
40743     restoreLast : function(){
40744         if(this._last){
40745             this.last = this._last;
40746         }
40747     },
40748
40749     // private
40750     acceptsNav : function(row, col, cm){
40751         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40752     },
40753
40754     // private
40755     onEditorKey : function(field, e){
40756         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40757         if(k == e.TAB){
40758             e.stopEvent();
40759             ed.completeEdit();
40760             if(e.shiftKey){
40761                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40762             }else{
40763                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40764             }
40765         }else if(k == e.ENTER && !e.ctrlKey){
40766             e.stopEvent();
40767             ed.completeEdit();
40768             if(e.shiftKey){
40769                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40770             }else{
40771                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40772             }
40773         }else if(k == e.ESC){
40774             ed.cancelEdit();
40775         }
40776         if(newCell){
40777             g.startEditing(newCell[0], newCell[1]);
40778         }
40779     }
40780 });/*
40781  * Based on:
40782  * Ext JS Library 1.1.1
40783  * Copyright(c) 2006-2007, Ext JS, LLC.
40784  *
40785  * Originally Released Under LGPL - original licence link has changed is not relivant.
40786  *
40787  * Fork - LGPL
40788  * <script type="text/javascript">
40789  */
40790 /**
40791  * @class Roo.grid.CellSelectionModel
40792  * @extends Roo.grid.AbstractSelectionModel
40793  * This class provides the basic implementation for cell selection in a grid.
40794  * @constructor
40795  * @param {Object} config The object containing the configuration of this model.
40796  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40797  */
40798 Roo.grid.CellSelectionModel = function(config){
40799     Roo.apply(this, config);
40800
40801     this.selection = null;
40802
40803     this.addEvents({
40804         /**
40805              * @event beforerowselect
40806              * Fires before a cell is selected.
40807              * @param {SelectionModel} this
40808              * @param {Number} rowIndex The selected row index
40809              * @param {Number} colIndex The selected cell index
40810              */
40811             "beforecellselect" : true,
40812         /**
40813              * @event cellselect
40814              * Fires when a cell is selected.
40815              * @param {SelectionModel} this
40816              * @param {Number} rowIndex The selected row index
40817              * @param {Number} colIndex The selected cell index
40818              */
40819             "cellselect" : true,
40820         /**
40821              * @event selectionchange
40822              * Fires when the active selection changes.
40823              * @param {SelectionModel} this
40824              * @param {Object} selection null for no selection or an object (o) with two properties
40825                 <ul>
40826                 <li>o.record: the record object for the row the selection is in</li>
40827                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40828                 </ul>
40829              */
40830             "selectionchange" : true,
40831         /**
40832              * @event tabend
40833              * Fires when the tab (or enter) was pressed on the last editable cell
40834              * You can use this to trigger add new row.
40835              * @param {SelectionModel} this
40836              */
40837             "tabend" : true,
40838          /**
40839              * @event beforeeditnext
40840              * Fires before the next editable sell is made active
40841              * You can use this to skip to another cell or fire the tabend
40842              *    if you set cell to false
40843              * @param {Object} eventdata object : { cell : [ row, col ] } 
40844              */
40845             "beforeeditnext" : true
40846     });
40847     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40848 };
40849
40850 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40851     
40852     enter_is_tab: false,
40853
40854     /** @ignore */
40855     initEvents : function(){
40856         this.grid.on("mousedown", this.handleMouseDown, this);
40857         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40858         var view = this.grid.view;
40859         view.on("refresh", this.onViewChange, this);
40860         view.on("rowupdated", this.onRowUpdated, this);
40861         view.on("beforerowremoved", this.clearSelections, this);
40862         view.on("beforerowsinserted", this.clearSelections, this);
40863         if(this.grid.isEditor){
40864             this.grid.on("beforeedit", this.beforeEdit,  this);
40865         }
40866     },
40867
40868         //private
40869     beforeEdit : function(e){
40870         this.select(e.row, e.column, false, true, e.record);
40871     },
40872
40873         //private
40874     onRowUpdated : function(v, index, r){
40875         if(this.selection && this.selection.record == r){
40876             v.onCellSelect(index, this.selection.cell[1]);
40877         }
40878     },
40879
40880         //private
40881     onViewChange : function(){
40882         this.clearSelections(true);
40883     },
40884
40885         /**
40886          * Returns the currently selected cell,.
40887          * @return {Array} The selected cell (row, column) or null if none selected.
40888          */
40889     getSelectedCell : function(){
40890         return this.selection ? this.selection.cell : null;
40891     },
40892
40893     /**
40894      * Clears all selections.
40895      * @param {Boolean} true to prevent the gridview from being notified about the change.
40896      */
40897     clearSelections : function(preventNotify){
40898         var s = this.selection;
40899         if(s){
40900             if(preventNotify !== true){
40901                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40902             }
40903             this.selection = null;
40904             this.fireEvent("selectionchange", this, null);
40905         }
40906     },
40907
40908     /**
40909      * Returns true if there is a selection.
40910      * @return {Boolean}
40911      */
40912     hasSelection : function(){
40913         return this.selection ? true : false;
40914     },
40915
40916     /** @ignore */
40917     handleMouseDown : function(e, t){
40918         var v = this.grid.getView();
40919         if(this.isLocked()){
40920             return;
40921         };
40922         var row = v.findRowIndex(t);
40923         var cell = v.findCellIndex(t);
40924         if(row !== false && cell !== false){
40925             this.select(row, cell);
40926         }
40927     },
40928
40929     /**
40930      * Selects a cell.
40931      * @param {Number} rowIndex
40932      * @param {Number} collIndex
40933      */
40934     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40935         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40936             this.clearSelections();
40937             r = r || this.grid.dataSource.getAt(rowIndex);
40938             this.selection = {
40939                 record : r,
40940                 cell : [rowIndex, colIndex]
40941             };
40942             if(!preventViewNotify){
40943                 var v = this.grid.getView();
40944                 v.onCellSelect(rowIndex, colIndex);
40945                 if(preventFocus !== true){
40946                     v.focusCell(rowIndex, colIndex);
40947                 }
40948             }
40949             this.fireEvent("cellselect", this, rowIndex, colIndex);
40950             this.fireEvent("selectionchange", this, this.selection);
40951         }
40952     },
40953
40954         //private
40955     isSelectable : function(rowIndex, colIndex, cm){
40956         return !cm.isHidden(colIndex);
40957     },
40958
40959     /** @ignore */
40960     handleKeyDown : function(e){
40961         //Roo.log('Cell Sel Model handleKeyDown');
40962         if(!e.isNavKeyPress()){
40963             return;
40964         }
40965         var g = this.grid, s = this.selection;
40966         if(!s){
40967             e.stopEvent();
40968             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40969             if(cell){
40970                 this.select(cell[0], cell[1]);
40971             }
40972             return;
40973         }
40974         var sm = this;
40975         var walk = function(row, col, step){
40976             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40977         };
40978         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40979         var newCell;
40980
40981       
40982
40983         switch(k){
40984             case e.TAB:
40985                 // handled by onEditorKey
40986                 if (g.isEditor && g.editing) {
40987                     return;
40988                 }
40989                 if(e.shiftKey) {
40990                     newCell = walk(r, c-1, -1);
40991                 } else {
40992                     newCell = walk(r, c+1, 1);
40993                 }
40994                 break;
40995             
40996             case e.DOWN:
40997                newCell = walk(r+1, c, 1);
40998                 break;
40999             
41000             case e.UP:
41001                 newCell = walk(r-1, c, -1);
41002                 break;
41003             
41004             case e.RIGHT:
41005                 newCell = walk(r, c+1, 1);
41006                 break;
41007             
41008             case e.LEFT:
41009                 newCell = walk(r, c-1, -1);
41010                 break;
41011             
41012             case e.ENTER:
41013                 
41014                 if(g.isEditor && !g.editing){
41015                    g.startEditing(r, c);
41016                    e.stopEvent();
41017                    return;
41018                 }
41019                 
41020                 
41021              break;
41022         };
41023         if(newCell){
41024             this.select(newCell[0], newCell[1]);
41025             e.stopEvent();
41026             
41027         }
41028     },
41029
41030     acceptsNav : function(row, col, cm){
41031         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41032     },
41033     /**
41034      * Selects a cell.
41035      * @param {Number} field (not used) - as it's normally used as a listener
41036      * @param {Number} e - event - fake it by using
41037      *
41038      * var e = Roo.EventObjectImpl.prototype;
41039      * e.keyCode = e.TAB
41040      *
41041      * 
41042      */
41043     onEditorKey : function(field, e){
41044         
41045         var k = e.getKey(),
41046             newCell,
41047             g = this.grid,
41048             ed = g.activeEditor,
41049             forward = false;
41050         ///Roo.log('onEditorKey' + k);
41051         
41052         
41053         if (this.enter_is_tab && k == e.ENTER) {
41054             k = e.TAB;
41055         }
41056         
41057         if(k == e.TAB){
41058             if(e.shiftKey){
41059                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41060             }else{
41061                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41062                 forward = true;
41063             }
41064             
41065             e.stopEvent();
41066             
41067         } else if(k == e.ENTER &&  !e.ctrlKey){
41068             ed.completeEdit();
41069             e.stopEvent();
41070             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41071         
41072                 } else if(k == e.ESC){
41073             ed.cancelEdit();
41074         }
41075                 
41076         if (newCell) {
41077             var ecall = { cell : newCell, forward : forward };
41078             this.fireEvent('beforeeditnext', ecall );
41079             newCell = ecall.cell;
41080                         forward = ecall.forward;
41081         }
41082                 
41083         if(newCell){
41084             //Roo.log('next cell after edit');
41085             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41086         } else if (forward) {
41087             // tabbed past last
41088             this.fireEvent.defer(100, this, ['tabend',this]);
41089         }
41090     }
41091 });/*
41092  * Based on:
41093  * Ext JS Library 1.1.1
41094  * Copyright(c) 2006-2007, Ext JS, LLC.
41095  *
41096  * Originally Released Under LGPL - original licence link has changed is not relivant.
41097  *
41098  * Fork - LGPL
41099  * <script type="text/javascript">
41100  */
41101  
41102 /**
41103  * @class Roo.grid.EditorGrid
41104  * @extends Roo.grid.Grid
41105  * Class for creating and editable grid.
41106  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41107  * The container MUST have some type of size defined for the grid to fill. The container will be 
41108  * automatically set to position relative if it isn't already.
41109  * @param {Object} dataSource The data model to bind to
41110  * @param {Object} colModel The column model with info about this grid's columns
41111  */
41112 Roo.grid.EditorGrid = function(container, config){
41113     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41114     this.getGridEl().addClass("xedit-grid");
41115
41116     if(!this.selModel){
41117         this.selModel = new Roo.grid.CellSelectionModel();
41118     }
41119
41120     this.activeEditor = null;
41121
41122         this.addEvents({
41123             /**
41124              * @event beforeedit
41125              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41126              * <ul style="padding:5px;padding-left:16px;">
41127              * <li>grid - This grid</li>
41128              * <li>record - The record being edited</li>
41129              * <li>field - The field name being edited</li>
41130              * <li>value - The value for the field being edited.</li>
41131              * <li>row - The grid row index</li>
41132              * <li>column - The grid column index</li>
41133              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41134              * </ul>
41135              * @param {Object} e An edit event (see above for description)
41136              */
41137             "beforeedit" : true,
41138             /**
41139              * @event afteredit
41140              * Fires after a cell is edited. <br />
41141              * <ul style="padding:5px;padding-left:16px;">
41142              * <li>grid - This grid</li>
41143              * <li>record - The record being edited</li>
41144              * <li>field - The field name being edited</li>
41145              * <li>value - The value being set</li>
41146              * <li>originalValue - The original value for the field, before the edit.</li>
41147              * <li>row - The grid row index</li>
41148              * <li>column - The grid column index</li>
41149              * </ul>
41150              * @param {Object} e An edit event (see above for description)
41151              */
41152             "afteredit" : true,
41153             /**
41154              * @event validateedit
41155              * Fires after a cell is edited, but before the value is set in the record. 
41156          * You can use this to modify the value being set in the field, Return false
41157              * to cancel the change. The edit event object has the following properties <br />
41158              * <ul style="padding:5px;padding-left:16px;">
41159          * <li>editor - This editor</li>
41160              * <li>grid - This grid</li>
41161              * <li>record - The record being edited</li>
41162              * <li>field - The field name being edited</li>
41163              * <li>value - The value being set</li>
41164              * <li>originalValue - The original value for the field, before the edit.</li>
41165              * <li>row - The grid row index</li>
41166              * <li>column - The grid column index</li>
41167              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41168              * </ul>
41169              * @param {Object} e An edit event (see above for description)
41170              */
41171             "validateedit" : true
41172         });
41173     this.on("bodyscroll", this.stopEditing,  this);
41174     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41175 };
41176
41177 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41178     /**
41179      * @cfg {Number} clicksToEdit
41180      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41181      */
41182     clicksToEdit: 2,
41183
41184     // private
41185     isEditor : true,
41186     // private
41187     trackMouseOver: false, // causes very odd FF errors
41188
41189     onCellDblClick : function(g, row, col){
41190         this.startEditing(row, col);
41191     },
41192
41193     onEditComplete : function(ed, value, startValue){
41194         this.editing = false;
41195         this.activeEditor = null;
41196         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41197         var r = ed.record;
41198         var field = this.colModel.getDataIndex(ed.col);
41199         var e = {
41200             grid: this,
41201             record: r,
41202             field: field,
41203             originalValue: startValue,
41204             value: value,
41205             row: ed.row,
41206             column: ed.col,
41207             cancel:false,
41208             editor: ed
41209         };
41210         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41211         cell.show();
41212           
41213         if(String(value) !== String(startValue)){
41214             
41215             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41216                 r.set(field, e.value);
41217                 // if we are dealing with a combo box..
41218                 // then we also set the 'name' colum to be the displayField
41219                 if (ed.field.displayField && ed.field.name) {
41220                     r.set(ed.field.name, ed.field.el.dom.value);
41221                 }
41222                 
41223                 delete e.cancel; //?? why!!!
41224                 this.fireEvent("afteredit", e);
41225             }
41226         } else {
41227             this.fireEvent("afteredit", e); // always fire it!
41228         }
41229         this.view.focusCell(ed.row, ed.col);
41230     },
41231
41232     /**
41233      * Starts editing the specified for the specified row/column
41234      * @param {Number} rowIndex
41235      * @param {Number} colIndex
41236      */
41237     startEditing : function(row, col){
41238         this.stopEditing();
41239         if(this.colModel.isCellEditable(col, row)){
41240             this.view.ensureVisible(row, col, true);
41241           
41242             var r = this.dataSource.getAt(row);
41243             var field = this.colModel.getDataIndex(col);
41244             var cell = Roo.get(this.view.getCell(row,col));
41245             var e = {
41246                 grid: this,
41247                 record: r,
41248                 field: field,
41249                 value: r.data[field],
41250                 row: row,
41251                 column: col,
41252                 cancel:false 
41253             };
41254             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41255                 this.editing = true;
41256                 var ed = this.colModel.getCellEditor(col, row);
41257                 
41258                 if (!ed) {
41259                     return;
41260                 }
41261                 if(!ed.rendered){
41262                     ed.render(ed.parentEl || document.body);
41263                 }
41264                 ed.field.reset();
41265                
41266                 cell.hide();
41267                 
41268                 (function(){ // complex but required for focus issues in safari, ie and opera
41269                     ed.row = row;
41270                     ed.col = col;
41271                     ed.record = r;
41272                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41273                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41274                     this.activeEditor = ed;
41275                     var v = r.data[field];
41276                     ed.startEdit(this.view.getCell(row, col), v);
41277                     // combo's with 'displayField and name set
41278                     if (ed.field.displayField && ed.field.name) {
41279                         ed.field.el.dom.value = r.data[ed.field.name];
41280                     }
41281                     
41282                     
41283                 }).defer(50, this);
41284             }
41285         }
41286     },
41287         
41288     /**
41289      * Stops any active editing
41290      */
41291     stopEditing : function(){
41292         if(this.activeEditor){
41293             this.activeEditor.completeEdit();
41294         }
41295         this.activeEditor = null;
41296     },
41297         
41298          /**
41299      * Called to get grid's drag proxy text, by default returns this.ddText.
41300      * @return {String}
41301      */
41302     getDragDropText : function(){
41303         var count = this.selModel.getSelectedCell() ? 1 : 0;
41304         return String.format(this.ddText, count, count == 1 ? '' : 's');
41305     }
41306         
41307 });/*
41308  * Based on:
41309  * Ext JS Library 1.1.1
41310  * Copyright(c) 2006-2007, Ext JS, LLC.
41311  *
41312  * Originally Released Under LGPL - original licence link has changed is not relivant.
41313  *
41314  * Fork - LGPL
41315  * <script type="text/javascript">
41316  */
41317
41318 // private - not really -- you end up using it !
41319 // This is a support class used internally by the Grid components
41320
41321 /**
41322  * @class Roo.grid.GridEditor
41323  * @extends Roo.Editor
41324  * Class for creating and editable grid elements.
41325  * @param {Object} config any settings (must include field)
41326  */
41327 Roo.grid.GridEditor = function(field, config){
41328     if (!config && field.field) {
41329         config = field;
41330         field = Roo.factory(config.field, Roo.form);
41331     }
41332     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41333     field.monitorTab = false;
41334 };
41335
41336 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41337     
41338     /**
41339      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41340      */
41341     
41342     alignment: "tl-tl",
41343     autoSize: "width",
41344     hideEl : false,
41345     cls: "x-small-editor x-grid-editor",
41346     shim:false,
41347     shadow:"frame"
41348 });/*
41349  * Based on:
41350  * Ext JS Library 1.1.1
41351  * Copyright(c) 2006-2007, Ext JS, LLC.
41352  *
41353  * Originally Released Under LGPL - original licence link has changed is not relivant.
41354  *
41355  * Fork - LGPL
41356  * <script type="text/javascript">
41357  */
41358   
41359
41360   
41361 Roo.grid.PropertyRecord = Roo.data.Record.create([
41362     {name:'name',type:'string'},  'value'
41363 ]);
41364
41365
41366 Roo.grid.PropertyStore = function(grid, source){
41367     this.grid = grid;
41368     this.store = new Roo.data.Store({
41369         recordType : Roo.grid.PropertyRecord
41370     });
41371     this.store.on('update', this.onUpdate,  this);
41372     if(source){
41373         this.setSource(source);
41374     }
41375     Roo.grid.PropertyStore.superclass.constructor.call(this);
41376 };
41377
41378
41379
41380 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41381     setSource : function(o){
41382         this.source = o;
41383         this.store.removeAll();
41384         var data = [];
41385         for(var k in o){
41386             if(this.isEditableValue(o[k])){
41387                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41388             }
41389         }
41390         this.store.loadRecords({records: data}, {}, true);
41391     },
41392
41393     onUpdate : function(ds, record, type){
41394         if(type == Roo.data.Record.EDIT){
41395             var v = record.data['value'];
41396             var oldValue = record.modified['value'];
41397             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41398                 this.source[record.id] = v;
41399                 record.commit();
41400                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41401             }else{
41402                 record.reject();
41403             }
41404         }
41405     },
41406
41407     getProperty : function(row){
41408        return this.store.getAt(row);
41409     },
41410
41411     isEditableValue: function(val){
41412         if(val && val instanceof Date){
41413             return true;
41414         }else if(typeof val == 'object' || typeof val == 'function'){
41415             return false;
41416         }
41417         return true;
41418     },
41419
41420     setValue : function(prop, value){
41421         this.source[prop] = value;
41422         this.store.getById(prop).set('value', value);
41423     },
41424
41425     getSource : function(){
41426         return this.source;
41427     }
41428 });
41429
41430 Roo.grid.PropertyColumnModel = function(grid, store){
41431     this.grid = grid;
41432     var g = Roo.grid;
41433     g.PropertyColumnModel.superclass.constructor.call(this, [
41434         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41435         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41436     ]);
41437     this.store = store;
41438     this.bselect = Roo.DomHelper.append(document.body, {
41439         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41440             {tag: 'option', value: 'true', html: 'true'},
41441             {tag: 'option', value: 'false', html: 'false'}
41442         ]
41443     });
41444     Roo.id(this.bselect);
41445     var f = Roo.form;
41446     this.editors = {
41447         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41448         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41449         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41450         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41451         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41452     };
41453     this.renderCellDelegate = this.renderCell.createDelegate(this);
41454     this.renderPropDelegate = this.renderProp.createDelegate(this);
41455 };
41456
41457 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41458     
41459     
41460     nameText : 'Name',
41461     valueText : 'Value',
41462     
41463     dateFormat : 'm/j/Y',
41464     
41465     
41466     renderDate : function(dateVal){
41467         return dateVal.dateFormat(this.dateFormat);
41468     },
41469
41470     renderBool : function(bVal){
41471         return bVal ? 'true' : 'false';
41472     },
41473
41474     isCellEditable : function(colIndex, rowIndex){
41475         return colIndex == 1;
41476     },
41477
41478     getRenderer : function(col){
41479         return col == 1 ?
41480             this.renderCellDelegate : this.renderPropDelegate;
41481     },
41482
41483     renderProp : function(v){
41484         return this.getPropertyName(v);
41485     },
41486
41487     renderCell : function(val){
41488         var rv = val;
41489         if(val instanceof Date){
41490             rv = this.renderDate(val);
41491         }else if(typeof val == 'boolean'){
41492             rv = this.renderBool(val);
41493         }
41494         return Roo.util.Format.htmlEncode(rv);
41495     },
41496
41497     getPropertyName : function(name){
41498         var pn = this.grid.propertyNames;
41499         return pn && pn[name] ? pn[name] : name;
41500     },
41501
41502     getCellEditor : function(colIndex, rowIndex){
41503         var p = this.store.getProperty(rowIndex);
41504         var n = p.data['name'], val = p.data['value'];
41505         
41506         if(typeof(this.grid.customEditors[n]) == 'string'){
41507             return this.editors[this.grid.customEditors[n]];
41508         }
41509         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41510             return this.grid.customEditors[n];
41511         }
41512         if(val instanceof Date){
41513             return this.editors['date'];
41514         }else if(typeof val == 'number'){
41515             return this.editors['number'];
41516         }else if(typeof val == 'boolean'){
41517             return this.editors['boolean'];
41518         }else{
41519             return this.editors['string'];
41520         }
41521     }
41522 });
41523
41524 /**
41525  * @class Roo.grid.PropertyGrid
41526  * @extends Roo.grid.EditorGrid
41527  * This class represents the  interface of a component based property grid control.
41528  * <br><br>Usage:<pre><code>
41529  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41530       
41531  });
41532  // set any options
41533  grid.render();
41534  * </code></pre>
41535   
41536  * @constructor
41537  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41538  * The container MUST have some type of size defined for the grid to fill. The container will be
41539  * automatically set to position relative if it isn't already.
41540  * @param {Object} config A config object that sets properties on this grid.
41541  */
41542 Roo.grid.PropertyGrid = function(container, config){
41543     config = config || {};
41544     var store = new Roo.grid.PropertyStore(this);
41545     this.store = store;
41546     var cm = new Roo.grid.PropertyColumnModel(this, store);
41547     store.store.sort('name', 'ASC');
41548     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41549         ds: store.store,
41550         cm: cm,
41551         enableColLock:false,
41552         enableColumnMove:false,
41553         stripeRows:false,
41554         trackMouseOver: false,
41555         clicksToEdit:1
41556     }, config));
41557     this.getGridEl().addClass('x-props-grid');
41558     this.lastEditRow = null;
41559     this.on('columnresize', this.onColumnResize, this);
41560     this.addEvents({
41561          /**
41562              * @event beforepropertychange
41563              * Fires before a property changes (return false to stop?)
41564              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41565              * @param {String} id Record Id
41566              * @param {String} newval New Value
41567          * @param {String} oldval Old Value
41568              */
41569         "beforepropertychange": true,
41570         /**
41571              * @event propertychange
41572              * Fires after a property changes
41573              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41574              * @param {String} id Record Id
41575              * @param {String} newval New Value
41576          * @param {String} oldval Old Value
41577              */
41578         "propertychange": true
41579     });
41580     this.customEditors = this.customEditors || {};
41581 };
41582 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41583     
41584      /**
41585      * @cfg {Object} customEditors map of colnames=> custom editors.
41586      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41587      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41588      * false disables editing of the field.
41589          */
41590     
41591       /**
41592      * @cfg {Object} propertyNames map of property Names to their displayed value
41593          */
41594     
41595     render : function(){
41596         Roo.grid.PropertyGrid.superclass.render.call(this);
41597         this.autoSize.defer(100, this);
41598     },
41599
41600     autoSize : function(){
41601         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41602         if(this.view){
41603             this.view.fitColumns();
41604         }
41605     },
41606
41607     onColumnResize : function(){
41608         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41609         this.autoSize();
41610     },
41611     /**
41612      * Sets the data for the Grid
41613      * accepts a Key => Value object of all the elements avaiable.
41614      * @param {Object} data  to appear in grid.
41615      */
41616     setSource : function(source){
41617         this.store.setSource(source);
41618         //this.autoSize();
41619     },
41620     /**
41621      * Gets all the data from the grid.
41622      * @return {Object} data  data stored in grid
41623      */
41624     getSource : function(){
41625         return this.store.getSource();
41626     }
41627 });/*
41628   
41629  * Licence LGPL
41630  
41631  */
41632  
41633 /**
41634  * @class Roo.grid.Calendar
41635  * @extends Roo.grid.Grid
41636  * This class extends the Grid to provide a calendar widget
41637  * <br><br>Usage:<pre><code>
41638  var grid = new Roo.grid.Calendar("my-container-id", {
41639      ds: myDataStore,
41640      cm: myColModel,
41641      selModel: mySelectionModel,
41642      autoSizeColumns: true,
41643      monitorWindowResize: false,
41644      trackMouseOver: true
41645      eventstore : real data store..
41646  });
41647  // set any options
41648  grid.render();
41649   
41650   * @constructor
41651  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41652  * The container MUST have some type of size defined for the grid to fill. The container will be
41653  * automatically set to position relative if it isn't already.
41654  * @param {Object} config A config object that sets properties on this grid.
41655  */
41656 Roo.grid.Calendar = function(container, config){
41657         // initialize the container
41658         this.container = Roo.get(container);
41659         this.container.update("");
41660         this.container.setStyle("overflow", "hidden");
41661     this.container.addClass('x-grid-container');
41662
41663     this.id = this.container.id;
41664
41665     Roo.apply(this, config);
41666     // check and correct shorthanded configs
41667     
41668     var rows = [];
41669     var d =1;
41670     for (var r = 0;r < 6;r++) {
41671         
41672         rows[r]=[];
41673         for (var c =0;c < 7;c++) {
41674             rows[r][c]= '';
41675         }
41676     }
41677     if (this.eventStore) {
41678         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41679         this.eventStore.on('load',this.onLoad, this);
41680         this.eventStore.on('beforeload',this.clearEvents, this);
41681          
41682     }
41683     
41684     this.dataSource = new Roo.data.Store({
41685             proxy: new Roo.data.MemoryProxy(rows),
41686             reader: new Roo.data.ArrayReader({}, [
41687                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41688     });
41689
41690     this.dataSource.load();
41691     this.ds = this.dataSource;
41692     this.ds.xmodule = this.xmodule || false;
41693     
41694     
41695     var cellRender = function(v,x,r)
41696     {
41697         return String.format(
41698             '<div class="fc-day  fc-widget-content"><div>' +
41699                 '<div class="fc-event-container"></div>' +
41700                 '<div class="fc-day-number">{0}</div>'+
41701                 
41702                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41703             '</div></div>', v);
41704     
41705     }
41706     
41707     
41708     this.colModel = new Roo.grid.ColumnModel( [
41709         {
41710             xtype: 'ColumnModel',
41711             xns: Roo.grid,
41712             dataIndex : 'weekday0',
41713             header : 'Sunday',
41714             renderer : cellRender
41715         },
41716         {
41717             xtype: 'ColumnModel',
41718             xns: Roo.grid,
41719             dataIndex : 'weekday1',
41720             header : 'Monday',
41721             renderer : cellRender
41722         },
41723         {
41724             xtype: 'ColumnModel',
41725             xns: Roo.grid,
41726             dataIndex : 'weekday2',
41727             header : 'Tuesday',
41728             renderer : cellRender
41729         },
41730         {
41731             xtype: 'ColumnModel',
41732             xns: Roo.grid,
41733             dataIndex : 'weekday3',
41734             header : 'Wednesday',
41735             renderer : cellRender
41736         },
41737         {
41738             xtype: 'ColumnModel',
41739             xns: Roo.grid,
41740             dataIndex : 'weekday4',
41741             header : 'Thursday',
41742             renderer : cellRender
41743         },
41744         {
41745             xtype: 'ColumnModel',
41746             xns: Roo.grid,
41747             dataIndex : 'weekday5',
41748             header : 'Friday',
41749             renderer : cellRender
41750         },
41751         {
41752             xtype: 'ColumnModel',
41753             xns: Roo.grid,
41754             dataIndex : 'weekday6',
41755             header : 'Saturday',
41756             renderer : cellRender
41757         }
41758     ]);
41759     this.cm = this.colModel;
41760     this.cm.xmodule = this.xmodule || false;
41761  
41762         
41763           
41764     //this.selModel = new Roo.grid.CellSelectionModel();
41765     //this.sm = this.selModel;
41766     //this.selModel.init(this);
41767     
41768     
41769     if(this.width){
41770         this.container.setWidth(this.width);
41771     }
41772
41773     if(this.height){
41774         this.container.setHeight(this.height);
41775     }
41776     /** @private */
41777         this.addEvents({
41778         // raw events
41779         /**
41780          * @event click
41781          * The raw click event for the entire grid.
41782          * @param {Roo.EventObject} e
41783          */
41784         "click" : true,
41785         /**
41786          * @event dblclick
41787          * The raw dblclick event for the entire grid.
41788          * @param {Roo.EventObject} e
41789          */
41790         "dblclick" : true,
41791         /**
41792          * @event contextmenu
41793          * The raw contextmenu event for the entire grid.
41794          * @param {Roo.EventObject} e
41795          */
41796         "contextmenu" : true,
41797         /**
41798          * @event mousedown
41799          * The raw mousedown event for the entire grid.
41800          * @param {Roo.EventObject} e
41801          */
41802         "mousedown" : true,
41803         /**
41804          * @event mouseup
41805          * The raw mouseup event for the entire grid.
41806          * @param {Roo.EventObject} e
41807          */
41808         "mouseup" : true,
41809         /**
41810          * @event mouseover
41811          * The raw mouseover event for the entire grid.
41812          * @param {Roo.EventObject} e
41813          */
41814         "mouseover" : true,
41815         /**
41816          * @event mouseout
41817          * The raw mouseout event for the entire grid.
41818          * @param {Roo.EventObject} e
41819          */
41820         "mouseout" : true,
41821         /**
41822          * @event keypress
41823          * The raw keypress event for the entire grid.
41824          * @param {Roo.EventObject} e
41825          */
41826         "keypress" : true,
41827         /**
41828          * @event keydown
41829          * The raw keydown event for the entire grid.
41830          * @param {Roo.EventObject} e
41831          */
41832         "keydown" : true,
41833
41834         // custom events
41835
41836         /**
41837          * @event cellclick
41838          * Fires when a cell is clicked
41839          * @param {Grid} this
41840          * @param {Number} rowIndex
41841          * @param {Number} columnIndex
41842          * @param {Roo.EventObject} e
41843          */
41844         "cellclick" : true,
41845         /**
41846          * @event celldblclick
41847          * Fires when a cell is double clicked
41848          * @param {Grid} this
41849          * @param {Number} rowIndex
41850          * @param {Number} columnIndex
41851          * @param {Roo.EventObject} e
41852          */
41853         "celldblclick" : true,
41854         /**
41855          * @event rowclick
41856          * Fires when a row is clicked
41857          * @param {Grid} this
41858          * @param {Number} rowIndex
41859          * @param {Roo.EventObject} e
41860          */
41861         "rowclick" : true,
41862         /**
41863          * @event rowdblclick
41864          * Fires when a row is double clicked
41865          * @param {Grid} this
41866          * @param {Number} rowIndex
41867          * @param {Roo.EventObject} e
41868          */
41869         "rowdblclick" : true,
41870         /**
41871          * @event headerclick
41872          * Fires when a header is clicked
41873          * @param {Grid} this
41874          * @param {Number} columnIndex
41875          * @param {Roo.EventObject} e
41876          */
41877         "headerclick" : true,
41878         /**
41879          * @event headerdblclick
41880          * Fires when a header cell is double clicked
41881          * @param {Grid} this
41882          * @param {Number} columnIndex
41883          * @param {Roo.EventObject} e
41884          */
41885         "headerdblclick" : true,
41886         /**
41887          * @event rowcontextmenu
41888          * Fires when a row is right clicked
41889          * @param {Grid} this
41890          * @param {Number} rowIndex
41891          * @param {Roo.EventObject} e
41892          */
41893         "rowcontextmenu" : true,
41894         /**
41895          * @event cellcontextmenu
41896          * Fires when a cell is right clicked
41897          * @param {Grid} this
41898          * @param {Number} rowIndex
41899          * @param {Number} cellIndex
41900          * @param {Roo.EventObject} e
41901          */
41902          "cellcontextmenu" : true,
41903         /**
41904          * @event headercontextmenu
41905          * Fires when a header is right clicked
41906          * @param {Grid} this
41907          * @param {Number} columnIndex
41908          * @param {Roo.EventObject} e
41909          */
41910         "headercontextmenu" : true,
41911         /**
41912          * @event bodyscroll
41913          * Fires when the body element is scrolled
41914          * @param {Number} scrollLeft
41915          * @param {Number} scrollTop
41916          */
41917         "bodyscroll" : true,
41918         /**
41919          * @event columnresize
41920          * Fires when the user resizes a column
41921          * @param {Number} columnIndex
41922          * @param {Number} newSize
41923          */
41924         "columnresize" : true,
41925         /**
41926          * @event columnmove
41927          * Fires when the user moves a column
41928          * @param {Number} oldIndex
41929          * @param {Number} newIndex
41930          */
41931         "columnmove" : true,
41932         /**
41933          * @event startdrag
41934          * Fires when row(s) start being dragged
41935          * @param {Grid} this
41936          * @param {Roo.GridDD} dd The drag drop object
41937          * @param {event} e The raw browser event
41938          */
41939         "startdrag" : true,
41940         /**
41941          * @event enddrag
41942          * Fires when a drag operation is complete
41943          * @param {Grid} this
41944          * @param {Roo.GridDD} dd The drag drop object
41945          * @param {event} e The raw browser event
41946          */
41947         "enddrag" : true,
41948         /**
41949          * @event dragdrop
41950          * Fires when dragged row(s) are dropped on a valid DD target
41951          * @param {Grid} this
41952          * @param {Roo.GridDD} dd The drag drop object
41953          * @param {String} targetId The target drag drop object
41954          * @param {event} e The raw browser event
41955          */
41956         "dragdrop" : true,
41957         /**
41958          * @event dragover
41959          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41960          * @param {Grid} this
41961          * @param {Roo.GridDD} dd The drag drop object
41962          * @param {String} targetId The target drag drop object
41963          * @param {event} e The raw browser event
41964          */
41965         "dragover" : true,
41966         /**
41967          * @event dragenter
41968          *  Fires when the dragged row(s) first cross another DD target while being dragged
41969          * @param {Grid} this
41970          * @param {Roo.GridDD} dd The drag drop object
41971          * @param {String} targetId The target drag drop object
41972          * @param {event} e The raw browser event
41973          */
41974         "dragenter" : true,
41975         /**
41976          * @event dragout
41977          * Fires when the dragged row(s) leave another DD target while being dragged
41978          * @param {Grid} this
41979          * @param {Roo.GridDD} dd The drag drop object
41980          * @param {String} targetId The target drag drop object
41981          * @param {event} e The raw browser event
41982          */
41983         "dragout" : true,
41984         /**
41985          * @event rowclass
41986          * Fires when a row is rendered, so you can change add a style to it.
41987          * @param {GridView} gridview   The grid view
41988          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41989          */
41990         'rowclass' : true,
41991
41992         /**
41993          * @event render
41994          * Fires when the grid is rendered
41995          * @param {Grid} grid
41996          */
41997         'render' : true,
41998             /**
41999              * @event select
42000              * Fires when a date is selected
42001              * @param {DatePicker} this
42002              * @param {Date} date The selected date
42003              */
42004         'select': true,
42005         /**
42006              * @event monthchange
42007              * Fires when the displayed month changes 
42008              * @param {DatePicker} this
42009              * @param {Date} date The selected month
42010              */
42011         'monthchange': true,
42012         /**
42013              * @event evententer
42014              * Fires when mouse over an event
42015              * @param {Calendar} this
42016              * @param {event} Event
42017              */
42018         'evententer': true,
42019         /**
42020              * @event eventleave
42021              * Fires when the mouse leaves an
42022              * @param {Calendar} this
42023              * @param {event}
42024              */
42025         'eventleave': true,
42026         /**
42027              * @event eventclick
42028              * Fires when the mouse click an
42029              * @param {Calendar} this
42030              * @param {event}
42031              */
42032         'eventclick': true,
42033         /**
42034              * @event eventrender
42035              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42036              * @param {Calendar} this
42037              * @param {data} data to be modified
42038              */
42039         'eventrender': true
42040         
42041     });
42042
42043     Roo.grid.Grid.superclass.constructor.call(this);
42044     this.on('render', function() {
42045         this.view.el.addClass('x-grid-cal'); 
42046         
42047         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42048
42049     },this);
42050     
42051     if (!Roo.grid.Calendar.style) {
42052         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42053             
42054             
42055             '.x-grid-cal .x-grid-col' :  {
42056                 height: 'auto !important',
42057                 'vertical-align': 'top'
42058             },
42059             '.x-grid-cal  .fc-event-hori' : {
42060                 height: '14px'
42061             }
42062              
42063             
42064         }, Roo.id());
42065     }
42066
42067     
42068     
42069 };
42070 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42071     /**
42072      * @cfg {Store} eventStore The store that loads events.
42073      */
42074     eventStore : 25,
42075
42076      
42077     activeDate : false,
42078     startDay : 0,
42079     autoWidth : true,
42080     monitorWindowResize : false,
42081
42082     
42083     resizeColumns : function() {
42084         var col = (this.view.el.getWidth() / 7) - 3;
42085         // loop through cols, and setWidth
42086         for(var i =0 ; i < 7 ; i++){
42087             this.cm.setColumnWidth(i, col);
42088         }
42089     },
42090      setDate :function(date) {
42091         
42092         Roo.log('setDate?');
42093         
42094         this.resizeColumns();
42095         var vd = this.activeDate;
42096         this.activeDate = date;
42097 //        if(vd && this.el){
42098 //            var t = date.getTime();
42099 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42100 //                Roo.log('using add remove');
42101 //                
42102 //                this.fireEvent('monthchange', this, date);
42103 //                
42104 //                this.cells.removeClass("fc-state-highlight");
42105 //                this.cells.each(function(c){
42106 //                   if(c.dateValue == t){
42107 //                       c.addClass("fc-state-highlight");
42108 //                       setTimeout(function(){
42109 //                            try{c.dom.firstChild.focus();}catch(e){}
42110 //                       }, 50);
42111 //                       return false;
42112 //                   }
42113 //                   return true;
42114 //                });
42115 //                return;
42116 //            }
42117 //        }
42118         
42119         var days = date.getDaysInMonth();
42120         
42121         var firstOfMonth = date.getFirstDateOfMonth();
42122         var startingPos = firstOfMonth.getDay()-this.startDay;
42123         
42124         if(startingPos < this.startDay){
42125             startingPos += 7;
42126         }
42127         
42128         var pm = date.add(Date.MONTH, -1);
42129         var prevStart = pm.getDaysInMonth()-startingPos;
42130 //        
42131         
42132         
42133         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42134         
42135         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42136         //this.cells.addClassOnOver('fc-state-hover');
42137         
42138         var cells = this.cells.elements;
42139         var textEls = this.textNodes;
42140         
42141         //Roo.each(cells, function(cell){
42142         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42143         //});
42144         
42145         days += startingPos;
42146
42147         // convert everything to numbers so it's fast
42148         var day = 86400000;
42149         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42150         //Roo.log(d);
42151         //Roo.log(pm);
42152         //Roo.log(prevStart);
42153         
42154         var today = new Date().clearTime().getTime();
42155         var sel = date.clearTime().getTime();
42156         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42157         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42158         var ddMatch = this.disabledDatesRE;
42159         var ddText = this.disabledDatesText;
42160         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42161         var ddaysText = this.disabledDaysText;
42162         var format = this.format;
42163         
42164         var setCellClass = function(cal, cell){
42165             
42166             //Roo.log('set Cell Class');
42167             cell.title = "";
42168             var t = d.getTime();
42169             
42170             //Roo.log(d);
42171             
42172             
42173             cell.dateValue = t;
42174             if(t == today){
42175                 cell.className += " fc-today";
42176                 cell.className += " fc-state-highlight";
42177                 cell.title = cal.todayText;
42178             }
42179             if(t == sel){
42180                 // disable highlight in other month..
42181                 cell.className += " fc-state-highlight";
42182                 
42183             }
42184             // disabling
42185             if(t < min) {
42186                 //cell.className = " fc-state-disabled";
42187                 cell.title = cal.minText;
42188                 return;
42189             }
42190             if(t > max) {
42191                 //cell.className = " fc-state-disabled";
42192                 cell.title = cal.maxText;
42193                 return;
42194             }
42195             if(ddays){
42196                 if(ddays.indexOf(d.getDay()) != -1){
42197                     // cell.title = ddaysText;
42198                    // cell.className = " fc-state-disabled";
42199                 }
42200             }
42201             if(ddMatch && format){
42202                 var fvalue = d.dateFormat(format);
42203                 if(ddMatch.test(fvalue)){
42204                     cell.title = ddText.replace("%0", fvalue);
42205                    cell.className = " fc-state-disabled";
42206                 }
42207             }
42208             
42209             if (!cell.initialClassName) {
42210                 cell.initialClassName = cell.dom.className;
42211             }
42212             
42213             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42214         };
42215
42216         var i = 0;
42217         
42218         for(; i < startingPos; i++) {
42219             cells[i].dayName =  (++prevStart);
42220             Roo.log(textEls[i]);
42221             d.setDate(d.getDate()+1);
42222             
42223             //cells[i].className = "fc-past fc-other-month";
42224             setCellClass(this, cells[i]);
42225         }
42226         
42227         var intDay = 0;
42228         
42229         for(; i < days; i++){
42230             intDay = i - startingPos + 1;
42231             cells[i].dayName =  (intDay);
42232             d.setDate(d.getDate()+1);
42233             
42234             cells[i].className = ''; // "x-date-active";
42235             setCellClass(this, cells[i]);
42236         }
42237         var extraDays = 0;
42238         
42239         for(; i < 42; i++) {
42240             //textEls[i].innerHTML = (++extraDays);
42241             
42242             d.setDate(d.getDate()+1);
42243             cells[i].dayName = (++extraDays);
42244             cells[i].className = "fc-future fc-other-month";
42245             setCellClass(this, cells[i]);
42246         }
42247         
42248         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42249         
42250         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42251         
42252         // this will cause all the cells to mis
42253         var rows= [];
42254         var i =0;
42255         for (var r = 0;r < 6;r++) {
42256             for (var c =0;c < 7;c++) {
42257                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42258             }    
42259         }
42260         
42261         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42262         for(i=0;i<cells.length;i++) {
42263             
42264             this.cells.elements[i].dayName = cells[i].dayName ;
42265             this.cells.elements[i].className = cells[i].className;
42266             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42267             this.cells.elements[i].title = cells[i].title ;
42268             this.cells.elements[i].dateValue = cells[i].dateValue ;
42269         }
42270         
42271         
42272         
42273         
42274         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42275         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42276         
42277         ////if(totalRows != 6){
42278             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42279            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42280        // }
42281         
42282         this.fireEvent('monthchange', this, date);
42283         
42284         
42285     },
42286  /**
42287      * Returns the grid's SelectionModel.
42288      * @return {SelectionModel}
42289      */
42290     getSelectionModel : function(){
42291         if(!this.selModel){
42292             this.selModel = new Roo.grid.CellSelectionModel();
42293         }
42294         return this.selModel;
42295     },
42296
42297     load: function() {
42298         this.eventStore.load()
42299         
42300         
42301         
42302     },
42303     
42304     findCell : function(dt) {
42305         dt = dt.clearTime().getTime();
42306         var ret = false;
42307         this.cells.each(function(c){
42308             //Roo.log("check " +c.dateValue + '?=' + dt);
42309             if(c.dateValue == dt){
42310                 ret = c;
42311                 return false;
42312             }
42313             return true;
42314         });
42315         
42316         return ret;
42317     },
42318     
42319     findCells : function(rec) {
42320         var s = rec.data.start_dt.clone().clearTime().getTime();
42321        // Roo.log(s);
42322         var e= rec.data.end_dt.clone().clearTime().getTime();
42323        // Roo.log(e);
42324         var ret = [];
42325         this.cells.each(function(c){
42326              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42327             
42328             if(c.dateValue > e){
42329                 return ;
42330             }
42331             if(c.dateValue < s){
42332                 return ;
42333             }
42334             ret.push(c);
42335         });
42336         
42337         return ret;    
42338     },
42339     
42340     findBestRow: function(cells)
42341     {
42342         var ret = 0;
42343         
42344         for (var i =0 ; i < cells.length;i++) {
42345             ret  = Math.max(cells[i].rows || 0,ret);
42346         }
42347         return ret;
42348         
42349     },
42350     
42351     
42352     addItem : function(rec)
42353     {
42354         // look for vertical location slot in
42355         var cells = this.findCells(rec);
42356         
42357         rec.row = this.findBestRow(cells);
42358         
42359         // work out the location.
42360         
42361         var crow = false;
42362         var rows = [];
42363         for(var i =0; i < cells.length; i++) {
42364             if (!crow) {
42365                 crow = {
42366                     start : cells[i],
42367                     end :  cells[i]
42368                 };
42369                 continue;
42370             }
42371             if (crow.start.getY() == cells[i].getY()) {
42372                 // on same row.
42373                 crow.end = cells[i];
42374                 continue;
42375             }
42376             // different row.
42377             rows.push(crow);
42378             crow = {
42379                 start: cells[i],
42380                 end : cells[i]
42381             };
42382             
42383         }
42384         
42385         rows.push(crow);
42386         rec.els = [];
42387         rec.rows = rows;
42388         rec.cells = cells;
42389         for (var i = 0; i < cells.length;i++) {
42390             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42391             
42392         }
42393         
42394         
42395     },
42396     
42397     clearEvents: function() {
42398         
42399         if (!this.eventStore.getCount()) {
42400             return;
42401         }
42402         // reset number of rows in cells.
42403         Roo.each(this.cells.elements, function(c){
42404             c.rows = 0;
42405         });
42406         
42407         this.eventStore.each(function(e) {
42408             this.clearEvent(e);
42409         },this);
42410         
42411     },
42412     
42413     clearEvent : function(ev)
42414     {
42415         if (ev.els) {
42416             Roo.each(ev.els, function(el) {
42417                 el.un('mouseenter' ,this.onEventEnter, this);
42418                 el.un('mouseleave' ,this.onEventLeave, this);
42419                 el.remove();
42420             },this);
42421             ev.els = [];
42422         }
42423     },
42424     
42425     
42426     renderEvent : function(ev,ctr) {
42427         if (!ctr) {
42428              ctr = this.view.el.select('.fc-event-container',true).first();
42429         }
42430         
42431          
42432         this.clearEvent(ev);
42433             //code
42434        
42435         
42436         
42437         ev.els = [];
42438         var cells = ev.cells;
42439         var rows = ev.rows;
42440         this.fireEvent('eventrender', this, ev);
42441         
42442         for(var i =0; i < rows.length; i++) {
42443             
42444             cls = '';
42445             if (i == 0) {
42446                 cls += ' fc-event-start';
42447             }
42448             if ((i+1) == rows.length) {
42449                 cls += ' fc-event-end';
42450             }
42451             
42452             //Roo.log(ev.data);
42453             // how many rows should it span..
42454             var cg = this.eventTmpl.append(ctr,Roo.apply({
42455                 fccls : cls
42456                 
42457             }, ev.data) , true);
42458             
42459             
42460             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42461             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42462             cg.on('click', this.onEventClick, this, ev);
42463             
42464             ev.els.push(cg);
42465             
42466             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42467             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42468             //Roo.log(cg);
42469              
42470             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42471             cg.setWidth(ebox.right - sbox.x -2);
42472         }
42473     },
42474     
42475     renderEvents: function()
42476     {   
42477         // first make sure there is enough space..
42478         
42479         if (!this.eventTmpl) {
42480             this.eventTmpl = new Roo.Template(
42481                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42482                     '<div class="fc-event-inner">' +
42483                         '<span class="fc-event-time">{time}</span>' +
42484                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42485                     '</div>' +
42486                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42487                 '</div>'
42488             );
42489                 
42490         }
42491                
42492         
42493         
42494         this.cells.each(function(c) {
42495             //Roo.log(c.select('.fc-day-content div',true).first());
42496             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42497         });
42498         
42499         var ctr = this.view.el.select('.fc-event-container',true).first();
42500         
42501         var cls;
42502         this.eventStore.each(function(ev){
42503             
42504             this.renderEvent(ev);
42505              
42506              
42507         }, this);
42508         this.view.layout();
42509         
42510     },
42511     
42512     onEventEnter: function (e, el,event,d) {
42513         this.fireEvent('evententer', this, el, event);
42514     },
42515     
42516     onEventLeave: function (e, el,event,d) {
42517         this.fireEvent('eventleave', this, el, event);
42518     },
42519     
42520     onEventClick: function (e, el,event,d) {
42521         this.fireEvent('eventclick', this, el, event);
42522     },
42523     
42524     onMonthChange: function () {
42525         this.store.load();
42526     },
42527     
42528     onLoad: function () {
42529         
42530         //Roo.log('calendar onload');
42531 //         
42532         if(this.eventStore.getCount() > 0){
42533             
42534            
42535             
42536             this.eventStore.each(function(d){
42537                 
42538                 
42539                 // FIXME..
42540                 var add =   d.data;
42541                 if (typeof(add.end_dt) == 'undefined')  {
42542                     Roo.log("Missing End time in calendar data: ");
42543                     Roo.log(d);
42544                     return;
42545                 }
42546                 if (typeof(add.start_dt) == 'undefined')  {
42547                     Roo.log("Missing Start time in calendar data: ");
42548                     Roo.log(d);
42549                     return;
42550                 }
42551                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42552                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42553                 add.id = add.id || d.id;
42554                 add.title = add.title || '??';
42555                 
42556                 this.addItem(d);
42557                 
42558              
42559             },this);
42560         }
42561         
42562         this.renderEvents();
42563     }
42564     
42565
42566 });
42567 /*
42568  grid : {
42569                 xtype: 'Grid',
42570                 xns: Roo.grid,
42571                 listeners : {
42572                     render : function ()
42573                     {
42574                         _this.grid = this;
42575                         
42576                         if (!this.view.el.hasClass('course-timesheet')) {
42577                             this.view.el.addClass('course-timesheet');
42578                         }
42579                         if (this.tsStyle) {
42580                             this.ds.load({});
42581                             return; 
42582                         }
42583                         Roo.log('width');
42584                         Roo.log(_this.grid.view.el.getWidth());
42585                         
42586                         
42587                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42588                             '.course-timesheet .x-grid-row' : {
42589                                 height: '80px'
42590                             },
42591                             '.x-grid-row td' : {
42592                                 'vertical-align' : 0
42593                             },
42594                             '.course-edit-link' : {
42595                                 'color' : 'blue',
42596                                 'text-overflow' : 'ellipsis',
42597                                 'overflow' : 'hidden',
42598                                 'white-space' : 'nowrap',
42599                                 'cursor' : 'pointer'
42600                             },
42601                             '.sub-link' : {
42602                                 'color' : 'green'
42603                             },
42604                             '.de-act-sup-link' : {
42605                                 'color' : 'purple',
42606                                 'text-decoration' : 'line-through'
42607                             },
42608                             '.de-act-link' : {
42609                                 'color' : 'red',
42610                                 'text-decoration' : 'line-through'
42611                             },
42612                             '.course-timesheet .course-highlight' : {
42613                                 'border-top-style': 'dashed !important',
42614                                 'border-bottom-bottom': 'dashed !important'
42615                             },
42616                             '.course-timesheet .course-item' : {
42617                                 'font-family'   : 'tahoma, arial, helvetica',
42618                                 'font-size'     : '11px',
42619                                 'overflow'      : 'hidden',
42620                                 'padding-left'  : '10px',
42621                                 'padding-right' : '10px',
42622                                 'padding-top' : '10px' 
42623                             }
42624                             
42625                         }, Roo.id());
42626                                 this.ds.load({});
42627                     }
42628                 },
42629                 autoWidth : true,
42630                 monitorWindowResize : false,
42631                 cellrenderer : function(v,x,r)
42632                 {
42633                     return v;
42634                 },
42635                 sm : {
42636                     xtype: 'CellSelectionModel',
42637                     xns: Roo.grid
42638                 },
42639                 dataSource : {
42640                     xtype: 'Store',
42641                     xns: Roo.data,
42642                     listeners : {
42643                         beforeload : function (_self, options)
42644                         {
42645                             options.params = options.params || {};
42646                             options.params._month = _this.monthField.getValue();
42647                             options.params.limit = 9999;
42648                             options.params['sort'] = 'when_dt';    
42649                             options.params['dir'] = 'ASC';    
42650                             this.proxy.loadResponse = this.loadResponse;
42651                             Roo.log("load?");
42652                             //this.addColumns();
42653                         },
42654                         load : function (_self, records, options)
42655                         {
42656                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42657                                 // if you click on the translation.. you can edit it...
42658                                 var el = Roo.get(this);
42659                                 var id = el.dom.getAttribute('data-id');
42660                                 var d = el.dom.getAttribute('data-date');
42661                                 var t = el.dom.getAttribute('data-time');
42662                                 //var id = this.child('span').dom.textContent;
42663                                 
42664                                 //Roo.log(this);
42665                                 Pman.Dialog.CourseCalendar.show({
42666                                     id : id,
42667                                     when_d : d,
42668                                     when_t : t,
42669                                     productitem_active : id ? 1 : 0
42670                                 }, function() {
42671                                     _this.grid.ds.load({});
42672                                 });
42673                            
42674                            });
42675                            
42676                            _this.panel.fireEvent('resize', [ '', '' ]);
42677                         }
42678                     },
42679                     loadResponse : function(o, success, response){
42680                             // this is overridden on before load..
42681                             
42682                             Roo.log("our code?");       
42683                             //Roo.log(success);
42684                             //Roo.log(response)
42685                             delete this.activeRequest;
42686                             if(!success){
42687                                 this.fireEvent("loadexception", this, o, response);
42688                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42689                                 return;
42690                             }
42691                             var result;
42692                             try {
42693                                 result = o.reader.read(response);
42694                             }catch(e){
42695                                 Roo.log("load exception?");
42696                                 this.fireEvent("loadexception", this, o, response, e);
42697                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42698                                 return;
42699                             }
42700                             Roo.log("ready...");        
42701                             // loop through result.records;
42702                             // and set this.tdate[date] = [] << array of records..
42703                             _this.tdata  = {};
42704                             Roo.each(result.records, function(r){
42705                                 //Roo.log(r.data);
42706                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42707                                     _this.tdata[r.data.when_dt.format('j')] = [];
42708                                 }
42709                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42710                             });
42711                             
42712                             //Roo.log(_this.tdata);
42713                             
42714                             result.records = [];
42715                             result.totalRecords = 6;
42716                     
42717                             // let's generate some duumy records for the rows.
42718                             //var st = _this.dateField.getValue();
42719                             
42720                             // work out monday..
42721                             //st = st.add(Date.DAY, -1 * st.format('w'));
42722                             
42723                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42724                             
42725                             var firstOfMonth = date.getFirstDayOfMonth();
42726                             var days = date.getDaysInMonth();
42727                             var d = 1;
42728                             var firstAdded = false;
42729                             for (var i = 0; i < result.totalRecords ; i++) {
42730                                 //var d= st.add(Date.DAY, i);
42731                                 var row = {};
42732                                 var added = 0;
42733                                 for(var w = 0 ; w < 7 ; w++){
42734                                     if(!firstAdded && firstOfMonth != w){
42735                                         continue;
42736                                     }
42737                                     if(d > days){
42738                                         continue;
42739                                     }
42740                                     firstAdded = true;
42741                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42742                                     row['weekday'+w] = String.format(
42743                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42744                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42745                                                     d,
42746                                                     date.format('Y-m-')+dd
42747                                                 );
42748                                     added++;
42749                                     if(typeof(_this.tdata[d]) != 'undefined'){
42750                                         Roo.each(_this.tdata[d], function(r){
42751                                             var is_sub = '';
42752                                             var deactive = '';
42753                                             var id = r.id;
42754                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42755                                             if(r.parent_id*1>0){
42756                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42757                                                 id = r.parent_id;
42758                                             }
42759                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42760                                                 deactive = 'de-act-link';
42761                                             }
42762                                             
42763                                             row['weekday'+w] += String.format(
42764                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42765                                                     id, //0
42766                                                     r.product_id_name, //1
42767                                                     r.when_dt.format('h:ia'), //2
42768                                                     is_sub, //3
42769                                                     deactive, //4
42770                                                     desc // 5
42771                                             );
42772                                         });
42773                                     }
42774                                     d++;
42775                                 }
42776                                 
42777                                 // only do this if something added..
42778                                 if(added > 0){ 
42779                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42780                                 }
42781                                 
42782                                 
42783                                 // push it twice. (second one with an hour..
42784                                 
42785                             }
42786                             //Roo.log(result);
42787                             this.fireEvent("load", this, o, o.request.arg);
42788                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42789                         },
42790                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42791                     proxy : {
42792                         xtype: 'HttpProxy',
42793                         xns: Roo.data,
42794                         method : 'GET',
42795                         url : baseURL + '/Roo/Shop_course.php'
42796                     },
42797                     reader : {
42798                         xtype: 'JsonReader',
42799                         xns: Roo.data,
42800                         id : 'id',
42801                         fields : [
42802                             {
42803                                 'name': 'id',
42804                                 'type': 'int'
42805                             },
42806                             {
42807                                 'name': 'when_dt',
42808                                 'type': 'string'
42809                             },
42810                             {
42811                                 'name': 'end_dt',
42812                                 'type': 'string'
42813                             },
42814                             {
42815                                 'name': 'parent_id',
42816                                 'type': 'int'
42817                             },
42818                             {
42819                                 'name': 'product_id',
42820                                 'type': 'int'
42821                             },
42822                             {
42823                                 'name': 'productitem_id',
42824                                 'type': 'int'
42825                             },
42826                             {
42827                                 'name': 'guid',
42828                                 'type': 'int'
42829                             }
42830                         ]
42831                     }
42832                 },
42833                 toolbar : {
42834                     xtype: 'Toolbar',
42835                     xns: Roo,
42836                     items : [
42837                         {
42838                             xtype: 'Button',
42839                             xns: Roo.Toolbar,
42840                             listeners : {
42841                                 click : function (_self, e)
42842                                 {
42843                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42844                                     sd.setMonth(sd.getMonth()-1);
42845                                     _this.monthField.setValue(sd.format('Y-m-d'));
42846                                     _this.grid.ds.load({});
42847                                 }
42848                             },
42849                             text : "Back"
42850                         },
42851                         {
42852                             xtype: 'Separator',
42853                             xns: Roo.Toolbar
42854                         },
42855                         {
42856                             xtype: 'MonthField',
42857                             xns: Roo.form,
42858                             listeners : {
42859                                 render : function (_self)
42860                                 {
42861                                     _this.monthField = _self;
42862                                    // _this.monthField.set  today
42863                                 },
42864                                 select : function (combo, date)
42865                                 {
42866                                     _this.grid.ds.load({});
42867                                 }
42868                             },
42869                             value : (function() { return new Date(); })()
42870                         },
42871                         {
42872                             xtype: 'Separator',
42873                             xns: Roo.Toolbar
42874                         },
42875                         {
42876                             xtype: 'TextItem',
42877                             xns: Roo.Toolbar,
42878                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42879                         },
42880                         {
42881                             xtype: 'Fill',
42882                             xns: Roo.Toolbar
42883                         },
42884                         {
42885                             xtype: 'Button',
42886                             xns: Roo.Toolbar,
42887                             listeners : {
42888                                 click : function (_self, e)
42889                                 {
42890                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42891                                     sd.setMonth(sd.getMonth()+1);
42892                                     _this.monthField.setValue(sd.format('Y-m-d'));
42893                                     _this.grid.ds.load({});
42894                                 }
42895                             },
42896                             text : "Next"
42897                         }
42898                     ]
42899                 },
42900                  
42901             }
42902         };
42903         
42904         *//*
42905  * Based on:
42906  * Ext JS Library 1.1.1
42907  * Copyright(c) 2006-2007, Ext JS, LLC.
42908  *
42909  * Originally Released Under LGPL - original licence link has changed is not relivant.
42910  *
42911  * Fork - LGPL
42912  * <script type="text/javascript">
42913  */
42914  
42915 /**
42916  * @class Roo.LoadMask
42917  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42918  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42919  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42920  * element's UpdateManager load indicator and will be destroyed after the initial load.
42921  * @constructor
42922  * Create a new LoadMask
42923  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42924  * @param {Object} config The config object
42925  */
42926 Roo.LoadMask = function(el, config){
42927     this.el = Roo.get(el);
42928     Roo.apply(this, config);
42929     if(this.store){
42930         this.store.on('beforeload', this.onBeforeLoad, this);
42931         this.store.on('load', this.onLoad, this);
42932         this.store.on('loadexception', this.onLoadException, this);
42933         this.removeMask = false;
42934     }else{
42935         var um = this.el.getUpdateManager();
42936         um.showLoadIndicator = false; // disable the default indicator
42937         um.on('beforeupdate', this.onBeforeLoad, this);
42938         um.on('update', this.onLoad, this);
42939         um.on('failure', this.onLoad, this);
42940         this.removeMask = true;
42941     }
42942 };
42943
42944 Roo.LoadMask.prototype = {
42945     /**
42946      * @cfg {Boolean} removeMask
42947      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42948      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42949      */
42950     removeMask : false,
42951     /**
42952      * @cfg {String} msg
42953      * The text to display in a centered loading message box (defaults to 'Loading...')
42954      */
42955     msg : 'Loading...',
42956     /**
42957      * @cfg {String} msgCls
42958      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42959      */
42960     msgCls : 'x-mask-loading',
42961
42962     /**
42963      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42964      * @type Boolean
42965      */
42966     disabled: false,
42967
42968     /**
42969      * Disables the mask to prevent it from being displayed
42970      */
42971     disable : function(){
42972        this.disabled = true;
42973     },
42974
42975     /**
42976      * Enables the mask so that it can be displayed
42977      */
42978     enable : function(){
42979         this.disabled = false;
42980     },
42981     
42982     onLoadException : function()
42983     {
42984         Roo.log(arguments);
42985         
42986         if (typeof(arguments[3]) != 'undefined') {
42987             Roo.MessageBox.alert("Error loading",arguments[3]);
42988         } 
42989         /*
42990         try {
42991             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42992                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42993             }   
42994         } catch(e) {
42995             
42996         }
42997         */
42998     
42999         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43000     },
43001     // private
43002     onLoad : function()
43003     {
43004         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43005     },
43006
43007     // private
43008     onBeforeLoad : function(){
43009         if(!this.disabled){
43010             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43011         }
43012     },
43013
43014     // private
43015     destroy : function(){
43016         if(this.store){
43017             this.store.un('beforeload', this.onBeforeLoad, this);
43018             this.store.un('load', this.onLoad, this);
43019             this.store.un('loadexception', this.onLoadException, this);
43020         }else{
43021             var um = this.el.getUpdateManager();
43022             um.un('beforeupdate', this.onBeforeLoad, this);
43023             um.un('update', this.onLoad, this);
43024             um.un('failure', this.onLoad, this);
43025         }
43026     }
43027 };/*
43028  * Based on:
43029  * Ext JS Library 1.1.1
43030  * Copyright(c) 2006-2007, Ext JS, LLC.
43031  *
43032  * Originally Released Under LGPL - original licence link has changed is not relivant.
43033  *
43034  * Fork - LGPL
43035  * <script type="text/javascript">
43036  */
43037
43038
43039 /**
43040  * @class Roo.XTemplate
43041  * @extends Roo.Template
43042  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43043 <pre><code>
43044 var t = new Roo.XTemplate(
43045         '&lt;select name="{name}"&gt;',
43046                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43047         '&lt;/select&gt;'
43048 );
43049  
43050 // then append, applying the master template values
43051  </code></pre>
43052  *
43053  * Supported features:
43054  *
43055  *  Tags:
43056
43057 <pre><code>
43058       {a_variable} - output encoded.
43059       {a_variable.format:("Y-m-d")} - call a method on the variable
43060       {a_variable:raw} - unencoded output
43061       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43062       {a_variable:this.method_on_template(...)} - call a method on the template object.
43063  
43064 </code></pre>
43065  *  The tpl tag:
43066 <pre><code>
43067         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43068         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43069         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43070         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43071   
43072         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43073         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43074 </code></pre>
43075  *      
43076  */
43077 Roo.XTemplate = function()
43078 {
43079     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43080     if (this.html) {
43081         this.compile();
43082     }
43083 };
43084
43085
43086 Roo.extend(Roo.XTemplate, Roo.Template, {
43087
43088     /**
43089      * The various sub templates
43090      */
43091     tpls : false,
43092     /**
43093      *
43094      * basic tag replacing syntax
43095      * WORD:WORD()
43096      *
43097      * // you can fake an object call by doing this
43098      *  x.t:(test,tesT) 
43099      * 
43100      */
43101     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43102
43103     /**
43104      * compile the template
43105      *
43106      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43107      *
43108      */
43109     compile: function()
43110     {
43111         var s = this.html;
43112      
43113         s = ['<tpl>', s, '</tpl>'].join('');
43114     
43115         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43116             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43117             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43118             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43119             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43120             m,
43121             id     = 0,
43122             tpls   = [];
43123     
43124         while(true == !!(m = s.match(re))){
43125             var forMatch   = m[0].match(nameRe),
43126                 ifMatch   = m[0].match(ifRe),
43127                 execMatch   = m[0].match(execRe),
43128                 namedMatch   = m[0].match(namedRe),
43129                 
43130                 exp  = null, 
43131                 fn   = null,
43132                 exec = null,
43133                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43134                 
43135             if (ifMatch) {
43136                 // if - puts fn into test..
43137                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43138                 if(exp){
43139                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43140                 }
43141             }
43142             
43143             if (execMatch) {
43144                 // exec - calls a function... returns empty if true is  returned.
43145                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43146                 if(exp){
43147                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43148                 }
43149             }
43150             
43151             
43152             if (name) {
43153                 // for = 
43154                 switch(name){
43155                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43156                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43157                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43158                 }
43159             }
43160             var uid = namedMatch ? namedMatch[1] : id;
43161             
43162             
43163             tpls.push({
43164                 id:     namedMatch ? namedMatch[1] : id,
43165                 target: name,
43166                 exec:   exec,
43167                 test:   fn,
43168                 body:   m[1] || ''
43169             });
43170             if (namedMatch) {
43171                 s = s.replace(m[0], '');
43172             } else { 
43173                 s = s.replace(m[0], '{xtpl'+ id + '}');
43174             }
43175             ++id;
43176         }
43177         this.tpls = [];
43178         for(var i = tpls.length-1; i >= 0; --i){
43179             this.compileTpl(tpls[i]);
43180             this.tpls[tpls[i].id] = tpls[i];
43181         }
43182         this.master = tpls[tpls.length-1];
43183         return this;
43184     },
43185     /**
43186      * same as applyTemplate, except it's done to one of the subTemplates
43187      * when using named templates, you can do:
43188      *
43189      * var str = pl.applySubTemplate('your-name', values);
43190      *
43191      * 
43192      * @param {Number} id of the template
43193      * @param {Object} values to apply to template
43194      * @param {Object} parent (normaly the instance of this object)
43195      */
43196     applySubTemplate : function(id, values, parent)
43197     {
43198         
43199         
43200         var t = this.tpls[id];
43201         
43202         
43203         try { 
43204             if(t.test && !t.test.call(this, values, parent)){
43205                 return '';
43206             }
43207         } catch(e) {
43208             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43209             Roo.log(e.toString());
43210             Roo.log(t.test);
43211             return ''
43212         }
43213         try { 
43214             
43215             if(t.exec && t.exec.call(this, values, parent)){
43216                 return '';
43217             }
43218         } catch(e) {
43219             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43220             Roo.log(e.toString());
43221             Roo.log(t.exec);
43222             return ''
43223         }
43224         try {
43225             var vs = t.target ? t.target.call(this, values, parent) : values;
43226             parent = t.target ? values : parent;
43227             if(t.target && vs instanceof Array){
43228                 var buf = [];
43229                 for(var i = 0, len = vs.length; i < len; i++){
43230                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43231                 }
43232                 return buf.join('');
43233             }
43234             return t.compiled.call(this, vs, parent);
43235         } catch (e) {
43236             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43237             Roo.log(e.toString());
43238             Roo.log(t.compiled);
43239             return '';
43240         }
43241     },
43242
43243     compileTpl : function(tpl)
43244     {
43245         var fm = Roo.util.Format;
43246         var useF = this.disableFormats !== true;
43247         var sep = Roo.isGecko ? "+" : ",";
43248         var undef = function(str) {
43249             Roo.log("Property not found :"  + str);
43250             return '';
43251         };
43252         
43253         var fn = function(m, name, format, args)
43254         {
43255             //Roo.log(arguments);
43256             args = args ? args.replace(/\\'/g,"'") : args;
43257             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43258             if (typeof(format) == 'undefined') {
43259                 format= 'htmlEncode';
43260             }
43261             if (format == 'raw' ) {
43262                 format = false;
43263             }
43264             
43265             if(name.substr(0, 4) == 'xtpl'){
43266                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43267             }
43268             
43269             // build an array of options to determine if value is undefined..
43270             
43271             // basically get 'xxxx.yyyy' then do
43272             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43273             //    (function () { Roo.log("Property not found"); return ''; })() :
43274             //    ......
43275             
43276             var udef_ar = [];
43277             var lookfor = '';
43278             Roo.each(name.split('.'), function(st) {
43279                 lookfor += (lookfor.length ? '.': '') + st;
43280                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43281             });
43282             
43283             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43284             
43285             
43286             if(format && useF){
43287                 
43288                 args = args ? ',' + args : "";
43289                  
43290                 if(format.substr(0, 5) != "this."){
43291                     format = "fm." + format + '(';
43292                 }else{
43293                     format = 'this.call("'+ format.substr(5) + '", ';
43294                     args = ", values";
43295                 }
43296                 
43297                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43298             }
43299              
43300             if (args.length) {
43301                 // called with xxyx.yuu:(test,test)
43302                 // change to ()
43303                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43304             }
43305             // raw.. - :raw modifier..
43306             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43307             
43308         };
43309         var body;
43310         // branched to use + in gecko and [].join() in others
43311         if(Roo.isGecko){
43312             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43313                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43314                     "';};};";
43315         }else{
43316             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43317             body.push(tpl.body.replace(/(\r\n|\n)/g,
43318                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43319             body.push("'].join('');};};");
43320             body = body.join('');
43321         }
43322         
43323         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43324        
43325         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43326         eval(body);
43327         
43328         return this;
43329     },
43330
43331     applyTemplate : function(values){
43332         return this.master.compiled.call(this, values, {});
43333         //var s = this.subs;
43334     },
43335
43336     apply : function(){
43337         return this.applyTemplate.apply(this, arguments);
43338     }
43339
43340  });
43341
43342 Roo.XTemplate.from = function(el){
43343     el = Roo.getDom(el);
43344     return new Roo.XTemplate(el.value || el.innerHTML);
43345 };