d570ba0f4056f089092edb754fc2bdb0663de836
[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             if (this.enableBlocks) {
25415                 Roo.htmleditor.Block.initAll(this.doc.body);
25416             }
25417             
25418             this.updateLanguage();
25419             
25420             var lc = this.doc.body.lastChild;
25421             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25422                 // add an extra line at the end.
25423                 this.doc.body.appendChild(this.doc.createElement('br'));
25424             }
25425             
25426             
25427         }
25428     },
25429
25430     // private
25431     deferFocus : function(){
25432         this.focus.defer(10, this);
25433     },
25434
25435     // doc'ed in Field
25436     focus : function(){
25437         if(this.win && !this.sourceEditMode){
25438             this.win.focus();
25439         }else{
25440             this.el.focus();
25441         }
25442     },
25443     
25444     assignDocWin: function()
25445     {
25446         var iframe = this.iframe;
25447         
25448          if(Roo.isIE){
25449             this.doc = iframe.contentWindow.document;
25450             this.win = iframe.contentWindow;
25451         } else {
25452 //            if (!Roo.get(this.frameId)) {
25453 //                return;
25454 //            }
25455 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25456 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25457             
25458             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25459                 return;
25460             }
25461             
25462             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25463             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25464         }
25465     },
25466     
25467     // private
25468     initEditor : function(){
25469         //console.log("INIT EDITOR");
25470         this.assignDocWin();
25471         
25472         
25473         
25474         this.doc.designMode="on";
25475         this.doc.open();
25476         this.doc.write(this.getDocMarkup());
25477         this.doc.close();
25478         
25479         var dbody = (this.doc.body || this.doc.documentElement);
25480         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25481         // this copies styles from the containing element into thsi one..
25482         // not sure why we need all of this..
25483         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25484         
25485         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25486         //ss['background-attachment'] = 'fixed'; // w3c
25487         dbody.bgProperties = 'fixed'; // ie
25488         dbody.setAttribute("translate", "no");
25489         
25490         //Roo.DomHelper.applyStyles(dbody, ss);
25491         Roo.EventManager.on(this.doc, {
25492              
25493             'mouseup': this.onEditorEvent,
25494             'dblclick': this.onEditorEvent,
25495             'click': this.onEditorEvent,
25496             'keyup': this.onEditorEvent,
25497             
25498             buffer:100,
25499             scope: this
25500         });
25501         Roo.EventManager.on(this.doc, {
25502             'paste': this.onPasteEvent,
25503             scope : this
25504         });
25505         if(Roo.isGecko){
25506             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25507         }
25508         //??? needed???
25509         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25510             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25511         }
25512         this.initialized = true;
25513
25514         
25515         // initialize special key events - enter
25516         new Roo.htmleditor.KeyEnter({core : this});
25517         
25518          
25519         
25520         this.owner.fireEvent('initialize', this);
25521         this.pushValue();
25522     },
25523     // this is to prevent a href clicks resulting in a redirect?
25524    
25525     onPasteEvent : function(e,v)
25526     {
25527         // I think we better assume paste is going to be a dirty load of rubish from word..
25528         
25529         // even pasting into a 'email version' of this widget will have to clean up that mess.
25530         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25531         
25532         // check what type of paste - if it's an image, then handle it differently.
25533         if (cd.files.length > 0) {
25534             // pasting images?
25535             var urlAPI = (window.createObjectURL && window) || 
25536                 (window.URL && URL.revokeObjectURL && URL) || 
25537                 (window.webkitURL && webkitURL);
25538     
25539             var url = urlAPI.createObjectURL( cd.files[0]);
25540             this.insertAtCursor('<img src=" + url + ">');
25541             return false;
25542         }
25543         
25544         var html = cd.getData('text/html'); // clipboard event
25545         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25546         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25547         Roo.log(images);
25548         //Roo.log(imgs);
25549         // fixme..
25550         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
25551                        .map(function(g) { return g.toDataURL(); });
25552         
25553         
25554         html = this.cleanWordChars(html);
25555         
25556         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25557         
25558         
25559         var sn = this.getParentElement();
25560         // check if d contains a table, and prevent nesting??
25561         //Roo.log(d.getElementsByTagName('table'));
25562         //Roo.log(sn);
25563         //Roo.log(sn.closest('table'));
25564         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25565             e.preventDefault();
25566             this.insertAtCursor("You can not nest tables");
25567             //Roo.log("prevent?"); // fixme - 
25568             return false;
25569         }
25570         
25571         if (images.length > 0) {
25572             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25573                 img.setAttribute('src', images[i]);
25574             });
25575         }
25576         if (this.autoClean) {
25577             new Roo.htmleditor.FilterStyleToTag({ node : d });
25578             new Roo.htmleditor.FilterAttributes({
25579                 node : d,
25580                 attrib_white : ['href', 'src', 'name', 'align'],
25581                 attrib_clean : ['href', 'src' ] 
25582             });
25583             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25584             // should be fonts..
25585             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25586             new Roo.htmleditor.FilterParagraph({ node : d });
25587             new Roo.htmleditor.FilterSpan({ node : d });
25588             new Roo.htmleditor.FilterLongBr({ node : d });
25589         }
25590         if (this.enableBlocks) {
25591                 
25592             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25593                 if (img.closest('figure')) { // assume!! that it's aready
25594                     return;
25595                 }
25596                 var fig  = new Roo.htmleditor.BlockFigure({
25597                     image_src  : img.src
25598                 });
25599                 fig.updateElement(img); // replace it..
25600                 
25601             });
25602         }
25603         
25604         
25605         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25606         if (this.enableBlocks) {
25607             Roo.htmleditor.Block.initAll(this.doc.body);
25608         }
25609         
25610         
25611         e.preventDefault();
25612         return false;
25613         // default behaveiour should be our local cleanup paste? (optional?)
25614         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25615         //this.owner.fireEvent('paste', e, v);
25616     },
25617     // private
25618     onDestroy : function(){
25619         
25620         
25621         
25622         if(this.rendered){
25623             
25624             //for (var i =0; i < this.toolbars.length;i++) {
25625             //    // fixme - ask toolbars for heights?
25626             //    this.toolbars[i].onDestroy();
25627            // }
25628             
25629             //this.wrap.dom.innerHTML = '';
25630             //this.wrap.remove();
25631         }
25632     },
25633
25634     // private
25635     onFirstFocus : function(){
25636         
25637         this.assignDocWin();
25638         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25639         
25640         this.activated = true;
25641          
25642     
25643         if(Roo.isGecko){ // prevent silly gecko errors
25644             this.win.focus();
25645             var s = this.win.getSelection();
25646             if(!s.focusNode || s.focusNode.nodeType != 3){
25647                 var r = s.getRangeAt(0);
25648                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25649                 r.collapse(true);
25650                 this.deferFocus();
25651             }
25652             try{
25653                 this.execCmd('useCSS', true);
25654                 this.execCmd('styleWithCSS', false);
25655             }catch(e){}
25656         }
25657         this.owner.fireEvent('activate', this);
25658     },
25659
25660     // private
25661     adjustFont: function(btn){
25662         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25663         //if(Roo.isSafari){ // safari
25664         //    adjust *= 2;
25665        // }
25666         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25667         if(Roo.isSafari){ // safari
25668             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25669             v =  (v < 10) ? 10 : v;
25670             v =  (v > 48) ? 48 : v;
25671             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25672             
25673         }
25674         
25675         
25676         v = Math.max(1, v+adjust);
25677         
25678         this.execCmd('FontSize', v  );
25679     },
25680
25681     onEditorEvent : function(e)
25682     {
25683          
25684         
25685         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25686             return; // we do not handle this.. (undo manager does..)
25687         }
25688         // in theory this detects if the last element is not a br, then we try and do that.
25689         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25690         if (e &&
25691             e.target.nodeName == 'BODY' &&
25692             e.type == "mouseup" &&
25693             this.doc.body.lastChild
25694            ) {
25695             var lc = this.doc.body.lastChild;
25696             // gtx-trans is google translate plugin adding crap.
25697             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25698                 lc = lc.previousSibling;
25699             }
25700             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25701             // if last element is <BR> - then dont do anything.
25702             
25703                 var ns = this.doc.createElement('br');
25704                 this.doc.body.appendChild(ns);
25705                 range = this.doc.createRange();
25706                 range.setStartAfter(ns);
25707                 range.collapse(true);
25708                 var sel = this.win.getSelection();
25709                 sel.removeAllRanges();
25710                 sel.addRange(range);
25711             }
25712         }
25713         
25714         
25715         
25716         this.fireEditorEvent(e);
25717       //  this.updateToolbar();
25718         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25719     },
25720     
25721     fireEditorEvent: function(e)
25722     {
25723         this.owner.fireEvent('editorevent', this, e);
25724     },
25725
25726     insertTag : function(tg)
25727     {
25728         // could be a bit smarter... -> wrap the current selected tRoo..
25729         if (tg.toLowerCase() == 'span' ||
25730             tg.toLowerCase() == 'code' ||
25731             tg.toLowerCase() == 'sup' ||
25732             tg.toLowerCase() == 'sub' 
25733             ) {
25734             
25735             range = this.createRange(this.getSelection());
25736             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25737             wrappingNode.appendChild(range.extractContents());
25738             range.insertNode(wrappingNode);
25739
25740             return;
25741             
25742             
25743             
25744         }
25745         this.execCmd("formatblock",   tg);
25746         this.undoManager.addEvent(); 
25747     },
25748     
25749     insertText : function(txt)
25750     {
25751         
25752         
25753         var range = this.createRange();
25754         range.deleteContents();
25755                //alert(Sender.getAttribute('label'));
25756                
25757         range.insertNode(this.doc.createTextNode(txt));
25758         this.undoManager.addEvent();
25759     } ,
25760     
25761      
25762
25763     /**
25764      * Executes a Midas editor command on the editor document and performs necessary focus and
25765      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25766      * @param {String} cmd The Midas command
25767      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25768      */
25769     relayCmd : function(cmd, value)
25770     {
25771         
25772         switch (cmd) {
25773             case 'justifyleft':
25774             case 'justifyright':
25775             case 'justifycenter':
25776                 // if we are in a cell, then we will adjust the
25777                 var n = this.getParentElement();
25778                 var td = n.closest('td');
25779                 if (td) {
25780                     var bl = Roo.htmleditor.Block.factory(td);
25781                     bl.textAlign = cmd.replace('justify','');
25782                     bl.updateElement();
25783                     this.owner.fireEvent('editorevent', this);
25784                     return;
25785                 }
25786                 this.execCmd('styleWithCSS', true); // 
25787                 break;
25788             case 'bold':
25789             case 'italic':
25790                 // if there is no selection, then we insert, and set the curson inside it..
25791                 this.execCmd('styleWithCSS', false); 
25792                 break;
25793                 
25794         
25795             default:
25796                 break;
25797         }
25798         
25799         
25800         this.win.focus();
25801         this.execCmd(cmd, value);
25802         this.owner.fireEvent('editorevent', this);
25803         //this.updateToolbar();
25804         this.owner.deferFocus();
25805     },
25806
25807     /**
25808      * Executes a Midas editor command directly on the editor document.
25809      * For visual commands, you should use {@link #relayCmd} instead.
25810      * <b>This should only be called after the editor is initialized.</b>
25811      * @param {String} cmd The Midas command
25812      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25813      */
25814     execCmd : function(cmd, value){
25815         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25816         this.syncValue();
25817     },
25818  
25819  
25820    
25821     /**
25822      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25823      * to insert tRoo.
25824      * @param {String} text | dom node.. 
25825      */
25826     insertAtCursor : function(text)
25827     {
25828         
25829         if(!this.activated){
25830             return;
25831         }
25832          
25833         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25834             this.win.focus();
25835             
25836             
25837             // from jquery ui (MIT licenced)
25838             var range, node;
25839             var win = this.win;
25840             
25841             if (win.getSelection && win.getSelection().getRangeAt) {
25842                 
25843                 // delete the existing?
25844                 
25845                 this.createRange(this.getSelection()).deleteContents();
25846                 range = win.getSelection().getRangeAt(0);
25847                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25848                 range.insertNode(node);
25849                 range = range.cloneRange();
25850                 range.collapse(false);
25851                  
25852                 win.getSelection().removeAllRanges();
25853                 win.getSelection().addRange(range);
25854                 
25855                 
25856                 
25857             } else if (win.document.selection && win.document.selection.createRange) {
25858                 // no firefox support
25859                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25860                 win.document.selection.createRange().pasteHTML(txt);
25861             
25862             } else {
25863                 // no firefox support
25864                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25865                 this.execCmd('InsertHTML', txt);
25866             } 
25867             this.syncValue();
25868             
25869             this.deferFocus();
25870         }
25871     },
25872  // private
25873     mozKeyPress : function(e){
25874         if(e.ctrlKey){
25875             var c = e.getCharCode(), cmd;
25876           
25877             if(c > 0){
25878                 c = String.fromCharCode(c).toLowerCase();
25879                 switch(c){
25880                     case 'b':
25881                         cmd = 'bold';
25882                         break;
25883                     case 'i':
25884                         cmd = 'italic';
25885                         break;
25886                     
25887                     case 'u':
25888                         cmd = 'underline';
25889                         break;
25890                     
25891                     //case 'v':
25892                       //  this.cleanUpPaste.defer(100, this);
25893                       //  return;
25894                         
25895                 }
25896                 if(cmd){
25897                     
25898                     this.relayCmd(cmd);
25899                     //this.win.focus();
25900                     //this.execCmd(cmd);
25901                     //this.deferFocus();
25902                     e.preventDefault();
25903                 }
25904                 
25905             }
25906         }
25907     },
25908
25909     // private
25910     fixKeys : function(){ // load time branching for fastest keydown performance
25911         
25912         
25913         if(Roo.isIE){
25914             return function(e){
25915                 var k = e.getKey(), r;
25916                 if(k == e.TAB){
25917                     e.stopEvent();
25918                     r = this.doc.selection.createRange();
25919                     if(r){
25920                         r.collapse(true);
25921                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25922                         this.deferFocus();
25923                     }
25924                     return;
25925                 }
25926                 /// this is handled by Roo.htmleditor.KeyEnter
25927                  /*
25928                 if(k == e.ENTER){
25929                     r = this.doc.selection.createRange();
25930                     if(r){
25931                         var target = r.parentElement();
25932                         if(!target || target.tagName.toLowerCase() != 'li'){
25933                             e.stopEvent();
25934                             r.pasteHTML('<br/>');
25935                             r.collapse(false);
25936                             r.select();
25937                         }
25938                     }
25939                 }
25940                 */
25941                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25942                 //    this.cleanUpPaste.defer(100, this);
25943                 //    return;
25944                 //}
25945                 
25946                 
25947             };
25948         }else if(Roo.isOpera){
25949             return function(e){
25950                 var k = e.getKey();
25951                 if(k == e.TAB){
25952                     e.stopEvent();
25953                     this.win.focus();
25954                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25955                     this.deferFocus();
25956                 }
25957                
25958                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25959                 //    this.cleanUpPaste.defer(100, this);
25960                  //   return;
25961                 //}
25962                 
25963             };
25964         }else if(Roo.isSafari){
25965             return function(e){
25966                 var k = e.getKey();
25967                 
25968                 if(k == e.TAB){
25969                     e.stopEvent();
25970                     this.execCmd('InsertText','\t');
25971                     this.deferFocus();
25972                     return;
25973                 }
25974                  this.mozKeyPress(e);
25975                 
25976                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25977                  //   this.cleanUpPaste.defer(100, this);
25978                  //   return;
25979                // }
25980                 
25981              };
25982         }
25983     }(),
25984     
25985     getAllAncestors: function()
25986     {
25987         var p = this.getSelectedNode();
25988         var a = [];
25989         if (!p) {
25990             a.push(p); // push blank onto stack..
25991             p = this.getParentElement();
25992         }
25993         
25994         
25995         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25996             a.push(p);
25997             p = p.parentNode;
25998         }
25999         a.push(this.doc.body);
26000         return a;
26001     },
26002     lastSel : false,
26003     lastSelNode : false,
26004     
26005     
26006     getSelection : function() 
26007     {
26008         this.assignDocWin();
26009         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26010     },
26011     /**
26012      * Select a dom node
26013      * @param {DomElement} node the node to select
26014      */
26015     selectNode : function(node, collapse)
26016     {
26017         var nodeRange = node.ownerDocument.createRange();
26018         try {
26019             nodeRange.selectNode(node);
26020         } catch (e) {
26021             nodeRange.selectNodeContents(node);
26022         }
26023         if (collapse === true) {
26024             nodeRange.collapse(true);
26025         }
26026         //
26027         var s = this.win.getSelection();
26028         s.removeAllRanges();
26029         s.addRange(nodeRange);
26030     },
26031     
26032     getSelectedNode: function() 
26033     {
26034         // this may only work on Gecko!!!
26035         
26036         // should we cache this!!!!
26037         
26038          
26039          
26040         var range = this.createRange(this.getSelection()).cloneRange();
26041         
26042         if (Roo.isIE) {
26043             var parent = range.parentElement();
26044             while (true) {
26045                 var testRange = range.duplicate();
26046                 testRange.moveToElementText(parent);
26047                 if (testRange.inRange(range)) {
26048                     break;
26049                 }
26050                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26051                     break;
26052                 }
26053                 parent = parent.parentElement;
26054             }
26055             return parent;
26056         }
26057         
26058         // is ancestor a text element.
26059         var ac =  range.commonAncestorContainer;
26060         if (ac.nodeType == 3) {
26061             ac = ac.parentNode;
26062         }
26063         
26064         var ar = ac.childNodes;
26065          
26066         var nodes = [];
26067         var other_nodes = [];
26068         var has_other_nodes = false;
26069         for (var i=0;i<ar.length;i++) {
26070             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26071                 continue;
26072             }
26073             // fullly contained node.
26074             
26075             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26076                 nodes.push(ar[i]);
26077                 continue;
26078             }
26079             
26080             // probably selected..
26081             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26082                 other_nodes.push(ar[i]);
26083                 continue;
26084             }
26085             // outer..
26086             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26087                 continue;
26088             }
26089             
26090             
26091             has_other_nodes = true;
26092         }
26093         if (!nodes.length && other_nodes.length) {
26094             nodes= other_nodes;
26095         }
26096         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26097             return false;
26098         }
26099         
26100         return nodes[0];
26101     },
26102     
26103     
26104     createRange: function(sel)
26105     {
26106         // this has strange effects when using with 
26107         // top toolbar - not sure if it's a great idea.
26108         //this.editor.contentWindow.focus();
26109         if (typeof sel != "undefined") {
26110             try {
26111                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26112             } catch(e) {
26113                 return this.doc.createRange();
26114             }
26115         } else {
26116             return this.doc.createRange();
26117         }
26118     },
26119     getParentElement: function()
26120     {
26121         
26122         this.assignDocWin();
26123         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26124         
26125         var range = this.createRange(sel);
26126          
26127         try {
26128             var p = range.commonAncestorContainer;
26129             while (p.nodeType == 3) { // text node
26130                 p = p.parentNode;
26131             }
26132             return p;
26133         } catch (e) {
26134             return null;
26135         }
26136     
26137     },
26138     /***
26139      *
26140      * Range intersection.. the hard stuff...
26141      *  '-1' = before
26142      *  '0' = hits..
26143      *  '1' = after.
26144      *         [ -- selected range --- ]
26145      *   [fail]                        [fail]
26146      *
26147      *    basically..
26148      *      if end is before start or  hits it. fail.
26149      *      if start is after end or hits it fail.
26150      *
26151      *   if either hits (but other is outside. - then it's not 
26152      *   
26153      *    
26154      **/
26155     
26156     
26157     // @see http://www.thismuchiknow.co.uk/?p=64.
26158     rangeIntersectsNode : function(range, node)
26159     {
26160         var nodeRange = node.ownerDocument.createRange();
26161         try {
26162             nodeRange.selectNode(node);
26163         } catch (e) {
26164             nodeRange.selectNodeContents(node);
26165         }
26166     
26167         var rangeStartRange = range.cloneRange();
26168         rangeStartRange.collapse(true);
26169     
26170         var rangeEndRange = range.cloneRange();
26171         rangeEndRange.collapse(false);
26172     
26173         var nodeStartRange = nodeRange.cloneRange();
26174         nodeStartRange.collapse(true);
26175     
26176         var nodeEndRange = nodeRange.cloneRange();
26177         nodeEndRange.collapse(false);
26178     
26179         return rangeStartRange.compareBoundaryPoints(
26180                  Range.START_TO_START, nodeEndRange) == -1 &&
26181                rangeEndRange.compareBoundaryPoints(
26182                  Range.START_TO_START, nodeStartRange) == 1;
26183         
26184          
26185     },
26186     rangeCompareNode : function(range, node)
26187     {
26188         var nodeRange = node.ownerDocument.createRange();
26189         try {
26190             nodeRange.selectNode(node);
26191         } catch (e) {
26192             nodeRange.selectNodeContents(node);
26193         }
26194         
26195         
26196         range.collapse(true);
26197     
26198         nodeRange.collapse(true);
26199      
26200         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26201         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26202          
26203         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26204         
26205         var nodeIsBefore   =  ss == 1;
26206         var nodeIsAfter    = ee == -1;
26207         
26208         if (nodeIsBefore && nodeIsAfter) {
26209             return 0; // outer
26210         }
26211         if (!nodeIsBefore && nodeIsAfter) {
26212             return 1; //right trailed.
26213         }
26214         
26215         if (nodeIsBefore && !nodeIsAfter) {
26216             return 2;  // left trailed.
26217         }
26218         // fully contined.
26219         return 3;
26220     },
26221  
26222     cleanWordChars : function(input) {// change the chars to hex code
26223         
26224        var swapCodes  = [ 
26225             [    8211, "&#8211;" ], 
26226             [    8212, "&#8212;" ], 
26227             [    8216,  "'" ],  
26228             [    8217, "'" ],  
26229             [    8220, '"' ],  
26230             [    8221, '"' ],  
26231             [    8226, "*" ],  
26232             [    8230, "..." ]
26233         ]; 
26234         var output = input;
26235         Roo.each(swapCodes, function(sw) { 
26236             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26237             
26238             output = output.replace(swapper, sw[1]);
26239         });
26240         
26241         return output;
26242     },
26243     
26244      
26245     
26246         
26247     
26248     cleanUpChild : function (node)
26249     {
26250         
26251         new Roo.htmleditor.FilterComment({node : node});
26252         new Roo.htmleditor.FilterAttributes({
26253                 node : node,
26254                 attrib_black : this.ablack,
26255                 attrib_clean : this.aclean,
26256                 style_white : this.cwhite,
26257                 style_black : this.cblack
26258         });
26259         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26260         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26261          
26262         
26263     },
26264     
26265     /**
26266      * Clean up MS wordisms...
26267      * @deprecated - use filter directly
26268      */
26269     cleanWord : function(node)
26270     {
26271         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26272         
26273     },
26274    
26275     
26276     /**
26277
26278      * @deprecated - use filters
26279      */
26280     cleanTableWidths : function(node)
26281     {
26282         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26283         
26284  
26285     },
26286     
26287      
26288         
26289     applyBlacklists : function()
26290     {
26291         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26292         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26293         
26294         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26295         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26296         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26297         
26298         this.white = [];
26299         this.black = [];
26300         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26301             if (b.indexOf(tag) > -1) {
26302                 return;
26303             }
26304             this.white.push(tag);
26305             
26306         }, this);
26307         
26308         Roo.each(w, function(tag) {
26309             if (b.indexOf(tag) > -1) {
26310                 return;
26311             }
26312             if (this.white.indexOf(tag) > -1) {
26313                 return;
26314             }
26315             this.white.push(tag);
26316             
26317         }, this);
26318         
26319         
26320         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26321             if (w.indexOf(tag) > -1) {
26322                 return;
26323             }
26324             this.black.push(tag);
26325             
26326         }, this);
26327         
26328         Roo.each(b, function(tag) {
26329             if (w.indexOf(tag) > -1) {
26330                 return;
26331             }
26332             if (this.black.indexOf(tag) > -1) {
26333                 return;
26334             }
26335             this.black.push(tag);
26336             
26337         }, this);
26338         
26339         
26340         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26341         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26342         
26343         this.cwhite = [];
26344         this.cblack = [];
26345         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26346             if (b.indexOf(tag) > -1) {
26347                 return;
26348             }
26349             this.cwhite.push(tag);
26350             
26351         }, this);
26352         
26353         Roo.each(w, function(tag) {
26354             if (b.indexOf(tag) > -1) {
26355                 return;
26356             }
26357             if (this.cwhite.indexOf(tag) > -1) {
26358                 return;
26359             }
26360             this.cwhite.push(tag);
26361             
26362         }, this);
26363         
26364         
26365         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26366             if (w.indexOf(tag) > -1) {
26367                 return;
26368             }
26369             this.cblack.push(tag);
26370             
26371         }, this);
26372         
26373         Roo.each(b, function(tag) {
26374             if (w.indexOf(tag) > -1) {
26375                 return;
26376             }
26377             if (this.cblack.indexOf(tag) > -1) {
26378                 return;
26379             }
26380             this.cblack.push(tag);
26381             
26382         }, this);
26383     },
26384     
26385     setStylesheets : function(stylesheets)
26386     {
26387         if(typeof(stylesheets) == 'string'){
26388             Roo.get(this.iframe.contentDocument.head).createChild({
26389                 tag : 'link',
26390                 rel : 'stylesheet',
26391                 type : 'text/css',
26392                 href : stylesheets
26393             });
26394             
26395             return;
26396         }
26397         var _this = this;
26398      
26399         Roo.each(stylesheets, function(s) {
26400             if(!s.length){
26401                 return;
26402             }
26403             
26404             Roo.get(_this.iframe.contentDocument.head).createChild({
26405                 tag : 'link',
26406                 rel : 'stylesheet',
26407                 type : 'text/css',
26408                 href : s
26409             });
26410         });
26411
26412         
26413     },
26414     
26415     
26416     updateLanguage : function()
26417     {
26418         if (!this.iframe || !this.iframe.contentDocument) {
26419             return;
26420         }
26421         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26422     },
26423     
26424     
26425     removeStylesheets : function()
26426     {
26427         var _this = this;
26428         
26429         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26430             s.remove();
26431         });
26432     },
26433     
26434     setStyle : function(style)
26435     {
26436         Roo.get(this.iframe.contentDocument.head).createChild({
26437             tag : 'style',
26438             type : 'text/css',
26439             html : style
26440         });
26441
26442         return;
26443     }
26444     
26445     // hide stuff that is not compatible
26446     /**
26447      * @event blur
26448      * @hide
26449      */
26450     /**
26451      * @event change
26452      * @hide
26453      */
26454     /**
26455      * @event focus
26456      * @hide
26457      */
26458     /**
26459      * @event specialkey
26460      * @hide
26461      */
26462     /**
26463      * @cfg {String} fieldClass @hide
26464      */
26465     /**
26466      * @cfg {String} focusClass @hide
26467      */
26468     /**
26469      * @cfg {String} autoCreate @hide
26470      */
26471     /**
26472      * @cfg {String} inputType @hide
26473      */
26474     /**
26475      * @cfg {String} invalidClass @hide
26476      */
26477     /**
26478      * @cfg {String} invalidText @hide
26479      */
26480     /**
26481      * @cfg {String} msgFx @hide
26482      */
26483     /**
26484      * @cfg {String} validateOnBlur @hide
26485      */
26486 });
26487
26488 Roo.HtmlEditorCore.white = [
26489         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26490         
26491        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26492        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26493        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26494        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26495        'TABLE',   'UL',         'XMP', 
26496        
26497        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26498       'THEAD',   'TR', 
26499      
26500       'DIR', 'MENU', 'OL', 'UL', 'DL',
26501        
26502       'EMBED',  'OBJECT'
26503 ];
26504
26505
26506 Roo.HtmlEditorCore.black = [
26507     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26508         'APPLET', // 
26509         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26510         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26511         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26512         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26513         //'FONT' // CLEAN LATER..
26514         'COLGROUP', 'COL'   // messy tables.
26515         
26516         
26517 ];
26518 Roo.HtmlEditorCore.clean = [ // ?? needed???
26519      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26520 ];
26521 Roo.HtmlEditorCore.tag_remove = [
26522     'FONT', 'TBODY'  
26523 ];
26524 // attributes..
26525
26526 Roo.HtmlEditorCore.ablack = [
26527     'on'
26528 ];
26529     
26530 Roo.HtmlEditorCore.aclean = [ 
26531     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26532 ];
26533
26534 // protocols..
26535 Roo.HtmlEditorCore.pwhite= [
26536         'http',  'https',  'mailto'
26537 ];
26538
26539 // white listed style attributes.
26540 Roo.HtmlEditorCore.cwhite= [
26541       //  'text-align', /// default is to allow most things..
26542       
26543          
26544 //        'font-size'//??
26545 ];
26546
26547 // black listed style attributes.
26548 Roo.HtmlEditorCore.cblack= [
26549       //  'font-size' -- this can be set by the project 
26550 ];
26551
26552
26553
26554
26555     //<script type="text/javascript">
26556
26557 /*
26558  * Ext JS Library 1.1.1
26559  * Copyright(c) 2006-2007, Ext JS, LLC.
26560  * Licence LGPL
26561  * 
26562  */
26563  
26564  
26565 Roo.form.HtmlEditor = function(config){
26566     
26567     
26568     
26569     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26570     
26571     if (!this.toolbars) {
26572         this.toolbars = [];
26573     }
26574     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26575     
26576     
26577 };
26578
26579 /**
26580  * @class Roo.form.HtmlEditor
26581  * @extends Roo.form.Field
26582  * Provides a lightweight HTML Editor component.
26583  *
26584  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26585  * 
26586  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26587  * supported by this editor.</b><br/><br/>
26588  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26589  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26590  */
26591 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26592     /**
26593      * @cfg {Boolean} clearUp
26594      */
26595     clearUp : true,
26596       /**
26597      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26598      */
26599     toolbars : false,
26600    
26601      /**
26602      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26603      *                        Roo.resizable.
26604      */
26605     resizable : false,
26606      /**
26607      * @cfg {Number} height (in pixels)
26608      */   
26609     height: 300,
26610    /**
26611      * @cfg {Number} width (in pixels)
26612      */   
26613     width: 500,
26614     
26615     /**
26616      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26617      * 
26618      */
26619     stylesheets: false,
26620     
26621     
26622      /**
26623      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26624      * 
26625      */
26626     cblack: false,
26627     /**
26628      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26629      * 
26630      */
26631     cwhite: false,
26632     
26633      /**
26634      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26635      * 
26636      */
26637     black: false,
26638     /**
26639      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26640      * 
26641      */
26642     white: false,
26643     /**
26644      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26645      */
26646     allowComments: false,
26647     /**
26648      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26649      */
26650     enableBlocks : true,
26651     
26652     /**
26653      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26654      *         if you are doing an email editor, this probably needs disabling, it's designed
26655      */
26656     autoClean: true,
26657     /**
26658      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
26659      */
26660     bodyCls : '',
26661     /**
26662      * @cfg {String} language default en - language of text (usefull for rtl languages)
26663      * 
26664      */
26665     language: 'en',
26666     
26667      
26668     // id of frame..
26669     frameId: false,
26670     
26671     // private properties
26672     validationEvent : false,
26673     deferHeight: true,
26674     initialized : false,
26675     activated : false,
26676     
26677     onFocus : Roo.emptyFn,
26678     iframePad:3,
26679     hideMode:'offsets',
26680     
26681     actionMode : 'container', // defaults to hiding it...
26682     
26683     defaultAutoCreate : { // modified by initCompnoent..
26684         tag: "textarea",
26685         style:"width:500px;height:300px;",
26686         autocomplete: "new-password"
26687     },
26688
26689     // private
26690     initComponent : function(){
26691         this.addEvents({
26692             /**
26693              * @event initialize
26694              * Fires when the editor is fully initialized (including the iframe)
26695              * @param {HtmlEditor} this
26696              */
26697             initialize: true,
26698             /**
26699              * @event activate
26700              * Fires when the editor is first receives the focus. Any insertion must wait
26701              * until after this event.
26702              * @param {HtmlEditor} this
26703              */
26704             activate: true,
26705              /**
26706              * @event beforesync
26707              * Fires before the textarea is updated with content from the editor iframe. Return false
26708              * to cancel the sync.
26709              * @param {HtmlEditor} this
26710              * @param {String} html
26711              */
26712             beforesync: true,
26713              /**
26714              * @event beforepush
26715              * Fires before the iframe editor is updated with content from the textarea. Return false
26716              * to cancel the push.
26717              * @param {HtmlEditor} this
26718              * @param {String} html
26719              */
26720             beforepush: true,
26721              /**
26722              * @event sync
26723              * Fires when the textarea is updated with content from the editor iframe.
26724              * @param {HtmlEditor} this
26725              * @param {String} html
26726              */
26727             sync: true,
26728              /**
26729              * @event push
26730              * Fires when the iframe editor is updated with content from the textarea.
26731              * @param {HtmlEditor} this
26732              * @param {String} html
26733              */
26734             push: true,
26735              /**
26736              * @event editmodechange
26737              * Fires when the editor switches edit modes
26738              * @param {HtmlEditor} this
26739              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26740              */
26741             editmodechange: true,
26742             /**
26743              * @event editorevent
26744              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26745              * @param {HtmlEditor} this
26746              */
26747             editorevent: true,
26748             /**
26749              * @event firstfocus
26750              * Fires when on first focus - needed by toolbars..
26751              * @param {HtmlEditor} this
26752              */
26753             firstfocus: true,
26754             /**
26755              * @event autosave
26756              * Auto save the htmlEditor value as a file into Events
26757              * @param {HtmlEditor} this
26758              */
26759             autosave: true,
26760             /**
26761              * @event savedpreview
26762              * preview the saved version of htmlEditor
26763              * @param {HtmlEditor} this
26764              */
26765             savedpreview: true,
26766             
26767             /**
26768             * @event stylesheetsclick
26769             * Fires when press the Sytlesheets button
26770             * @param {Roo.HtmlEditorCore} this
26771             */
26772             stylesheetsclick: true,
26773             /**
26774             * @event paste
26775             * Fires when press user pastes into the editor
26776             * @param {Roo.HtmlEditorCore} this
26777             */
26778             paste: true 
26779         });
26780         this.defaultAutoCreate =  {
26781             tag: "textarea",
26782             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26783             autocomplete: "new-password"
26784         };
26785     },
26786
26787     /**
26788      * Protected method that will not generally be called directly. It
26789      * is called when the editor creates its toolbar. Override this method if you need to
26790      * add custom toolbar buttons.
26791      * @param {HtmlEditor} editor
26792      */
26793     createToolbar : function(editor){
26794         Roo.log("create toolbars");
26795         if (!editor.toolbars || !editor.toolbars.length) {
26796             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26797         }
26798         
26799         for (var i =0 ; i < editor.toolbars.length;i++) {
26800             editor.toolbars[i] = Roo.factory(
26801                     typeof(editor.toolbars[i]) == 'string' ?
26802                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26803                 Roo.form.HtmlEditor);
26804             editor.toolbars[i].init(editor);
26805         }
26806          
26807         
26808     },
26809     /**
26810      * get the Context selected node
26811      * @returns {DomElement|boolean} selected node if active or false if none
26812      * 
26813      */
26814     getSelectedNode : function()
26815     {
26816         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26817             return false;
26818         }
26819         return this.toolbars[1].tb.selectedNode;
26820     
26821     },
26822     // private
26823     onRender : function(ct, position)
26824     {
26825         var _t = this;
26826         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26827         
26828         this.wrap = this.el.wrap({
26829             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26830         });
26831         
26832         this.editorcore.onRender(ct, position);
26833          
26834         if (this.resizable) {
26835             this.resizeEl = new Roo.Resizable(this.wrap, {
26836                 pinned : true,
26837                 wrap: true,
26838                 dynamic : true,
26839                 minHeight : this.height,
26840                 height: this.height,
26841                 handles : this.resizable,
26842                 width: this.width,
26843                 listeners : {
26844                     resize : function(r, w, h) {
26845                         _t.onResize(w,h); // -something
26846                     }
26847                 }
26848             });
26849             
26850         }
26851         this.createToolbar(this);
26852        
26853         
26854         if(!this.width){
26855             this.setSize(this.wrap.getSize());
26856         }
26857         if (this.resizeEl) {
26858             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26859             // should trigger onReize..
26860         }
26861         
26862         this.keyNav = new Roo.KeyNav(this.el, {
26863             
26864             "tab" : function(e){
26865                 e.preventDefault();
26866                 
26867                 var value = this.getValue();
26868                 
26869                 var start = this.el.dom.selectionStart;
26870                 var end = this.el.dom.selectionEnd;
26871                 
26872                 if(!e.shiftKey){
26873                     
26874                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26875                     this.el.dom.setSelectionRange(end + 1, end + 1);
26876                     return;
26877                 }
26878                 
26879                 var f = value.substring(0, start).split("\t");
26880                 
26881                 if(f.pop().length != 0){
26882                     return;
26883                 }
26884                 
26885                 this.setValue(f.join("\t") + value.substring(end));
26886                 this.el.dom.setSelectionRange(start - 1, start - 1);
26887                 
26888             },
26889             
26890             "home" : function(e){
26891                 e.preventDefault();
26892                 
26893                 var curr = this.el.dom.selectionStart;
26894                 var lines = this.getValue().split("\n");
26895                 
26896                 if(!lines.length){
26897                     return;
26898                 }
26899                 
26900                 if(e.ctrlKey){
26901                     this.el.dom.setSelectionRange(0, 0);
26902                     return;
26903                 }
26904                 
26905                 var pos = 0;
26906                 
26907                 for (var i = 0; i < lines.length;i++) {
26908                     pos += lines[i].length;
26909                     
26910                     if(i != 0){
26911                         pos += 1;
26912                     }
26913                     
26914                     if(pos < curr){
26915                         continue;
26916                     }
26917                     
26918                     pos -= lines[i].length;
26919                     
26920                     break;
26921                 }
26922                 
26923                 if(!e.shiftKey){
26924                     this.el.dom.setSelectionRange(pos, pos);
26925                     return;
26926                 }
26927                 
26928                 this.el.dom.selectionStart = pos;
26929                 this.el.dom.selectionEnd = curr;
26930             },
26931             
26932             "end" : function(e){
26933                 e.preventDefault();
26934                 
26935                 var curr = this.el.dom.selectionStart;
26936                 var lines = this.getValue().split("\n");
26937                 
26938                 if(!lines.length){
26939                     return;
26940                 }
26941                 
26942                 if(e.ctrlKey){
26943                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26944                     return;
26945                 }
26946                 
26947                 var pos = 0;
26948                 
26949                 for (var i = 0; i < lines.length;i++) {
26950                     
26951                     pos += lines[i].length;
26952                     
26953                     if(i != 0){
26954                         pos += 1;
26955                     }
26956                     
26957                     if(pos < curr){
26958                         continue;
26959                     }
26960                     
26961                     break;
26962                 }
26963                 
26964                 if(!e.shiftKey){
26965                     this.el.dom.setSelectionRange(pos, pos);
26966                     return;
26967                 }
26968                 
26969                 this.el.dom.selectionStart = curr;
26970                 this.el.dom.selectionEnd = pos;
26971             },
26972
26973             scope : this,
26974
26975             doRelay : function(foo, bar, hname){
26976                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26977             },
26978
26979             forceKeyDown: true
26980         });
26981         
26982 //        if(this.autosave && this.w){
26983 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26984 //        }
26985     },
26986
26987     // private
26988     onResize : function(w, h)
26989     {
26990         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26991         var ew = false;
26992         var eh = false;
26993         
26994         if(this.el ){
26995             if(typeof w == 'number'){
26996                 var aw = w - this.wrap.getFrameWidth('lr');
26997                 this.el.setWidth(this.adjustWidth('textarea', aw));
26998                 ew = aw;
26999             }
27000             if(typeof h == 'number'){
27001                 var tbh = 0;
27002                 for (var i =0; i < this.toolbars.length;i++) {
27003                     // fixme - ask toolbars for heights?
27004                     tbh += this.toolbars[i].tb.el.getHeight();
27005                     if (this.toolbars[i].footer) {
27006                         tbh += this.toolbars[i].footer.el.getHeight();
27007                     }
27008                 }
27009                 
27010                 
27011                 
27012                 
27013                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27014                 ah -= 5; // knock a few pixes off for look..
27015 //                Roo.log(ah);
27016                 this.el.setHeight(this.adjustWidth('textarea', ah));
27017                 var eh = ah;
27018             }
27019         }
27020         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27021         this.editorcore.onResize(ew,eh);
27022         
27023     },
27024
27025     /**
27026      * Toggles the editor between standard and source edit mode.
27027      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27028      */
27029     toggleSourceEdit : function(sourceEditMode)
27030     {
27031         this.editorcore.toggleSourceEdit(sourceEditMode);
27032         
27033         if(this.editorcore.sourceEditMode){
27034             Roo.log('editor - showing textarea');
27035             
27036 //            Roo.log('in');
27037 //            Roo.log(this.syncValue());
27038             this.editorcore.syncValue();
27039             this.el.removeClass('x-hidden');
27040             this.el.dom.removeAttribute('tabIndex');
27041             this.el.focus();
27042             this.el.dom.scrollTop = 0;
27043             
27044             
27045             for (var i = 0; i < this.toolbars.length; i++) {
27046                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27047                     this.toolbars[i].tb.hide();
27048                     this.toolbars[i].footer.hide();
27049                 }
27050             }
27051             
27052         }else{
27053             Roo.log('editor - hiding textarea');
27054 //            Roo.log('out')
27055 //            Roo.log(this.pushValue()); 
27056             this.editorcore.pushValue();
27057             
27058             this.el.addClass('x-hidden');
27059             this.el.dom.setAttribute('tabIndex', -1);
27060             
27061             for (var i = 0; i < this.toolbars.length; i++) {
27062                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27063                     this.toolbars[i].tb.show();
27064                     this.toolbars[i].footer.show();
27065                 }
27066             }
27067             
27068             //this.deferFocus();
27069         }
27070         
27071         this.setSize(this.wrap.getSize());
27072         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27073         
27074         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27075     },
27076  
27077     // private (for BoxComponent)
27078     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27079
27080     // private (for BoxComponent)
27081     getResizeEl : function(){
27082         return this.wrap;
27083     },
27084
27085     // private (for BoxComponent)
27086     getPositionEl : function(){
27087         return this.wrap;
27088     },
27089
27090     // private
27091     initEvents : function(){
27092         this.originalValue = this.getValue();
27093     },
27094
27095     /**
27096      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27097      * @method
27098      */
27099     markInvalid : Roo.emptyFn,
27100     /**
27101      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27102      * @method
27103      */
27104     clearInvalid : Roo.emptyFn,
27105
27106     setValue : function(v){
27107         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27108         this.editorcore.pushValue();
27109     },
27110
27111     /**
27112      * update the language in the body - really done by core
27113      * @param {String} language - eg. en / ar / zh-CN etc..
27114      */
27115     updateLanguage : function(lang)
27116     {
27117         this.language = lang;
27118         this.editorcore.language = lang;
27119         this.editorcore.updateLanguage();
27120      
27121     },
27122     // private
27123     deferFocus : function(){
27124         this.focus.defer(10, this);
27125     },
27126
27127     // doc'ed in Field
27128     focus : function(){
27129         this.editorcore.focus();
27130         
27131     },
27132       
27133
27134     // private
27135     onDestroy : function(){
27136         
27137         
27138         
27139         if(this.rendered){
27140             
27141             for (var i =0; i < this.toolbars.length;i++) {
27142                 // fixme - ask toolbars for heights?
27143                 this.toolbars[i].onDestroy();
27144             }
27145             
27146             this.wrap.dom.innerHTML = '';
27147             this.wrap.remove();
27148         }
27149     },
27150
27151     // private
27152     onFirstFocus : function(){
27153         //Roo.log("onFirstFocus");
27154         this.editorcore.onFirstFocus();
27155          for (var i =0; i < this.toolbars.length;i++) {
27156             this.toolbars[i].onFirstFocus();
27157         }
27158         
27159     },
27160     
27161     // private
27162     syncValue : function()
27163     {
27164         this.editorcore.syncValue();
27165     },
27166     
27167     pushValue : function()
27168     {
27169         this.editorcore.pushValue();
27170     },
27171     
27172     setStylesheets : function(stylesheets)
27173     {
27174         this.editorcore.setStylesheets(stylesheets);
27175     },
27176     
27177     removeStylesheets : function()
27178     {
27179         this.editorcore.removeStylesheets();
27180     }
27181      
27182     
27183     // hide stuff that is not compatible
27184     /**
27185      * @event blur
27186      * @hide
27187      */
27188     /**
27189      * @event change
27190      * @hide
27191      */
27192     /**
27193      * @event focus
27194      * @hide
27195      */
27196     /**
27197      * @event specialkey
27198      * @hide
27199      */
27200     /**
27201      * @cfg {String} fieldClass @hide
27202      */
27203     /**
27204      * @cfg {String} focusClass @hide
27205      */
27206     /**
27207      * @cfg {String} autoCreate @hide
27208      */
27209     /**
27210      * @cfg {String} inputType @hide
27211      */
27212     /**
27213      * @cfg {String} invalidClass @hide
27214      */
27215     /**
27216      * @cfg {String} invalidText @hide
27217      */
27218     /**
27219      * @cfg {String} msgFx @hide
27220      */
27221     /**
27222      * @cfg {String} validateOnBlur @hide
27223      */
27224 });
27225  
27226     /*
27227  * Based on
27228  * Ext JS Library 1.1.1
27229  * Copyright(c) 2006-2007, Ext JS, LLC.
27230  *  
27231  
27232  */
27233
27234 /**
27235  * @class Roo.form.HtmlEditor.ToolbarStandard
27236  * Basic Toolbar
27237
27238  * Usage:
27239  *
27240  new Roo.form.HtmlEditor({
27241     ....
27242     toolbars : [
27243         new Roo.form.HtmlEditorToolbar1({
27244             disable : { fonts: 1 , format: 1, ..., ... , ...],
27245             btns : [ .... ]
27246         })
27247     }
27248      
27249  * 
27250  * @cfg {Object} disable List of elements to disable..
27251  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27252  * 
27253  * 
27254  * NEEDS Extra CSS? 
27255  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27256  */
27257  
27258 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27259 {
27260     
27261     Roo.apply(this, config);
27262     
27263     // default disabled, based on 'good practice'..
27264     this.disable = this.disable || {};
27265     Roo.applyIf(this.disable, {
27266         fontSize : true,
27267         colors : true,
27268         specialElements : true
27269     });
27270     
27271     
27272     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27273     // dont call parent... till later.
27274 }
27275
27276 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27277     
27278     tb: false,
27279     
27280     rendered: false,
27281     
27282     editor : false,
27283     editorcore : false,
27284     /**
27285      * @cfg {Object} disable  List of toolbar elements to disable
27286          
27287      */
27288     disable : false,
27289     
27290     
27291      /**
27292      * @cfg {String} createLinkText The default text for the create link prompt
27293      */
27294     createLinkText : 'Please enter the URL for the link:',
27295     /**
27296      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27297      */
27298     defaultLinkValue : 'http:/'+'/',
27299    
27300     
27301       /**
27302      * @cfg {Array} fontFamilies An array of available font families
27303      */
27304     fontFamilies : [
27305         'Arial',
27306         'Courier New',
27307         'Tahoma',
27308         'Times New Roman',
27309         'Verdana'
27310     ],
27311     
27312     specialChars : [
27313            "&#169;",
27314           "&#174;",     
27315           "&#8482;",    
27316           "&#163;" ,    
27317          // "&#8212;",    
27318           "&#8230;",    
27319           "&#247;" ,    
27320         //  "&#225;" ,     ?? a acute?
27321            "&#8364;"    , //Euro
27322        //   "&#8220;"    ,
27323         //  "&#8221;"    ,
27324         //  "&#8226;"    ,
27325           "&#176;"  //   , // degrees
27326
27327          // "&#233;"     , // e ecute
27328          // "&#250;"     , // u ecute?
27329     ],
27330     
27331     specialElements : [
27332         {
27333             text: "Insert Table",
27334             xtype: 'MenuItem',
27335             xns : Roo.Menu,
27336             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27337                 
27338         },
27339         {    
27340             text: "Insert Image",
27341             xtype: 'MenuItem',
27342             xns : Roo.Menu,
27343             ihtml : '<img src="about:blank"/>'
27344             
27345         }
27346         
27347          
27348     ],
27349     
27350     
27351     inputElements : [ 
27352             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27353             "input:submit", "input:button", "select", "textarea", "label" ],
27354     formats : [
27355         ["p"] ,  
27356         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27357         ["pre"],[ "code"], 
27358         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27359         ['div'],['span'],
27360         ['sup'],['sub']
27361     ],
27362     
27363     cleanStyles : [
27364         "font-size"
27365     ],
27366      /**
27367      * @cfg {String} defaultFont default font to use.
27368      */
27369     defaultFont: 'tahoma',
27370    
27371     fontSelect : false,
27372     
27373     
27374     formatCombo : false,
27375     
27376     init : function(editor)
27377     {
27378         this.editor = editor;
27379         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27380         var editorcore = this.editorcore;
27381         
27382         var _t = this;
27383         
27384         var fid = editorcore.frameId;
27385         var etb = this;
27386         function btn(id, toggle, handler){
27387             var xid = fid + '-'+ id ;
27388             return {
27389                 id : xid,
27390                 cmd : id,
27391                 cls : 'x-btn-icon x-edit-'+id,
27392                 enableToggle:toggle !== false,
27393                 scope: _t, // was editor...
27394                 handler:handler||_t.relayBtnCmd,
27395                 clickEvent:'mousedown',
27396                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27397                 tabIndex:-1
27398             };
27399         }
27400         
27401         
27402         
27403         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27404         this.tb = tb;
27405          // stop form submits
27406         tb.el.on('click', function(e){
27407             e.preventDefault(); // what does this do?
27408         });
27409
27410         if(!this.disable.font) { // && !Roo.isSafari){
27411             /* why no safari for fonts 
27412             editor.fontSelect = tb.el.createChild({
27413                 tag:'select',
27414                 tabIndex: -1,
27415                 cls:'x-font-select',
27416                 html: this.createFontOptions()
27417             });
27418             
27419             editor.fontSelect.on('change', function(){
27420                 var font = editor.fontSelect.dom.value;
27421                 editor.relayCmd('fontname', font);
27422                 editor.deferFocus();
27423             }, editor);
27424             
27425             tb.add(
27426                 editor.fontSelect.dom,
27427                 '-'
27428             );
27429             */
27430             
27431         };
27432         if(!this.disable.formats){
27433             this.formatCombo = new Roo.form.ComboBox({
27434                 store: new Roo.data.SimpleStore({
27435                     id : 'tag',
27436                     fields: ['tag'],
27437                     data : this.formats // from states.js
27438                 }),
27439                 blockFocus : true,
27440                 name : '',
27441                 //autoCreate : {tag: "div",  size: "20"},
27442                 displayField:'tag',
27443                 typeAhead: false,
27444                 mode: 'local',
27445                 editable : false,
27446                 triggerAction: 'all',
27447                 emptyText:'Add tag',
27448                 selectOnFocus:true,
27449                 width:135,
27450                 listeners : {
27451                     'select': function(c, r, i) {
27452                         editorcore.insertTag(r.get('tag'));
27453                         editor.focus();
27454                     }
27455                 }
27456
27457             });
27458             tb.addField(this.formatCombo);
27459             
27460         }
27461         
27462         if(!this.disable.format){
27463             tb.add(
27464                 btn('bold'),
27465                 btn('italic'),
27466                 btn('underline'),
27467                 btn('strikethrough')
27468             );
27469         };
27470         if(!this.disable.fontSize){
27471             tb.add(
27472                 '-',
27473                 
27474                 
27475                 btn('increasefontsize', false, editorcore.adjustFont),
27476                 btn('decreasefontsize', false, editorcore.adjustFont)
27477             );
27478         };
27479         
27480         
27481         if(!this.disable.colors){
27482             tb.add(
27483                 '-', {
27484                     id:editorcore.frameId +'-forecolor',
27485                     cls:'x-btn-icon x-edit-forecolor',
27486                     clickEvent:'mousedown',
27487                     tooltip: this.buttonTips['forecolor'] || undefined,
27488                     tabIndex:-1,
27489                     menu : new Roo.menu.ColorMenu({
27490                         allowReselect: true,
27491                         focus: Roo.emptyFn,
27492                         value:'000000',
27493                         plain:true,
27494                         selectHandler: function(cp, color){
27495                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27496                             editor.deferFocus();
27497                         },
27498                         scope: editorcore,
27499                         clickEvent:'mousedown'
27500                     })
27501                 }, {
27502                     id:editorcore.frameId +'backcolor',
27503                     cls:'x-btn-icon x-edit-backcolor',
27504                     clickEvent:'mousedown',
27505                     tooltip: this.buttonTips['backcolor'] || undefined,
27506                     tabIndex:-1,
27507                     menu : new Roo.menu.ColorMenu({
27508                         focus: Roo.emptyFn,
27509                         value:'FFFFFF',
27510                         plain:true,
27511                         allowReselect: true,
27512                         selectHandler: function(cp, color){
27513                             if(Roo.isGecko){
27514                                 editorcore.execCmd('useCSS', false);
27515                                 editorcore.execCmd('hilitecolor', color);
27516                                 editorcore.execCmd('useCSS', true);
27517                                 editor.deferFocus();
27518                             }else{
27519                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27520                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27521                                 editor.deferFocus();
27522                             }
27523                         },
27524                         scope:editorcore,
27525                         clickEvent:'mousedown'
27526                     })
27527                 }
27528             );
27529         };
27530         // now add all the items...
27531         
27532
27533         if(!this.disable.alignments){
27534             tb.add(
27535                 '-',
27536                 btn('justifyleft'),
27537                 btn('justifycenter'),
27538                 btn('justifyright')
27539             );
27540         };
27541
27542         //if(!Roo.isSafari){
27543             if(!this.disable.links){
27544                 tb.add(
27545                     '-',
27546                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27547                 );
27548             };
27549
27550             if(!this.disable.lists){
27551                 tb.add(
27552                     '-',
27553                     btn('insertorderedlist'),
27554                     btn('insertunorderedlist')
27555                 );
27556             }
27557             if(!this.disable.sourceEdit){
27558                 tb.add(
27559                     '-',
27560                     btn('sourceedit', true, function(btn){
27561                         this.toggleSourceEdit(btn.pressed);
27562                     })
27563                 );
27564             }
27565         //}
27566         
27567         var smenu = { };
27568         // special menu.. - needs to be tidied up..
27569         if (!this.disable.special) {
27570             smenu = {
27571                 text: "&#169;",
27572                 cls: 'x-edit-none',
27573                 
27574                 menu : {
27575                     items : []
27576                 }
27577             };
27578             for (var i =0; i < this.specialChars.length; i++) {
27579                 smenu.menu.items.push({
27580                     
27581                     html: this.specialChars[i],
27582                     handler: function(a,b) {
27583                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27584                         //editor.insertAtCursor(a.html);
27585                         
27586                     },
27587                     tabIndex:-1
27588                 });
27589             }
27590             
27591             
27592             tb.add(smenu);
27593             
27594             
27595         }
27596         
27597         var cmenu = { };
27598         if (!this.disable.cleanStyles) {
27599             cmenu = {
27600                 cls: 'x-btn-icon x-btn-clear',
27601                 
27602                 menu : {
27603                     items : []
27604                 }
27605             };
27606             for (var i =0; i < this.cleanStyles.length; i++) {
27607                 cmenu.menu.items.push({
27608                     actiontype : this.cleanStyles[i],
27609                     html: 'Remove ' + this.cleanStyles[i],
27610                     handler: function(a,b) {
27611 //                        Roo.log(a);
27612 //                        Roo.log(b);
27613                         var c = Roo.get(editorcore.doc.body);
27614                         c.select('[style]').each(function(s) {
27615                             s.dom.style.removeProperty(a.actiontype);
27616                         });
27617                         editorcore.syncValue();
27618                     },
27619                     tabIndex:-1
27620                 });
27621             }
27622             cmenu.menu.items.push({
27623                 actiontype : 'tablewidths',
27624                 html: 'Remove Table Widths',
27625                 handler: function(a,b) {
27626                     editorcore.cleanTableWidths();
27627                     editorcore.syncValue();
27628                 },
27629                 tabIndex:-1
27630             });
27631             cmenu.menu.items.push({
27632                 actiontype : 'word',
27633                 html: 'Remove MS Word Formating',
27634                 handler: function(a,b) {
27635                     editorcore.cleanWord();
27636                     editorcore.syncValue();
27637                 },
27638                 tabIndex:-1
27639             });
27640             
27641             cmenu.menu.items.push({
27642                 actiontype : 'all',
27643                 html: 'Remove All Styles',
27644                 handler: function(a,b) {
27645                     
27646                     var c = Roo.get(editorcore.doc.body);
27647                     c.select('[style]').each(function(s) {
27648                         s.dom.removeAttribute('style');
27649                     });
27650                     editorcore.syncValue();
27651                 },
27652                 tabIndex:-1
27653             });
27654             
27655             cmenu.menu.items.push({
27656                 actiontype : 'all',
27657                 html: 'Remove All CSS Classes',
27658                 handler: function(a,b) {
27659                     
27660                     var c = Roo.get(editorcore.doc.body);
27661                     c.select('[class]').each(function(s) {
27662                         s.dom.removeAttribute('class');
27663                     });
27664                     editorcore.cleanWord();
27665                     editorcore.syncValue();
27666                 },
27667                 tabIndex:-1
27668             });
27669             
27670              cmenu.menu.items.push({
27671                 actiontype : 'tidy',
27672                 html: 'Tidy HTML Source',
27673                 handler: function(a,b) {
27674                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27675                     editorcore.syncValue();
27676                 },
27677                 tabIndex:-1
27678             });
27679             
27680             
27681             tb.add(cmenu);
27682         }
27683          
27684         if (!this.disable.specialElements) {
27685             var semenu = {
27686                 text: "Other;",
27687                 cls: 'x-edit-none',
27688                 menu : {
27689                     items : []
27690                 }
27691             };
27692             for (var i =0; i < this.specialElements.length; i++) {
27693                 semenu.menu.items.push(
27694                     Roo.apply({ 
27695                         handler: function(a,b) {
27696                             editor.insertAtCursor(this.ihtml);
27697                         }
27698                     }, this.specialElements[i])
27699                 );
27700                     
27701             }
27702             
27703             tb.add(semenu);
27704             
27705             
27706         }
27707          
27708         
27709         if (this.btns) {
27710             for(var i =0; i< this.btns.length;i++) {
27711                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27712                 b.cls =  'x-edit-none';
27713                 
27714                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27715                     b.cls += ' x-init-enable';
27716                 }
27717                 
27718                 b.scope = editorcore;
27719                 tb.add(b);
27720             }
27721         
27722         }
27723         
27724         
27725         
27726         // disable everything...
27727         
27728         this.tb.items.each(function(item){
27729             
27730            if(
27731                 item.id != editorcore.frameId+ '-sourceedit' && 
27732                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27733             ){
27734                 
27735                 item.disable();
27736             }
27737         });
27738         this.rendered = true;
27739         
27740         // the all the btns;
27741         editor.on('editorevent', this.updateToolbar, this);
27742         // other toolbars need to implement this..
27743         //editor.on('editmodechange', this.updateToolbar, this);
27744     },
27745     
27746     
27747     relayBtnCmd : function(btn) {
27748         this.editorcore.relayCmd(btn.cmd);
27749     },
27750     // private used internally
27751     createLink : function(){
27752         //Roo.log("create link?");
27753         var ec = this.editorcore;
27754         var ar = ec.getAllAncestors();
27755         var n = false;
27756         for(var i = 0;i< ar.length;i++) {
27757             if (ar[i] && ar[i].nodeName == 'A') {
27758                 n = ar[i];
27759                 break;
27760             }
27761         }
27762         
27763         (function() {
27764             
27765             Roo.MessageBox.show({
27766                 title : "Add / Edit Link URL",
27767                 msg : "Enter the url for the link",
27768                 buttons: Roo.MessageBox.OKCANCEL,
27769                 fn: function(btn, url){
27770                     if (btn != 'ok') {
27771                         return;
27772                     }
27773                     if(url && url != 'http:/'+'/'){
27774                         if (n) {
27775                             n.setAttribute('href', url);
27776                         } else {
27777                             ec.relayCmd('createlink', url);
27778                         }
27779                     }
27780                 },
27781                 minWidth:250,
27782                 prompt:true,
27783                 //multiline: multiline,
27784                 modal : true,
27785                 value :  n  ? n.getAttribute('href') : '' 
27786             });
27787             
27788              
27789         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27790         
27791     },
27792
27793     
27794     /**
27795      * Protected method that will not generally be called directly. It triggers
27796      * a toolbar update by reading the markup state of the current selection in the editor.
27797      */
27798     updateToolbar: function(){
27799
27800         if(!this.editorcore.activated){
27801             this.editor.onFirstFocus();
27802             return;
27803         }
27804
27805         var btns = this.tb.items.map, 
27806             doc = this.editorcore.doc,
27807             frameId = this.editorcore.frameId;
27808
27809         if(!this.disable.font && !Roo.isSafari){
27810             /*
27811             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27812             if(name != this.fontSelect.dom.value){
27813                 this.fontSelect.dom.value = name;
27814             }
27815             */
27816         }
27817         if(!this.disable.format){
27818             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27819             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27820             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27821             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27822         }
27823         if(!this.disable.alignments){
27824             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27825             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27826             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27827         }
27828         if(!Roo.isSafari && !this.disable.lists){
27829             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27830             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27831         }
27832         
27833         var ans = this.editorcore.getAllAncestors();
27834         if (this.formatCombo) {
27835             
27836             
27837             var store = this.formatCombo.store;
27838             this.formatCombo.setValue("");
27839             for (var i =0; i < ans.length;i++) {
27840                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27841                     // select it..
27842                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27843                     break;
27844                 }
27845             }
27846         }
27847         
27848         
27849         
27850         // hides menus... - so this cant be on a menu...
27851         Roo.menu.MenuMgr.hideAll();
27852
27853         //this.editorsyncValue();
27854     },
27855    
27856     
27857     createFontOptions : function(){
27858         var buf = [], fs = this.fontFamilies, ff, lc;
27859         
27860         
27861         
27862         for(var i = 0, len = fs.length; i< len; i++){
27863             ff = fs[i];
27864             lc = ff.toLowerCase();
27865             buf.push(
27866                 '<option value="',lc,'" style="font-family:',ff,';"',
27867                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27868                     ff,
27869                 '</option>'
27870             );
27871         }
27872         return buf.join('');
27873     },
27874     
27875     toggleSourceEdit : function(sourceEditMode){
27876         
27877         Roo.log("toolbar toogle");
27878         if(sourceEditMode === undefined){
27879             sourceEditMode = !this.sourceEditMode;
27880         }
27881         this.sourceEditMode = sourceEditMode === true;
27882         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27883         // just toggle the button?
27884         if(btn.pressed !== this.sourceEditMode){
27885             btn.toggle(this.sourceEditMode);
27886             return;
27887         }
27888         
27889         if(sourceEditMode){
27890             Roo.log("disabling buttons");
27891             this.tb.items.each(function(item){
27892                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27893                     item.disable();
27894                 }
27895             });
27896           
27897         }else{
27898             Roo.log("enabling buttons");
27899             if(this.editorcore.initialized){
27900                 this.tb.items.each(function(item){
27901                     item.enable();
27902                 });
27903                 // initialize 'blocks'
27904                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27905                     Roo.htmleditor.Block.factory(e).updateElement(e);
27906                 },this);
27907             
27908             }
27909             
27910         }
27911         Roo.log("calling toggole on editor");
27912         // tell the editor that it's been pressed..
27913         this.editor.toggleSourceEdit(sourceEditMode);
27914        
27915     },
27916      /**
27917      * Object collection of toolbar tooltips for the buttons in the editor. The key
27918      * is the command id associated with that button and the value is a valid QuickTips object.
27919      * For example:
27920 <pre><code>
27921 {
27922     bold : {
27923         title: 'Bold (Ctrl+B)',
27924         text: 'Make the selected text bold.',
27925         cls: 'x-html-editor-tip'
27926     },
27927     italic : {
27928         title: 'Italic (Ctrl+I)',
27929         text: 'Make the selected text italic.',
27930         cls: 'x-html-editor-tip'
27931     },
27932     ...
27933 </code></pre>
27934     * @type Object
27935      */
27936     buttonTips : {
27937         bold : {
27938             title: 'Bold (Ctrl+B)',
27939             text: 'Make the selected text bold.',
27940             cls: 'x-html-editor-tip'
27941         },
27942         italic : {
27943             title: 'Italic (Ctrl+I)',
27944             text: 'Make the selected text italic.',
27945             cls: 'x-html-editor-tip'
27946         },
27947         underline : {
27948             title: 'Underline (Ctrl+U)',
27949             text: 'Underline the selected text.',
27950             cls: 'x-html-editor-tip'
27951         },
27952         strikethrough : {
27953             title: 'Strikethrough',
27954             text: 'Strikethrough the selected text.',
27955             cls: 'x-html-editor-tip'
27956         },
27957         increasefontsize : {
27958             title: 'Grow Text',
27959             text: 'Increase the font size.',
27960             cls: 'x-html-editor-tip'
27961         },
27962         decreasefontsize : {
27963             title: 'Shrink Text',
27964             text: 'Decrease the font size.',
27965             cls: 'x-html-editor-tip'
27966         },
27967         backcolor : {
27968             title: 'Text Highlight Color',
27969             text: 'Change the background color of the selected text.',
27970             cls: 'x-html-editor-tip'
27971         },
27972         forecolor : {
27973             title: 'Font Color',
27974             text: 'Change the color of the selected text.',
27975             cls: 'x-html-editor-tip'
27976         },
27977         justifyleft : {
27978             title: 'Align Text Left',
27979             text: 'Align text to the left.',
27980             cls: 'x-html-editor-tip'
27981         },
27982         justifycenter : {
27983             title: 'Center Text',
27984             text: 'Center text in the editor.',
27985             cls: 'x-html-editor-tip'
27986         },
27987         justifyright : {
27988             title: 'Align Text Right',
27989             text: 'Align text to the right.',
27990             cls: 'x-html-editor-tip'
27991         },
27992         insertunorderedlist : {
27993             title: 'Bullet List',
27994             text: 'Start a bulleted list.',
27995             cls: 'x-html-editor-tip'
27996         },
27997         insertorderedlist : {
27998             title: 'Numbered List',
27999             text: 'Start a numbered list.',
28000             cls: 'x-html-editor-tip'
28001         },
28002         createlink : {
28003             title: 'Hyperlink',
28004             text: 'Make the selected text a hyperlink.',
28005             cls: 'x-html-editor-tip'
28006         },
28007         sourceedit : {
28008             title: 'Source Edit',
28009             text: 'Switch to source editing mode.',
28010             cls: 'x-html-editor-tip'
28011         }
28012     },
28013     // private
28014     onDestroy : function(){
28015         if(this.rendered){
28016             
28017             this.tb.items.each(function(item){
28018                 if(item.menu){
28019                     item.menu.removeAll();
28020                     if(item.menu.el){
28021                         item.menu.el.destroy();
28022                     }
28023                 }
28024                 item.destroy();
28025             });
28026              
28027         }
28028     },
28029     onFirstFocus: function() {
28030         this.tb.items.each(function(item){
28031            item.enable();
28032         });
28033     }
28034 };
28035
28036
28037
28038
28039 // <script type="text/javascript">
28040 /*
28041  * Based on
28042  * Ext JS Library 1.1.1
28043  * Copyright(c) 2006-2007, Ext JS, LLC.
28044  *  
28045  
28046  */
28047
28048  
28049 /**
28050  * @class Roo.form.HtmlEditor.ToolbarContext
28051  * Context Toolbar
28052  * 
28053  * Usage:
28054  *
28055  new Roo.form.HtmlEditor({
28056     ....
28057     toolbars : [
28058         { xtype: 'ToolbarStandard', styles : {} }
28059         { xtype: 'ToolbarContext', disable : {} }
28060     ]
28061 })
28062
28063      
28064  * 
28065  * @config : {Object} disable List of elements to disable.. (not done yet.)
28066  * @config : {Object} styles  Map of styles available.
28067  * 
28068  */
28069
28070 Roo.form.HtmlEditor.ToolbarContext = function(config)
28071 {
28072     
28073     Roo.apply(this, config);
28074     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28075     // dont call parent... till later.
28076     this.styles = this.styles || {};
28077 }
28078
28079  
28080
28081 Roo.form.HtmlEditor.ToolbarContext.types = {
28082     'IMG' : [
28083         {
28084             name : 'width',
28085             title: "Width",
28086             width: 40
28087         },
28088         {
28089             name : 'height',
28090             title: "Height",
28091             width: 40
28092         },
28093         {
28094             name : 'align',
28095             title: "Align",
28096             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28097             width : 80
28098             
28099         },
28100         {
28101             name : 'border',
28102             title: "Border",
28103             width: 40
28104         },
28105         {
28106             name : 'alt',
28107             title: "Alt",
28108             width: 120
28109         },
28110         {
28111             name : 'src',
28112             title: "Src",
28113             width: 220
28114         }
28115         
28116     ],
28117     
28118     'FIGURE' : [
28119         {
28120             name : 'align',
28121             title: "Align",
28122             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28123             width : 80  
28124         }
28125     ],
28126     'A' : [
28127         {
28128             name : 'name',
28129             title: "Name",
28130             width: 50
28131         },
28132         {
28133             name : 'target',
28134             title: "Target",
28135             width: 120
28136         },
28137         {
28138             name : 'href',
28139             title: "Href",
28140             width: 220
28141         } // border?
28142         
28143     ],
28144     
28145     'INPUT' : [
28146         {
28147             name : 'name',
28148             title: "name",
28149             width: 120
28150         },
28151         {
28152             name : 'value',
28153             title: "Value",
28154             width: 120
28155         },
28156         {
28157             name : 'width',
28158             title: "Width",
28159             width: 40
28160         }
28161     ],
28162     'LABEL' : [
28163          {
28164             name : 'for',
28165             title: "For",
28166             width: 120
28167         }
28168     ],
28169     'TEXTAREA' : [
28170         {
28171             name : 'name',
28172             title: "name",
28173             width: 120
28174         },
28175         {
28176             name : 'rows',
28177             title: "Rows",
28178             width: 20
28179         },
28180         {
28181             name : 'cols',
28182             title: "Cols",
28183             width: 20
28184         }
28185     ],
28186     'SELECT' : [
28187         {
28188             name : 'name',
28189             title: "name",
28190             width: 120
28191         },
28192         {
28193             name : 'selectoptions',
28194             title: "Options",
28195             width: 200
28196         }
28197     ],
28198     
28199     // should we really allow this??
28200     // should this just be 
28201     'BODY' : [
28202         
28203         {
28204             name : 'title',
28205             title: "Title",
28206             width: 200,
28207             disabled : true
28208         }
28209     ],
28210  
28211     '*' : [
28212         // empty.
28213     ]
28214
28215 };
28216
28217 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28218 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28219
28220 Roo.form.HtmlEditor.ToolbarContext.options = {
28221         'font-family'  : [ 
28222                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28223                 [ 'Courier New', 'Courier New'],
28224                 [ 'Tahoma', 'Tahoma'],
28225                 [ 'Times New Roman,serif', 'Times'],
28226                 [ 'Verdana','Verdana' ]
28227         ]
28228 };
28229
28230 // fixme - these need to be configurable..
28231  
28232
28233 //Roo.form.HtmlEditor.ToolbarContext.types
28234
28235
28236 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28237     
28238     tb: false,
28239     
28240     rendered: false,
28241     
28242     editor : false,
28243     editorcore : false,
28244     /**
28245      * @cfg {Object} disable  List of toolbar elements to disable
28246          
28247      */
28248     disable : false,
28249     /**
28250      * @cfg {Object} styles List of styles 
28251      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28252      *
28253      * These must be defined in the page, so they get rendered correctly..
28254      * .headline { }
28255      * TD.underline { }
28256      * 
28257      */
28258     styles : false,
28259     
28260     options: false,
28261     
28262     toolbars : false,
28263     
28264     init : function(editor)
28265     {
28266         this.editor = editor;
28267         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28268         var editorcore = this.editorcore;
28269         
28270         var fid = editorcore.frameId;
28271         var etb = this;
28272         function btn(id, toggle, handler){
28273             var xid = fid + '-'+ id ;
28274             return {
28275                 id : xid,
28276                 cmd : id,
28277                 cls : 'x-btn-icon x-edit-'+id,
28278                 enableToggle:toggle !== false,
28279                 scope: editorcore, // was editor...
28280                 handler:handler||editorcore.relayBtnCmd,
28281                 clickEvent:'mousedown',
28282                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28283                 tabIndex:-1
28284             };
28285         }
28286         // create a new element.
28287         var wdiv = editor.wrap.createChild({
28288                 tag: 'div'
28289             }, editor.wrap.dom.firstChild.nextSibling, true);
28290         
28291         // can we do this more than once??
28292         
28293          // stop form submits
28294       
28295  
28296         // disable everything...
28297         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28298         this.toolbars = {};
28299         // block toolbars are built in updateToolbar when needed.
28300         for (var i in  ty) {
28301             
28302             this.toolbars[i] = this.buildToolbar(ty[i],i);
28303         }
28304         this.tb = this.toolbars.BODY;
28305         this.tb.el.show();
28306         this.buildFooter();
28307         this.footer.show();
28308         editor.on('hide', function( ) { this.footer.hide() }, this);
28309         editor.on('show', function( ) { this.footer.show() }, this);
28310         
28311          
28312         this.rendered = true;
28313         
28314         // the all the btns;
28315         editor.on('editorevent', this.updateToolbar, this);
28316         // other toolbars need to implement this..
28317         //editor.on('editmodechange', this.updateToolbar, this);
28318     },
28319     
28320     
28321     
28322     /**
28323      * Protected method that will not generally be called directly. It triggers
28324      * a toolbar update by reading the markup state of the current selection in the editor.
28325      *
28326      * Note you can force an update by calling on('editorevent', scope, false)
28327      */
28328     updateToolbar: function(editor ,ev, sel)
28329     {
28330         
28331         if (ev) {
28332             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28333         }
28334         
28335         //Roo.log(ev);
28336         // capture mouse up - this is handy for selecting images..
28337         // perhaps should go somewhere else...
28338         if(!this.editorcore.activated){
28339              this.editor.onFirstFocus();
28340             return;
28341         }
28342         //Roo.log(ev ? ev.target : 'NOTARGET');
28343         
28344         
28345         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28346         // selectNode - might want to handle IE?
28347         
28348         
28349         
28350         if (ev &&
28351             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28352             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28353             // they have click on an image...
28354             // let's see if we can change the selection...
28355             sel = ev.target;
28356             
28357             // this triggers looping?
28358             //this.editorcore.selectNode(sel);
28359              
28360         }
28361         
28362         // this forces an id..
28363         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28364              e.classList.remove('roo-ed-selection');
28365         });
28366         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28367         //Roo.get(node).addClass('roo-ed-selection');
28368       
28369         //var updateFooter = sel ? false : true; 
28370         
28371         
28372         var ans = this.editorcore.getAllAncestors();
28373         
28374         // pick
28375         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28376         
28377         if (!sel) { 
28378             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28379             sel = sel ? sel : this.editorcore.doc.body;
28380             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28381             
28382         }
28383         
28384         var tn = sel.tagName.toUpperCase();
28385         var lastSel = this.tb.selectedNode;
28386         this.tb.selectedNode = sel;
28387         var left_label = tn;
28388         
28389         // ok see if we are editing a block?
28390         
28391         var db = false;
28392         // you are not actually selecting the block.
28393         if (sel && sel.hasAttribute('data-block')) {
28394             db = sel;
28395         } else if (sel && sel.closest('[data-block]')) {
28396             
28397             db = sel.closest('[data-block]');
28398             //var cepar = sel.closest('[contenteditable=true]');
28399             //if (db && cepar && cepar.tagName != 'BODY') {
28400             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28401             //}   
28402         }
28403         
28404         
28405         var block = false;
28406         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28407         if (db && this.editorcore.enableBlocks) {
28408             block = Roo.htmleditor.Block.factory(db);
28409             
28410             
28411             if (block) {
28412                  db.className = (
28413                         db.classList.length > 0  ? db.className + ' ' : ''
28414                     )  + 'roo-ed-selection';
28415                  
28416                  // since we removed it earlier... its not there..
28417                 tn = 'BLOCK.' + db.getAttribute('data-block');
28418                 
28419                 //this.editorcore.selectNode(db);
28420                 if (typeof(this.toolbars[tn]) == 'undefined') {
28421                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28422                 }
28423                 this.toolbars[tn].selectedNode = db;
28424                 left_label = block.friendly_name;
28425                 ans = this.editorcore.getAllAncestors();
28426             }
28427             
28428                 
28429             
28430         }
28431         
28432         
28433         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28434             return; // no change?
28435         }
28436         
28437         
28438           
28439         this.tb.el.hide();
28440         ///console.log("show: " + tn);
28441         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28442         
28443         this.tb.el.show();
28444         // update name
28445         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28446         
28447         
28448         // update attributes
28449         if (block && this.tb.fields) {
28450              
28451             this.tb.fields.each(function(e) {
28452                 e.setValue(block[e.name]);
28453             });
28454             
28455             
28456         } else  if (this.tb.fields && this.tb.selectedNode) {
28457             this.tb.fields.each( function(e) {
28458                 if (e.stylename) {
28459                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28460                     return;
28461                 } 
28462                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28463             }, this);
28464             this.updateToolbarStyles(this.tb.selectedNode);  
28465         }
28466         
28467         
28468        
28469         Roo.menu.MenuMgr.hideAll();
28470
28471         
28472         
28473     
28474         // update the footer
28475         //
28476         this.updateFooter(ans);
28477              
28478     },
28479     
28480     updateToolbarStyles : function(sel)
28481     {
28482         var hasStyles = false;
28483         for(var i in this.styles) {
28484             hasStyles = true;
28485             break;
28486         }
28487         
28488         // update styles
28489         if (hasStyles && this.tb.hasStyles) { 
28490             var st = this.tb.fields.item(0);
28491             
28492             st.store.removeAll();
28493             var cn = sel.className.split(/\s+/);
28494             
28495             var avs = [];
28496             if (this.styles['*']) {
28497                 
28498                 Roo.each(this.styles['*'], function(v) {
28499                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28500                 });
28501             }
28502             if (this.styles[tn]) { 
28503                 Roo.each(this.styles[tn], function(v) {
28504                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28505                 });
28506             }
28507             
28508             st.store.loadData(avs);
28509             st.collapse();
28510             st.setValue(cn);
28511         }
28512     },
28513     
28514      
28515     updateFooter : function(ans)
28516     {
28517         var html = '';
28518         if (ans === false) {
28519             this.footDisp.dom.innerHTML = '';
28520             return;
28521         }
28522         
28523         this.footerEls = ans.reverse();
28524         Roo.each(this.footerEls, function(a,i) {
28525             if (!a) { return; }
28526             html += html.length ? ' &gt; '  :  '';
28527             
28528             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28529             
28530         });
28531        
28532         // 
28533         var sz = this.footDisp.up('td').getSize();
28534         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28535         this.footDisp.dom.style.marginLeft = '5px';
28536         
28537         this.footDisp.dom.style.overflow = 'hidden';
28538         
28539         this.footDisp.dom.innerHTML = html;
28540             
28541         
28542     },
28543    
28544        
28545     // private
28546     onDestroy : function(){
28547         if(this.rendered){
28548             
28549             this.tb.items.each(function(item){
28550                 if(item.menu){
28551                     item.menu.removeAll();
28552                     if(item.menu.el){
28553                         item.menu.el.destroy();
28554                     }
28555                 }
28556                 item.destroy();
28557             });
28558              
28559         }
28560     },
28561     onFirstFocus: function() {
28562         // need to do this for all the toolbars..
28563         this.tb.items.each(function(item){
28564            item.enable();
28565         });
28566     },
28567     buildToolbar: function(tlist, nm, friendly_name, block)
28568     {
28569         var editor = this.editor;
28570         var editorcore = this.editorcore;
28571          // create a new element.
28572         var wdiv = editor.wrap.createChild({
28573                 tag: 'div'
28574             }, editor.wrap.dom.firstChild.nextSibling, true);
28575         
28576        
28577         var tb = new Roo.Toolbar(wdiv);
28578         ///this.tb = tb; // << this sets the active toolbar..
28579         if (tlist === false && block) {
28580             tlist = block.contextMenu(this);
28581         }
28582         
28583         tb.hasStyles = false;
28584         tb.name = nm;
28585         
28586         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28587         
28588         var styles = Array.from(this.styles);
28589         
28590         
28591         // styles...
28592         if (styles && styles.length) {
28593             tb.hasStyles = true;
28594             // this needs a multi-select checkbox...
28595             tb.addField( new Roo.form.ComboBox({
28596                 store: new Roo.data.SimpleStore({
28597                     id : 'val',
28598                     fields: ['val', 'selected'],
28599                     data : [] 
28600                 }),
28601                 name : '-roo-edit-className',
28602                 attrname : 'className',
28603                 displayField: 'val',
28604                 typeAhead: false,
28605                 mode: 'local',
28606                 editable : false,
28607                 triggerAction: 'all',
28608                 emptyText:'Select Style',
28609                 selectOnFocus:true,
28610                 width: 130,
28611                 listeners : {
28612                     'select': function(c, r, i) {
28613                         // initial support only for on class per el..
28614                         tb.selectedNode.className =  r ? r.get('val') : '';
28615                         editorcore.syncValue();
28616                     }
28617                 }
28618     
28619             }));
28620         }
28621         
28622         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28623         
28624         
28625         for (var i = 0; i < tlist.length; i++) {
28626             
28627             // newer versions will use xtype cfg to create menus.
28628             if (typeof(tlist[i].xtype) != 'undefined') {
28629                 
28630                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28631                 
28632                 
28633                 continue;
28634             }
28635             
28636             var item = tlist[i];
28637             tb.add(item.title + ":&nbsp;");
28638             
28639             
28640             //optname == used so you can configure the options available..
28641             var opts = item.opts ? item.opts : false;
28642             if (item.optname) { // use the b
28643                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28644            
28645             }
28646             
28647             if (opts) {
28648                 // opts == pulldown..
28649                 tb.addField( new Roo.form.ComboBox({
28650                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28651                         id : 'val',
28652                         fields: ['val', 'display'],
28653                         data : opts  
28654                     }),
28655                     name : '-roo-edit-' + tlist[i].name,
28656                     
28657                     attrname : tlist[i].name,
28658                     stylename : item.style ? item.style : false,
28659                     
28660                     displayField: item.displayField ? item.displayField : 'val',
28661                     valueField :  'val',
28662                     typeAhead: false,
28663                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28664                     editable : false,
28665                     triggerAction: 'all',
28666                     emptyText:'Select',
28667                     selectOnFocus:true,
28668                     width: item.width ? item.width  : 130,
28669                     listeners : {
28670                         'select': function(c, r, i) {
28671                              
28672                             
28673                             if (c.stylename) {
28674                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28675                                 editorcore.syncValue();
28676                                 return;
28677                             }
28678                             if (r === false) {
28679                                 tb.selectedNode.removeAttribute(c.attrname);
28680                                 editorcore.syncValue();
28681                                 return;
28682                             }
28683                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28684                             editorcore.syncValue();
28685                         }
28686                     }
28687
28688                 }));
28689                 continue;
28690                     
28691                  
28692                 /*
28693                 tb.addField( new Roo.form.TextField({
28694                     name: i,
28695                     width: 100,
28696                     //allowBlank:false,
28697                     value: ''
28698                 }));
28699                 continue;
28700                 */
28701             }
28702             tb.addField( new Roo.form.TextField({
28703                 name: '-roo-edit-' + tlist[i].name,
28704                 attrname : tlist[i].name,
28705                 
28706                 width: item.width,
28707                 //allowBlank:true,
28708                 value: '',
28709                 listeners: {
28710                     'change' : function(f, nv, ov) {
28711                         
28712                          
28713                         tb.selectedNode.setAttribute(f.attrname, nv);
28714                         editorcore.syncValue();
28715                     }
28716                 }
28717             }));
28718              
28719         }
28720         
28721         var _this = this;
28722         var show_delete = !block || block.deleteTitle !== false;
28723         if(nm == 'BODY'){
28724             show_delete = false;
28725             tb.addSeparator();
28726         
28727             tb.addButton( {
28728                 text: 'Stylesheets',
28729
28730                 listeners : {
28731                     click : function ()
28732                     {
28733                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28734                     }
28735                 }
28736             });
28737         }
28738         
28739         tb.addFill();
28740         if (show_delete) {
28741             tb.addButton({
28742                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28743         
28744                 listeners : {
28745                     click : function ()
28746                     {
28747                         var sn = tb.selectedNode;
28748                         if (block) {
28749                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28750                             
28751                         }
28752                         if (!sn) {
28753                             return;
28754                         }
28755                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28756                         if (sn.hasAttribute('data-block')) {
28757                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28758                             sn.parentNode.removeChild(sn);
28759                             
28760                         } else if (sn && sn.tagName != 'BODY') {
28761                             // remove and keep parents.
28762                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28763                             a.replaceTag(sn);
28764                         }
28765                         
28766                         
28767                         var range = editorcore.createRange();
28768             
28769                         range.setStart(stn,0);
28770                         range.setEnd(stn,0); 
28771                         var selection = editorcore.getSelection();
28772                         selection.removeAllRanges();
28773                         selection.addRange(range);
28774                         
28775                         
28776                         //_this.updateToolbar(null, null, pn);
28777                         _this.updateToolbar(null, null, null);
28778                         _this.updateFooter(false);
28779                         
28780                     }
28781                 }
28782                 
28783                         
28784                     
28785                 
28786             });
28787         }    
28788         
28789         tb.el.on('click', function(e){
28790             e.preventDefault(); // what does this do?
28791         });
28792         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28793         tb.el.hide();
28794         
28795         // dont need to disable them... as they will get hidden
28796         return tb;
28797          
28798         
28799     },
28800     buildFooter : function()
28801     {
28802         
28803         var fel = this.editor.wrap.createChild();
28804         this.footer = new Roo.Toolbar(fel);
28805         // toolbar has scrolly on left / right?
28806         var footDisp= new Roo.Toolbar.Fill();
28807         var _t = this;
28808         this.footer.add(
28809             {
28810                 text : '&lt;',
28811                 xtype: 'Button',
28812                 handler : function() {
28813                     _t.footDisp.scrollTo('left',0,true)
28814                 }
28815             }
28816         );
28817         this.footer.add( footDisp );
28818         this.footer.add( 
28819             {
28820                 text : '&gt;',
28821                 xtype: 'Button',
28822                 handler : function() {
28823                     // no animation..
28824                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28825                 }
28826             }
28827         );
28828         var fel = Roo.get(footDisp.el);
28829         fel.addClass('x-editor-context');
28830         this.footDispWrap = fel; 
28831         this.footDispWrap.overflow  = 'hidden';
28832         
28833         this.footDisp = fel.createChild();
28834         this.footDispWrap.on('click', this.onContextClick, this)
28835         
28836         
28837     },
28838     // when the footer contect changes
28839     onContextClick : function (ev,dom)
28840     {
28841         ev.preventDefault();
28842         var  cn = dom.className;
28843         //Roo.log(cn);
28844         if (!cn.match(/x-ed-loc-/)) {
28845             return;
28846         }
28847         var n = cn.split('-').pop();
28848         var ans = this.footerEls;
28849         var sel = ans[n];
28850         
28851         this.editorcore.selectNode(sel);
28852         
28853         
28854         this.updateToolbar(null, null, sel);
28855         
28856         
28857     }
28858     
28859     
28860     
28861     
28862     
28863 });
28864
28865
28866
28867
28868
28869 /*
28870  * Based on:
28871  * Ext JS Library 1.1.1
28872  * Copyright(c) 2006-2007, Ext JS, LLC.
28873  *
28874  * Originally Released Under LGPL - original licence link has changed is not relivant.
28875  *
28876  * Fork - LGPL
28877  * <script type="text/javascript">
28878  */
28879  
28880 /**
28881  * @class Roo.form.BasicForm
28882  * @extends Roo.util.Observable
28883  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28884  * @constructor
28885  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28886  * @param {Object} config Configuration options
28887  */
28888 Roo.form.BasicForm = function(el, config){
28889     this.allItems = [];
28890     this.childForms = [];
28891     Roo.apply(this, config);
28892     /*
28893      * The Roo.form.Field items in this form.
28894      * @type MixedCollection
28895      */
28896      
28897      
28898     this.items = new Roo.util.MixedCollection(false, function(o){
28899         return o.id || (o.id = Roo.id());
28900     });
28901     this.addEvents({
28902         /**
28903          * @event beforeaction
28904          * Fires before any action is performed. Return false to cancel the action.
28905          * @param {Form} this
28906          * @param {Action} action The action to be performed
28907          */
28908         beforeaction: true,
28909         /**
28910          * @event actionfailed
28911          * Fires when an action fails.
28912          * @param {Form} this
28913          * @param {Action} action The action that failed
28914          */
28915         actionfailed : true,
28916         /**
28917          * @event actioncomplete
28918          * Fires when an action is completed.
28919          * @param {Form} this
28920          * @param {Action} action The action that completed
28921          */
28922         actioncomplete : true
28923     });
28924     if(el){
28925         this.initEl(el);
28926     }
28927     Roo.form.BasicForm.superclass.constructor.call(this);
28928     
28929     Roo.form.BasicForm.popover.apply();
28930 };
28931
28932 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28933     /**
28934      * @cfg {String} method
28935      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28936      */
28937     /**
28938      * @cfg {DataReader} reader
28939      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28940      * This is optional as there is built-in support for processing JSON.
28941      */
28942     /**
28943      * @cfg {DataReader} errorReader
28944      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28945      * This is completely optional as there is built-in support for processing JSON.
28946      */
28947     /**
28948      * @cfg {String} url
28949      * The URL to use for form actions if one isn't supplied in the action options.
28950      */
28951     /**
28952      * @cfg {Boolean} fileUpload
28953      * Set to true if this form is a file upload.
28954      */
28955      
28956     /**
28957      * @cfg {Object} baseParams
28958      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28959      */
28960      /**
28961      
28962     /**
28963      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28964      */
28965     timeout: 30,
28966
28967     // private
28968     activeAction : null,
28969
28970     /**
28971      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28972      * or setValues() data instead of when the form was first created.
28973      */
28974     trackResetOnLoad : false,
28975     
28976     
28977     /**
28978      * childForms - used for multi-tab forms
28979      * @type {Array}
28980      */
28981     childForms : false,
28982     
28983     /**
28984      * allItems - full list of fields.
28985      * @type {Array}
28986      */
28987     allItems : false,
28988     
28989     /**
28990      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28991      * element by passing it or its id or mask the form itself by passing in true.
28992      * @type Mixed
28993      */
28994     waitMsgTarget : false,
28995     
28996     /**
28997      * @type Boolean
28998      */
28999     disableMask : false,
29000     
29001     /**
29002      * @cfg {Boolean} errorMask (true|false) default false
29003      */
29004     errorMask : false,
29005     
29006     /**
29007      * @cfg {Number} maskOffset Default 100
29008      */
29009     maskOffset : 100,
29010
29011     // private
29012     initEl : function(el){
29013         this.el = Roo.get(el);
29014         this.id = this.el.id || Roo.id();
29015         this.el.on('submit', this.onSubmit, this);
29016         this.el.addClass('x-form');
29017     },
29018
29019     // private
29020     onSubmit : function(e){
29021         e.stopEvent();
29022     },
29023
29024     /**
29025      * Returns true if client-side validation on the form is successful.
29026      * @return Boolean
29027      */
29028     isValid : function(){
29029         var valid = true;
29030         var target = false;
29031         this.items.each(function(f){
29032             if(f.validate()){
29033                 return;
29034             }
29035             
29036             valid = false;
29037                 
29038             if(!target && f.el.isVisible(true)){
29039                 target = f;
29040             }
29041         });
29042         
29043         if(this.errorMask && !valid){
29044             Roo.form.BasicForm.popover.mask(this, target);
29045         }
29046         
29047         return valid;
29048     },
29049     /**
29050      * Returns array of invalid form fields.
29051      * @return Array
29052      */
29053     
29054     invalidFields : function()
29055     {
29056         var ret = [];
29057         this.items.each(function(f){
29058             if(f.validate()){
29059                 return;
29060             }
29061             ret.push(f);
29062             
29063         });
29064         
29065         return ret;
29066     },
29067     
29068     
29069     /**
29070      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29071      * @return Boolean
29072      */
29073     isDirty : function(){
29074         var dirty = false;
29075         this.items.each(function(f){
29076            if(f.isDirty()){
29077                dirty = true;
29078                return false;
29079            }
29080         });
29081         return dirty;
29082     },
29083     
29084     /**
29085      * Returns true if any fields in this form have changed since their original load. (New version)
29086      * @return Boolean
29087      */
29088     
29089     hasChanged : function()
29090     {
29091         var dirty = false;
29092         this.items.each(function(f){
29093            if(f.hasChanged()){
29094                dirty = true;
29095                return false;
29096            }
29097         });
29098         return dirty;
29099         
29100     },
29101     /**
29102      * Resets all hasChanged to 'false' -
29103      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29104      * So hasChanged storage is only to be used for this purpose
29105      * @return Boolean
29106      */
29107     resetHasChanged : function()
29108     {
29109         this.items.each(function(f){
29110            f.resetHasChanged();
29111         });
29112         
29113     },
29114     
29115     
29116     /**
29117      * Performs a predefined action (submit or load) or custom actions you define on this form.
29118      * @param {String} actionName The name of the action type
29119      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29120      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29121      * accept other config options):
29122      * <pre>
29123 Property          Type             Description
29124 ----------------  ---------------  ----------------------------------------------------------------------------------
29125 url               String           The url for the action (defaults to the form's url)
29126 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29127 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29128 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29129                                    validate the form on the client (defaults to false)
29130      * </pre>
29131      * @return {BasicForm} this
29132      */
29133     doAction : function(action, options){
29134         if(typeof action == 'string'){
29135             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29136         }
29137         if(this.fireEvent('beforeaction', this, action) !== false){
29138             this.beforeAction(action);
29139             action.run.defer(100, action);
29140         }
29141         return this;
29142     },
29143
29144     /**
29145      * Shortcut to do a submit action.
29146      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29147      * @return {BasicForm} this
29148      */
29149     submit : function(options){
29150         this.doAction('submit', options);
29151         return this;
29152     },
29153
29154     /**
29155      * Shortcut to do a load action.
29156      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29157      * @return {BasicForm} this
29158      */
29159     load : function(options){
29160         this.doAction('load', options);
29161         return this;
29162     },
29163
29164     /**
29165      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29166      * @param {Record} record The record to edit
29167      * @return {BasicForm} this
29168      */
29169     updateRecord : function(record){
29170         record.beginEdit();
29171         var fs = record.fields;
29172         fs.each(function(f){
29173             var field = this.findField(f.name);
29174             if(field){
29175                 record.set(f.name, field.getValue());
29176             }
29177         }, this);
29178         record.endEdit();
29179         return this;
29180     },
29181
29182     /**
29183      * Loads an Roo.data.Record into this form.
29184      * @param {Record} record The record to load
29185      * @return {BasicForm} this
29186      */
29187     loadRecord : function(record){
29188         this.setValues(record.data);
29189         return this;
29190     },
29191
29192     // private
29193     beforeAction : function(action){
29194         var o = action.options;
29195         
29196         if(!this.disableMask) {
29197             if(this.waitMsgTarget === true){
29198                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29199             }else if(this.waitMsgTarget){
29200                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29201                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29202             }else {
29203                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29204             }
29205         }
29206         
29207          
29208     },
29209
29210     // private
29211     afterAction : function(action, success){
29212         this.activeAction = null;
29213         var o = action.options;
29214         
29215         if(!this.disableMask) {
29216             if(this.waitMsgTarget === true){
29217                 this.el.unmask();
29218             }else if(this.waitMsgTarget){
29219                 this.waitMsgTarget.unmask();
29220             }else{
29221                 Roo.MessageBox.updateProgress(1);
29222                 Roo.MessageBox.hide();
29223             }
29224         }
29225         
29226         if(success){
29227             if(o.reset){
29228                 this.reset();
29229             }
29230             Roo.callback(o.success, o.scope, [this, action]);
29231             this.fireEvent('actioncomplete', this, action);
29232             
29233         }else{
29234             
29235             // failure condition..
29236             // we have a scenario where updates need confirming.
29237             // eg. if a locking scenario exists..
29238             // we look for { errors : { needs_confirm : true }} in the response.
29239             if (
29240                 (typeof(action.result) != 'undefined')  &&
29241                 (typeof(action.result.errors) != 'undefined')  &&
29242                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29243            ){
29244                 var _t = this;
29245                 Roo.MessageBox.confirm(
29246                     "Change requires confirmation",
29247                     action.result.errorMsg,
29248                     function(r) {
29249                         if (r != 'yes') {
29250                             return;
29251                         }
29252                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29253                     }
29254                     
29255                 );
29256                 
29257                 
29258                 
29259                 return;
29260             }
29261             
29262             Roo.callback(o.failure, o.scope, [this, action]);
29263             // show an error message if no failed handler is set..
29264             if (!this.hasListener('actionfailed')) {
29265                 Roo.MessageBox.alert("Error",
29266                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29267                         action.result.errorMsg :
29268                         "Saving Failed, please check your entries or try again"
29269                 );
29270             }
29271             
29272             this.fireEvent('actionfailed', this, action);
29273         }
29274         
29275     },
29276
29277     /**
29278      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29279      * @param {String} id The value to search for
29280      * @return Field
29281      */
29282     findField : function(id){
29283         var field = this.items.get(id);
29284         if(!field){
29285             this.items.each(function(f){
29286                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29287                     field = f;
29288                     return false;
29289                 }
29290             });
29291         }
29292         return field || null;
29293     },
29294
29295     /**
29296      * Add a secondary form to this one, 
29297      * Used to provide tabbed forms. One form is primary, with hidden values 
29298      * which mirror the elements from the other forms.
29299      * 
29300      * @param {Roo.form.Form} form to add.
29301      * 
29302      */
29303     addForm : function(form)
29304     {
29305        
29306         if (this.childForms.indexOf(form) > -1) {
29307             // already added..
29308             return;
29309         }
29310         this.childForms.push(form);
29311         var n = '';
29312         Roo.each(form.allItems, function (fe) {
29313             
29314             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29315             if (this.findField(n)) { // already added..
29316                 return;
29317             }
29318             var add = new Roo.form.Hidden({
29319                 name : n
29320             });
29321             add.render(this.el);
29322             
29323             this.add( add );
29324         }, this);
29325         
29326     },
29327     /**
29328      * Mark fields in this form invalid in bulk.
29329      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29330      * @return {BasicForm} this
29331      */
29332     markInvalid : function(errors){
29333         if(errors instanceof Array){
29334             for(var i = 0, len = errors.length; i < len; i++){
29335                 var fieldError = errors[i];
29336                 var f = this.findField(fieldError.id);
29337                 if(f){
29338                     f.markInvalid(fieldError.msg);
29339                 }
29340             }
29341         }else{
29342             var field, id;
29343             for(id in errors){
29344                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29345                     field.markInvalid(errors[id]);
29346                 }
29347             }
29348         }
29349         Roo.each(this.childForms || [], function (f) {
29350             f.markInvalid(errors);
29351         });
29352         
29353         return this;
29354     },
29355
29356     /**
29357      * Set values for fields in this form in bulk.
29358      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29359      * @return {BasicForm} this
29360      */
29361     setValues : function(values){
29362         if(values instanceof Array){ // array of objects
29363             for(var i = 0, len = values.length; i < len; i++){
29364                 var v = values[i];
29365                 var f = this.findField(v.id);
29366                 if(f){
29367                     f.setValue(v.value);
29368                     if(this.trackResetOnLoad){
29369                         f.originalValue = f.getValue();
29370                     }
29371                 }
29372             }
29373         }else{ // object hash
29374             var field, id;
29375             for(id in values){
29376                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29377                     
29378                     if (field.setFromData && 
29379                         field.valueField && 
29380                         field.displayField &&
29381                         // combos' with local stores can 
29382                         // be queried via setValue()
29383                         // to set their value..
29384                         (field.store && !field.store.isLocal)
29385                         ) {
29386                         // it's a combo
29387                         var sd = { };
29388                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29389                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29390                         field.setFromData(sd);
29391                         
29392                     } else {
29393                         field.setValue(values[id]);
29394                     }
29395                     
29396                     
29397                     if(this.trackResetOnLoad){
29398                         field.originalValue = field.getValue();
29399                     }
29400                 }
29401             }
29402         }
29403         this.resetHasChanged();
29404         
29405         
29406         Roo.each(this.childForms || [], function (f) {
29407             f.setValues(values);
29408             f.resetHasChanged();
29409         });
29410                 
29411         return this;
29412     },
29413  
29414     /**
29415      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29416      * they are returned as an array.
29417      * @param {Boolean} asString
29418      * @return {Object}
29419      */
29420     getValues : function(asString)
29421     {
29422         if (this.childForms) {
29423             // copy values from the child forms
29424             Roo.each(this.childForms, function (f) {
29425                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29426             }, this);
29427         }
29428         
29429         // use formdata
29430         if (typeof(FormData) != 'undefined' && asString !== true) {
29431             // this relies on a 'recent' version of chrome apparently...
29432             try {
29433                 var fd = (new FormData(this.el.dom)).entries();
29434                 var ret = {};
29435                 var ent = fd.next();
29436                 while (!ent.done) {
29437                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29438                     ent = fd.next();
29439                 };
29440                 return ret;
29441             } catch(e) {
29442                 
29443             }
29444             
29445         }
29446         
29447         
29448         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29449         if(asString === true){
29450             return fs;
29451         }
29452         return Roo.urlDecode(fs);
29453     },
29454     
29455     /**
29456      * Returns the fields in this form as an object with key/value pairs. 
29457      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29458      * Normally this will not return readOnly data 
29459      * @param {Boolean} with_readonly return readonly field data.
29460      * @return {Object}
29461      */
29462     getFieldValues : function(with_readonly)
29463     {
29464         if (this.childForms) {
29465             // copy values from the child forms
29466             // should this call getFieldValues - probably not as we do not currently copy
29467             // hidden fields when we generate..
29468             Roo.each(this.childForms, function (f) {
29469                 this.setValues(f.getFieldValues());
29470             }, this);
29471         }
29472         
29473         var ret = {};
29474         this.items.each(function(f){
29475             
29476             if (f.readOnly && with_readonly !== true) {
29477                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29478                         // if a subform contains a copy of them.
29479                         // if you have subforms with the same editable data, you will need to copy the data back
29480                         // and forth.
29481             }
29482             
29483             if (!f.getName()) {
29484                 return;
29485             }
29486             var v = f.getValue();
29487             if (f.inputType =='radio') {
29488                 if (typeof(ret[f.getName()]) == 'undefined') {
29489                     ret[f.getName()] = ''; // empty..
29490                 }
29491                 
29492                 if (!f.el.dom.checked) {
29493                     return;
29494                     
29495                 }
29496                 v = f.el.dom.value;
29497                 
29498             }
29499             
29500             // not sure if this supported any more..
29501             if ((typeof(v) == 'object') && f.getRawValue) {
29502                 v = f.getRawValue() ; // dates..
29503             }
29504             // combo boxes where name != hiddenName...
29505             if (f.name != f.getName()) {
29506                 ret[f.name] = f.getRawValue();
29507             }
29508             ret[f.getName()] = v;
29509         });
29510         
29511         return ret;
29512     },
29513
29514     /**
29515      * Clears all invalid messages in this form.
29516      * @return {BasicForm} this
29517      */
29518     clearInvalid : function(){
29519         this.items.each(function(f){
29520            f.clearInvalid();
29521         });
29522         
29523         Roo.each(this.childForms || [], function (f) {
29524             f.clearInvalid();
29525         });
29526         
29527         
29528         return this;
29529     },
29530
29531     /**
29532      * Resets this form.
29533      * @return {BasicForm} this
29534      */
29535     reset : function(){
29536         this.items.each(function(f){
29537             f.reset();
29538         });
29539         
29540         Roo.each(this.childForms || [], function (f) {
29541             f.reset();
29542         });
29543         this.resetHasChanged();
29544         
29545         return this;
29546     },
29547
29548     /**
29549      * Add Roo.form components to this form.
29550      * @param {Field} field1
29551      * @param {Field} field2 (optional)
29552      * @param {Field} etc (optional)
29553      * @return {BasicForm} this
29554      */
29555     add : function(){
29556         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29557         return this;
29558     },
29559
29560
29561     /**
29562      * Removes a field from the items collection (does NOT remove its markup).
29563      * @param {Field} field
29564      * @return {BasicForm} this
29565      */
29566     remove : function(field){
29567         this.items.remove(field);
29568         return this;
29569     },
29570
29571     /**
29572      * Looks at the fields in this form, checks them for an id attribute,
29573      * and calls applyTo on the existing dom element with that id.
29574      * @return {BasicForm} this
29575      */
29576     render : function(){
29577         this.items.each(function(f){
29578             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29579                 f.applyTo(f.id);
29580             }
29581         });
29582         return this;
29583     },
29584
29585     /**
29586      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29587      * @param {Object} values
29588      * @return {BasicForm} this
29589      */
29590     applyToFields : function(o){
29591         this.items.each(function(f){
29592            Roo.apply(f, o);
29593         });
29594         return this;
29595     },
29596
29597     /**
29598      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29599      * @param {Object} values
29600      * @return {BasicForm} this
29601      */
29602     applyIfToFields : function(o){
29603         this.items.each(function(f){
29604            Roo.applyIf(f, o);
29605         });
29606         return this;
29607     }
29608 });
29609
29610 // back compat
29611 Roo.BasicForm = Roo.form.BasicForm;
29612
29613 Roo.apply(Roo.form.BasicForm, {
29614     
29615     popover : {
29616         
29617         padding : 5,
29618         
29619         isApplied : false,
29620         
29621         isMasked : false,
29622         
29623         form : false,
29624         
29625         target : false,
29626         
29627         intervalID : false,
29628         
29629         maskEl : false,
29630         
29631         apply : function()
29632         {
29633             if(this.isApplied){
29634                 return;
29635             }
29636             
29637             this.maskEl = {
29638                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29639                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29640                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29641                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29642             };
29643             
29644             this.maskEl.top.enableDisplayMode("block");
29645             this.maskEl.left.enableDisplayMode("block");
29646             this.maskEl.bottom.enableDisplayMode("block");
29647             this.maskEl.right.enableDisplayMode("block");
29648             
29649             Roo.get(document.body).on('click', function(){
29650                 this.unmask();
29651             }, this);
29652             
29653             Roo.get(document.body).on('touchstart', function(){
29654                 this.unmask();
29655             }, this);
29656             
29657             this.isApplied = true
29658         },
29659         
29660         mask : function(form, target)
29661         {
29662             this.form = form;
29663             
29664             this.target = target;
29665             
29666             if(!this.form.errorMask || !target.el){
29667                 return;
29668             }
29669             
29670             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29671             
29672             var ot = this.target.el.calcOffsetsTo(scrollable);
29673             
29674             var scrollTo = ot[1] - this.form.maskOffset;
29675             
29676             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29677             
29678             scrollable.scrollTo('top', scrollTo);
29679             
29680             var el = this.target.wrap || this.target.el;
29681             
29682             var box = el.getBox();
29683             
29684             this.maskEl.top.setStyle('position', 'absolute');
29685             this.maskEl.top.setStyle('z-index', 10000);
29686             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29687             this.maskEl.top.setLeft(0);
29688             this.maskEl.top.setTop(0);
29689             this.maskEl.top.show();
29690             
29691             this.maskEl.left.setStyle('position', 'absolute');
29692             this.maskEl.left.setStyle('z-index', 10000);
29693             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29694             this.maskEl.left.setLeft(0);
29695             this.maskEl.left.setTop(box.y - this.padding);
29696             this.maskEl.left.show();
29697
29698             this.maskEl.bottom.setStyle('position', 'absolute');
29699             this.maskEl.bottom.setStyle('z-index', 10000);
29700             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29701             this.maskEl.bottom.setLeft(0);
29702             this.maskEl.bottom.setTop(box.bottom + this.padding);
29703             this.maskEl.bottom.show();
29704
29705             this.maskEl.right.setStyle('position', 'absolute');
29706             this.maskEl.right.setStyle('z-index', 10000);
29707             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29708             this.maskEl.right.setLeft(box.right + this.padding);
29709             this.maskEl.right.setTop(box.y - this.padding);
29710             this.maskEl.right.show();
29711
29712             this.intervalID = window.setInterval(function() {
29713                 Roo.form.BasicForm.popover.unmask();
29714             }, 10000);
29715
29716             window.onwheel = function(){ return false;};
29717             
29718             (function(){ this.isMasked = true; }).defer(500, this);
29719             
29720         },
29721         
29722         unmask : function()
29723         {
29724             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29725                 return;
29726             }
29727             
29728             this.maskEl.top.setStyle('position', 'absolute');
29729             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29730             this.maskEl.top.hide();
29731
29732             this.maskEl.left.setStyle('position', 'absolute');
29733             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29734             this.maskEl.left.hide();
29735
29736             this.maskEl.bottom.setStyle('position', 'absolute');
29737             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29738             this.maskEl.bottom.hide();
29739
29740             this.maskEl.right.setStyle('position', 'absolute');
29741             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29742             this.maskEl.right.hide();
29743             
29744             window.onwheel = function(){ return true;};
29745             
29746             if(this.intervalID){
29747                 window.clearInterval(this.intervalID);
29748                 this.intervalID = false;
29749             }
29750             
29751             this.isMasked = false;
29752             
29753         }
29754         
29755     }
29756     
29757 });/*
29758  * Based on:
29759  * Ext JS Library 1.1.1
29760  * Copyright(c) 2006-2007, Ext JS, LLC.
29761  *
29762  * Originally Released Under LGPL - original licence link has changed is not relivant.
29763  *
29764  * Fork - LGPL
29765  * <script type="text/javascript">
29766  */
29767
29768 /**
29769  * @class Roo.form.Form
29770  * @extends Roo.form.BasicForm
29771  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29772  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29773  * @constructor
29774  * @param {Object} config Configuration options
29775  */
29776 Roo.form.Form = function(config){
29777     var xitems =  [];
29778     if (config.items) {
29779         xitems = config.items;
29780         delete config.items;
29781     }
29782    
29783     
29784     Roo.form.Form.superclass.constructor.call(this, null, config);
29785     this.url = this.url || this.action;
29786     if(!this.root){
29787         this.root = new Roo.form.Layout(Roo.applyIf({
29788             id: Roo.id()
29789         }, config));
29790     }
29791     this.active = this.root;
29792     /**
29793      * Array of all the buttons that have been added to this form via {@link addButton}
29794      * @type Array
29795      */
29796     this.buttons = [];
29797     this.allItems = [];
29798     this.addEvents({
29799         /**
29800          * @event clientvalidation
29801          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29802          * @param {Form} this
29803          * @param {Boolean} valid true if the form has passed client-side validation
29804          */
29805         clientvalidation: true,
29806         /**
29807          * @event rendered
29808          * Fires when the form is rendered
29809          * @param {Roo.form.Form} form
29810          */
29811         rendered : true
29812     });
29813     
29814     if (this.progressUrl) {
29815             // push a hidden field onto the list of fields..
29816             this.addxtype( {
29817                     xns: Roo.form, 
29818                     xtype : 'Hidden', 
29819                     name : 'UPLOAD_IDENTIFIER' 
29820             });
29821         }
29822         
29823     
29824     Roo.each(xitems, this.addxtype, this);
29825     
29826 };
29827
29828 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29829      /**
29830      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29831      */
29832     
29833     /**
29834      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29835      */
29836     /**
29837      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29838      */
29839     /**
29840      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29841      */
29842     buttonAlign:'center',
29843
29844     /**
29845      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29846      */
29847     minButtonWidth:75,
29848
29849     /**
29850      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29851      * This property cascades to child containers if not set.
29852      */
29853     labelAlign:'left',
29854
29855     /**
29856      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29857      * fires a looping event with that state. This is required to bind buttons to the valid
29858      * state using the config value formBind:true on the button.
29859      */
29860     monitorValid : false,
29861
29862     /**
29863      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29864      */
29865     monitorPoll : 200,
29866     
29867     /**
29868      * @cfg {String} progressUrl - Url to return progress data 
29869      */
29870     
29871     progressUrl : false,
29872     /**
29873      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29874      * sending a formdata with extra parameters - eg uploaded elements.
29875      */
29876     
29877     formData : false,
29878     
29879     /**
29880      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29881      * fields are added and the column is closed. If no fields are passed the column remains open
29882      * until end() is called.
29883      * @param {Object} config The config to pass to the column
29884      * @param {Field} field1 (optional)
29885      * @param {Field} field2 (optional)
29886      * @param {Field} etc (optional)
29887      * @return Column The column container object
29888      */
29889     column : function(c){
29890         var col = new Roo.form.Column(c);
29891         this.start(col);
29892         if(arguments.length > 1){ // duplicate code required because of Opera
29893             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29894             this.end();
29895         }
29896         return col;
29897     },
29898
29899     /**
29900      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29901      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29902      * until end() is called.
29903      * @param {Object} config The config to pass to the fieldset
29904      * @param {Field} field1 (optional)
29905      * @param {Field} field2 (optional)
29906      * @param {Field} etc (optional)
29907      * @return FieldSet The fieldset container object
29908      */
29909     fieldset : function(c){
29910         var fs = new Roo.form.FieldSet(c);
29911         this.start(fs);
29912         if(arguments.length > 1){ // duplicate code required because of Opera
29913             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29914             this.end();
29915         }
29916         return fs;
29917     },
29918
29919     /**
29920      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29921      * fields are added and the container is closed. If no fields are passed the container remains open
29922      * until end() is called.
29923      * @param {Object} config The config to pass to the Layout
29924      * @param {Field} field1 (optional)
29925      * @param {Field} field2 (optional)
29926      * @param {Field} etc (optional)
29927      * @return Layout The container object
29928      */
29929     container : function(c){
29930         var l = new Roo.form.Layout(c);
29931         this.start(l);
29932         if(arguments.length > 1){ // duplicate code required because of Opera
29933             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29934             this.end();
29935         }
29936         return l;
29937     },
29938
29939     /**
29940      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29941      * @param {Object} container A Roo.form.Layout or subclass of Layout
29942      * @return {Form} this
29943      */
29944     start : function(c){
29945         // cascade label info
29946         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29947         this.active.stack.push(c);
29948         c.ownerCt = this.active;
29949         this.active = c;
29950         return this;
29951     },
29952
29953     /**
29954      * Closes the current open container
29955      * @return {Form} this
29956      */
29957     end : function(){
29958         if(this.active == this.root){
29959             return this;
29960         }
29961         this.active = this.active.ownerCt;
29962         return this;
29963     },
29964
29965     /**
29966      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29967      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29968      * as the label of the field.
29969      * @param {Field} field1
29970      * @param {Field} field2 (optional)
29971      * @param {Field} etc. (optional)
29972      * @return {Form} this
29973      */
29974     add : function(){
29975         this.active.stack.push.apply(this.active.stack, arguments);
29976         this.allItems.push.apply(this.allItems,arguments);
29977         var r = [];
29978         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29979             if(a[i].isFormField){
29980                 r.push(a[i]);
29981             }
29982         }
29983         if(r.length > 0){
29984             Roo.form.Form.superclass.add.apply(this, r);
29985         }
29986         return this;
29987     },
29988     
29989
29990     
29991     
29992     
29993      /**
29994      * Find any element that has been added to a form, using it's ID or name
29995      * This can include framesets, columns etc. along with regular fields..
29996      * @param {String} id - id or name to find.
29997      
29998      * @return {Element} e - or false if nothing found.
29999      */
30000     findbyId : function(id)
30001     {
30002         var ret = false;
30003         if (!id) {
30004             return ret;
30005         }
30006         Roo.each(this.allItems, function(f){
30007             if (f.id == id || f.name == id ){
30008                 ret = f;
30009                 return false;
30010             }
30011         });
30012         return ret;
30013     },
30014
30015     
30016     
30017     /**
30018      * Render this form into the passed container. This should only be called once!
30019      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30020      * @return {Form} this
30021      */
30022     render : function(ct)
30023     {
30024         
30025         
30026         
30027         ct = Roo.get(ct);
30028         var o = this.autoCreate || {
30029             tag: 'form',
30030             method : this.method || 'POST',
30031             id : this.id || Roo.id()
30032         };
30033         this.initEl(ct.createChild(o));
30034
30035         this.root.render(this.el);
30036         
30037        
30038              
30039         this.items.each(function(f){
30040             f.render('x-form-el-'+f.id);
30041         });
30042
30043         if(this.buttons.length > 0){
30044             // tables are required to maintain order and for correct IE layout
30045             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30046                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30047                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30048             }}, null, true);
30049             var tr = tb.getElementsByTagName('tr')[0];
30050             for(var i = 0, len = this.buttons.length; i < len; i++) {
30051                 var b = this.buttons[i];
30052                 var td = document.createElement('td');
30053                 td.className = 'x-form-btn-td';
30054                 b.render(tr.appendChild(td));
30055             }
30056         }
30057         if(this.monitorValid){ // initialize after render
30058             this.startMonitoring();
30059         }
30060         this.fireEvent('rendered', this);
30061         return this;
30062     },
30063
30064     /**
30065      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30066      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30067      * object or a valid Roo.DomHelper element config
30068      * @param {Function} handler The function called when the button is clicked
30069      * @param {Object} scope (optional) The scope of the handler function
30070      * @return {Roo.Button}
30071      */
30072     addButton : function(config, handler, scope){
30073         var bc = {
30074             handler: handler,
30075             scope: scope,
30076             minWidth: this.minButtonWidth,
30077             hideParent:true
30078         };
30079         if(typeof config == "string"){
30080             bc.text = config;
30081         }else{
30082             Roo.apply(bc, config);
30083         }
30084         var btn = new Roo.Button(null, bc);
30085         this.buttons.push(btn);
30086         return btn;
30087     },
30088
30089      /**
30090      * Adds a series of form elements (using the xtype property as the factory method.
30091      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30092      * @param {Object} config 
30093      */
30094     
30095     addxtype : function()
30096     {
30097         var ar = Array.prototype.slice.call(arguments, 0);
30098         var ret = false;
30099         for(var i = 0; i < ar.length; i++) {
30100             if (!ar[i]) {
30101                 continue; // skip -- if this happends something invalid got sent, we 
30102                 // should ignore it, as basically that interface element will not show up
30103                 // and that should be pretty obvious!!
30104             }
30105             
30106             if (Roo.form[ar[i].xtype]) {
30107                 ar[i].form = this;
30108                 var fe = Roo.factory(ar[i], Roo.form);
30109                 if (!ret) {
30110                     ret = fe;
30111                 }
30112                 fe.form = this;
30113                 if (fe.store) {
30114                     fe.store.form = this;
30115                 }
30116                 if (fe.isLayout) {  
30117                          
30118                     this.start(fe);
30119                     this.allItems.push(fe);
30120                     if (fe.items && fe.addxtype) {
30121                         fe.addxtype.apply(fe, fe.items);
30122                         delete fe.items;
30123                     }
30124                      this.end();
30125                     continue;
30126                 }
30127                 
30128                 
30129                  
30130                 this.add(fe);
30131               //  console.log('adding ' + ar[i].xtype);
30132             }
30133             if (ar[i].xtype == 'Button') {  
30134                 //console.log('adding button');
30135                 //console.log(ar[i]);
30136                 this.addButton(ar[i]);
30137                 this.allItems.push(fe);
30138                 continue;
30139             }
30140             
30141             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30142                 alert('end is not supported on xtype any more, use items');
30143             //    this.end();
30144             //    //console.log('adding end');
30145             }
30146             
30147         }
30148         return ret;
30149     },
30150     
30151     /**
30152      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30153      * option "monitorValid"
30154      */
30155     startMonitoring : function(){
30156         if(!this.bound){
30157             this.bound = true;
30158             Roo.TaskMgr.start({
30159                 run : this.bindHandler,
30160                 interval : this.monitorPoll || 200,
30161                 scope: this
30162             });
30163         }
30164     },
30165
30166     /**
30167      * Stops monitoring of the valid state of this form
30168      */
30169     stopMonitoring : function(){
30170         this.bound = false;
30171     },
30172
30173     // private
30174     bindHandler : function(){
30175         if(!this.bound){
30176             return false; // stops binding
30177         }
30178         var valid = true;
30179         this.items.each(function(f){
30180             if(!f.isValid(true)){
30181                 valid = false;
30182                 return false;
30183             }
30184         });
30185         for(var i = 0, len = this.buttons.length; i < len; i++){
30186             var btn = this.buttons[i];
30187             if(btn.formBind === true && btn.disabled === valid){
30188                 btn.setDisabled(!valid);
30189             }
30190         }
30191         this.fireEvent('clientvalidation', this, valid);
30192     }
30193     
30194     
30195     
30196     
30197     
30198     
30199     
30200     
30201 });
30202
30203
30204 // back compat
30205 Roo.Form = Roo.form.Form;
30206 /*
30207  * Based on:
30208  * Ext JS Library 1.1.1
30209  * Copyright(c) 2006-2007, Ext JS, LLC.
30210  *
30211  * Originally Released Under LGPL - original licence link has changed is not relivant.
30212  *
30213  * Fork - LGPL
30214  * <script type="text/javascript">
30215  */
30216
30217 // as we use this in bootstrap.
30218 Roo.namespace('Roo.form');
30219  /**
30220  * @class Roo.form.Action
30221  * Internal Class used to handle form actions
30222  * @constructor
30223  * @param {Roo.form.BasicForm} el The form element or its id
30224  * @param {Object} config Configuration options
30225  */
30226
30227  
30228  
30229 // define the action interface
30230 Roo.form.Action = function(form, options){
30231     this.form = form;
30232     this.options = options || {};
30233 };
30234 /**
30235  * Client Validation Failed
30236  * @const 
30237  */
30238 Roo.form.Action.CLIENT_INVALID = 'client';
30239 /**
30240  * Server Validation Failed
30241  * @const 
30242  */
30243 Roo.form.Action.SERVER_INVALID = 'server';
30244  /**
30245  * Connect to Server Failed
30246  * @const 
30247  */
30248 Roo.form.Action.CONNECT_FAILURE = 'connect';
30249 /**
30250  * Reading Data from Server Failed
30251  * @const 
30252  */
30253 Roo.form.Action.LOAD_FAILURE = 'load';
30254
30255 Roo.form.Action.prototype = {
30256     type : 'default',
30257     failureType : undefined,
30258     response : undefined,
30259     result : undefined,
30260
30261     // interface method
30262     run : function(options){
30263
30264     },
30265
30266     // interface method
30267     success : function(response){
30268
30269     },
30270
30271     // interface method
30272     handleResponse : function(response){
30273
30274     },
30275
30276     // default connection failure
30277     failure : function(response){
30278         
30279         this.response = response;
30280         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30281         this.form.afterAction(this, false);
30282     },
30283
30284     processResponse : function(response){
30285         this.response = response;
30286         if(!response.responseText){
30287             return true;
30288         }
30289         this.result = this.handleResponse(response);
30290         return this.result;
30291     },
30292
30293     // utility functions used internally
30294     getUrl : function(appendParams){
30295         var url = this.options.url || this.form.url || this.form.el.dom.action;
30296         if(appendParams){
30297             var p = this.getParams();
30298             if(p){
30299                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30300             }
30301         }
30302         return url;
30303     },
30304
30305     getMethod : function(){
30306         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30307     },
30308
30309     getParams : function(){
30310         var bp = this.form.baseParams;
30311         var p = this.options.params;
30312         if(p){
30313             if(typeof p == "object"){
30314                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30315             }else if(typeof p == 'string' && bp){
30316                 p += '&' + Roo.urlEncode(bp);
30317             }
30318         }else if(bp){
30319             p = Roo.urlEncode(bp);
30320         }
30321         return p;
30322     },
30323
30324     createCallback : function(){
30325         return {
30326             success: this.success,
30327             failure: this.failure,
30328             scope: this,
30329             timeout: (this.form.timeout*1000),
30330             upload: this.form.fileUpload ? this.success : undefined
30331         };
30332     }
30333 };
30334
30335 Roo.form.Action.Submit = function(form, options){
30336     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30337 };
30338
30339 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30340     type : 'submit',
30341
30342     haveProgress : false,
30343     uploadComplete : false,
30344     
30345     // uploadProgress indicator.
30346     uploadProgress : function()
30347     {
30348         if (!this.form.progressUrl) {
30349             return;
30350         }
30351         
30352         if (!this.haveProgress) {
30353             Roo.MessageBox.progress("Uploading", "Uploading");
30354         }
30355         if (this.uploadComplete) {
30356            Roo.MessageBox.hide();
30357            return;
30358         }
30359         
30360         this.haveProgress = true;
30361    
30362         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30363         
30364         var c = new Roo.data.Connection();
30365         c.request({
30366             url : this.form.progressUrl,
30367             params: {
30368                 id : uid
30369             },
30370             method: 'GET',
30371             success : function(req){
30372                //console.log(data);
30373                 var rdata = false;
30374                 var edata;
30375                 try  {
30376                    rdata = Roo.decode(req.responseText)
30377                 } catch (e) {
30378                     Roo.log("Invalid data from server..");
30379                     Roo.log(edata);
30380                     return;
30381                 }
30382                 if (!rdata || !rdata.success) {
30383                     Roo.log(rdata);
30384                     Roo.MessageBox.alert(Roo.encode(rdata));
30385                     return;
30386                 }
30387                 var data = rdata.data;
30388                 
30389                 if (this.uploadComplete) {
30390                    Roo.MessageBox.hide();
30391                    return;
30392                 }
30393                    
30394                 if (data){
30395                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30396                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30397                     );
30398                 }
30399                 this.uploadProgress.defer(2000,this);
30400             },
30401        
30402             failure: function(data) {
30403                 Roo.log('progress url failed ');
30404                 Roo.log(data);
30405             },
30406             scope : this
30407         });
30408            
30409     },
30410     
30411     
30412     run : function()
30413     {
30414         // run get Values on the form, so it syncs any secondary forms.
30415         this.form.getValues();
30416         
30417         var o = this.options;
30418         var method = this.getMethod();
30419         var isPost = method == 'POST';
30420         if(o.clientValidation === false || this.form.isValid()){
30421             
30422             if (this.form.progressUrl) {
30423                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30424                     (new Date() * 1) + '' + Math.random());
30425                     
30426             } 
30427             
30428             
30429             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30430                 form:this.form.el.dom,
30431                 url:this.getUrl(!isPost),
30432                 method: method,
30433                 params:isPost ? this.getParams() : null,
30434                 isUpload: this.form.fileUpload,
30435                 formData : this.form.formData
30436             }));
30437             
30438             this.uploadProgress();
30439
30440         }else if (o.clientValidation !== false){ // client validation failed
30441             this.failureType = Roo.form.Action.CLIENT_INVALID;
30442             this.form.afterAction(this, false);
30443         }
30444     },
30445
30446     success : function(response)
30447     {
30448         this.uploadComplete= true;
30449         if (this.haveProgress) {
30450             Roo.MessageBox.hide();
30451         }
30452         
30453         
30454         var result = this.processResponse(response);
30455         if(result === true || result.success){
30456             this.form.afterAction(this, true);
30457             return;
30458         }
30459         if(result.errors){
30460             this.form.markInvalid(result.errors);
30461             this.failureType = Roo.form.Action.SERVER_INVALID;
30462         }
30463         this.form.afterAction(this, false);
30464     },
30465     failure : function(response)
30466     {
30467         this.uploadComplete= true;
30468         if (this.haveProgress) {
30469             Roo.MessageBox.hide();
30470         }
30471         
30472         this.response = response;
30473         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30474         this.form.afterAction(this, false);
30475     },
30476     
30477     handleResponse : function(response){
30478         if(this.form.errorReader){
30479             var rs = this.form.errorReader.read(response);
30480             var errors = [];
30481             if(rs.records){
30482                 for(var i = 0, len = rs.records.length; i < len; i++) {
30483                     var r = rs.records[i];
30484                     errors[i] = r.data;
30485                 }
30486             }
30487             if(errors.length < 1){
30488                 errors = null;
30489             }
30490             return {
30491                 success : rs.success,
30492                 errors : errors
30493             };
30494         }
30495         var ret = false;
30496         try {
30497             ret = Roo.decode(response.responseText);
30498         } catch (e) {
30499             ret = {
30500                 success: false,
30501                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30502                 errors : []
30503             };
30504         }
30505         return ret;
30506         
30507     }
30508 });
30509
30510
30511 Roo.form.Action.Load = function(form, options){
30512     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30513     this.reader = this.form.reader;
30514 };
30515
30516 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30517     type : 'load',
30518
30519     run : function(){
30520         
30521         Roo.Ajax.request(Roo.apply(
30522                 this.createCallback(), {
30523                     method:this.getMethod(),
30524                     url:this.getUrl(false),
30525                     params:this.getParams()
30526         }));
30527     },
30528
30529     success : function(response){
30530         
30531         var result = this.processResponse(response);
30532         if(result === true || !result.success || !result.data){
30533             this.failureType = Roo.form.Action.LOAD_FAILURE;
30534             this.form.afterAction(this, false);
30535             return;
30536         }
30537         this.form.clearInvalid();
30538         this.form.setValues(result.data);
30539         this.form.afterAction(this, true);
30540     },
30541
30542     handleResponse : function(response){
30543         if(this.form.reader){
30544             var rs = this.form.reader.read(response);
30545             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30546             return {
30547                 success : rs.success,
30548                 data : data
30549             };
30550         }
30551         return Roo.decode(response.responseText);
30552     }
30553 });
30554
30555 Roo.form.Action.ACTION_TYPES = {
30556     'load' : Roo.form.Action.Load,
30557     'submit' : Roo.form.Action.Submit
30558 };/*
30559  * Based on:
30560  * Ext JS Library 1.1.1
30561  * Copyright(c) 2006-2007, Ext JS, LLC.
30562  *
30563  * Originally Released Under LGPL - original licence link has changed is not relivant.
30564  *
30565  * Fork - LGPL
30566  * <script type="text/javascript">
30567  */
30568  
30569 /**
30570  * @class Roo.form.Layout
30571  * @extends Roo.Component
30572  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30573  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30574  * @constructor
30575  * @param {Object} config Configuration options
30576  */
30577 Roo.form.Layout = function(config){
30578     var xitems = [];
30579     if (config.items) {
30580         xitems = config.items;
30581         delete config.items;
30582     }
30583     Roo.form.Layout.superclass.constructor.call(this, config);
30584     this.stack = [];
30585     Roo.each(xitems, this.addxtype, this);
30586      
30587 };
30588
30589 Roo.extend(Roo.form.Layout, Roo.Component, {
30590     /**
30591      * @cfg {String/Object} autoCreate
30592      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30593      */
30594     /**
30595      * @cfg {String/Object/Function} style
30596      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30597      * a function which returns such a specification.
30598      */
30599     /**
30600      * @cfg {String} labelAlign
30601      * Valid values are "left," "top" and "right" (defaults to "left")
30602      */
30603     /**
30604      * @cfg {Number} labelWidth
30605      * Fixed width in pixels of all field labels (defaults to undefined)
30606      */
30607     /**
30608      * @cfg {Boolean} clear
30609      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30610      */
30611     clear : true,
30612     /**
30613      * @cfg {String} labelSeparator
30614      * The separator to use after field labels (defaults to ':')
30615      */
30616     labelSeparator : ':',
30617     /**
30618      * @cfg {Boolean} hideLabels
30619      * True to suppress the display of field labels in this layout (defaults to false)
30620      */
30621     hideLabels : false,
30622
30623     // private
30624     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30625     
30626     isLayout : true,
30627     
30628     // private
30629     onRender : function(ct, position){
30630         if(this.el){ // from markup
30631             this.el = Roo.get(this.el);
30632         }else {  // generate
30633             var cfg = this.getAutoCreate();
30634             this.el = ct.createChild(cfg, position);
30635         }
30636         if(this.style){
30637             this.el.applyStyles(this.style);
30638         }
30639         if(this.labelAlign){
30640             this.el.addClass('x-form-label-'+this.labelAlign);
30641         }
30642         if(this.hideLabels){
30643             this.labelStyle = "display:none";
30644             this.elementStyle = "padding-left:0;";
30645         }else{
30646             if(typeof this.labelWidth == 'number'){
30647                 this.labelStyle = "width:"+this.labelWidth+"px;";
30648                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30649             }
30650             if(this.labelAlign == 'top'){
30651                 this.labelStyle = "width:auto;";
30652                 this.elementStyle = "padding-left:0;";
30653             }
30654         }
30655         var stack = this.stack;
30656         var slen = stack.length;
30657         if(slen > 0){
30658             if(!this.fieldTpl){
30659                 var t = new Roo.Template(
30660                     '<div class="x-form-item {5}">',
30661                         '<label for="{0}" style="{2}">{1}{4}</label>',
30662                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30663                         '</div>',
30664                     '</div><div class="x-form-clear-left"></div>'
30665                 );
30666                 t.disableFormats = true;
30667                 t.compile();
30668                 Roo.form.Layout.prototype.fieldTpl = t;
30669             }
30670             for(var i = 0; i < slen; i++) {
30671                 if(stack[i].isFormField){
30672                     this.renderField(stack[i]);
30673                 }else{
30674                     this.renderComponent(stack[i]);
30675                 }
30676             }
30677         }
30678         if(this.clear){
30679             this.el.createChild({cls:'x-form-clear'});
30680         }
30681     },
30682
30683     // private
30684     renderField : function(f){
30685         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30686                f.id, //0
30687                f.fieldLabel, //1
30688                f.labelStyle||this.labelStyle||'', //2
30689                this.elementStyle||'', //3
30690                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30691                f.itemCls||this.itemCls||''  //5
30692        ], true).getPrevSibling());
30693     },
30694
30695     // private
30696     renderComponent : function(c){
30697         c.render(c.isLayout ? this.el : this.el.createChild());    
30698     },
30699     /**
30700      * Adds a object form elements (using the xtype property as the factory method.)
30701      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30702      * @param {Object} config 
30703      */
30704     addxtype : function(o)
30705     {
30706         // create the lement.
30707         o.form = this.form;
30708         var fe = Roo.factory(o, Roo.form);
30709         this.form.allItems.push(fe);
30710         this.stack.push(fe);
30711         
30712         if (fe.isFormField) {
30713             this.form.items.add(fe);
30714         }
30715          
30716         return fe;
30717     }
30718 });
30719
30720 /**
30721  * @class Roo.form.Column
30722  * @extends Roo.form.Layout
30723  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30724  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30725  * @constructor
30726  * @param {Object} config Configuration options
30727  */
30728 Roo.form.Column = function(config){
30729     Roo.form.Column.superclass.constructor.call(this, config);
30730 };
30731
30732 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30733     /**
30734      * @cfg {Number/String} width
30735      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30736      */
30737     /**
30738      * @cfg {String/Object} autoCreate
30739      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30740      */
30741
30742     // private
30743     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30744
30745     // private
30746     onRender : function(ct, position){
30747         Roo.form.Column.superclass.onRender.call(this, ct, position);
30748         if(this.width){
30749             this.el.setWidth(this.width);
30750         }
30751     }
30752 });
30753
30754
30755 /**
30756  * @class Roo.form.Row
30757  * @extends Roo.form.Layout
30758  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30759  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30760  * @constructor
30761  * @param {Object} config Configuration options
30762  */
30763
30764  
30765 Roo.form.Row = function(config){
30766     Roo.form.Row.superclass.constructor.call(this, config);
30767 };
30768  
30769 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30770       /**
30771      * @cfg {Number/String} width
30772      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30773      */
30774     /**
30775      * @cfg {Number/String} height
30776      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30777      */
30778     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30779     
30780     padWidth : 20,
30781     // private
30782     onRender : function(ct, position){
30783         //console.log('row render');
30784         if(!this.rowTpl){
30785             var t = new Roo.Template(
30786                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30787                     '<label for="{0}" style="{2}">{1}{4}</label>',
30788                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30789                     '</div>',
30790                 '</div>'
30791             );
30792             t.disableFormats = true;
30793             t.compile();
30794             Roo.form.Layout.prototype.rowTpl = t;
30795         }
30796         this.fieldTpl = this.rowTpl;
30797         
30798         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30799         var labelWidth = 100;
30800         
30801         if ((this.labelAlign != 'top')) {
30802             if (typeof this.labelWidth == 'number') {
30803                 labelWidth = this.labelWidth
30804             }
30805             this.padWidth =  20 + labelWidth;
30806             
30807         }
30808         
30809         Roo.form.Column.superclass.onRender.call(this, ct, position);
30810         if(this.width){
30811             this.el.setWidth(this.width);
30812         }
30813         if(this.height){
30814             this.el.setHeight(this.height);
30815         }
30816     },
30817     
30818     // private
30819     renderField : function(f){
30820         f.fieldEl = this.fieldTpl.append(this.el, [
30821                f.id, f.fieldLabel,
30822                f.labelStyle||this.labelStyle||'',
30823                this.elementStyle||'',
30824                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30825                f.itemCls||this.itemCls||'',
30826                f.width ? f.width + this.padWidth : 160 + this.padWidth
30827        ],true);
30828     }
30829 });
30830  
30831
30832 /**
30833  * @class Roo.form.FieldSet
30834  * @extends Roo.form.Layout
30835  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30836  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30837  * @constructor
30838  * @param {Object} config Configuration options
30839  */
30840 Roo.form.FieldSet = function(config){
30841     Roo.form.FieldSet.superclass.constructor.call(this, config);
30842 };
30843
30844 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30845     /**
30846      * @cfg {String} legend
30847      * The text to display as the legend for the FieldSet (defaults to '')
30848      */
30849     /**
30850      * @cfg {String/Object} autoCreate
30851      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30852      */
30853
30854     // private
30855     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30856
30857     // private
30858     onRender : function(ct, position){
30859         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30860         if(this.legend){
30861             this.setLegend(this.legend);
30862         }
30863     },
30864
30865     // private
30866     setLegend : function(text){
30867         if(this.rendered){
30868             this.el.child('legend').update(text);
30869         }
30870     }
30871 });/*
30872  * Based on:
30873  * Ext JS Library 1.1.1
30874  * Copyright(c) 2006-2007, Ext JS, LLC.
30875  *
30876  * Originally Released Under LGPL - original licence link has changed is not relivant.
30877  *
30878  * Fork - LGPL
30879  * <script type="text/javascript">
30880  */
30881 /**
30882  * @class Roo.form.VTypes
30883  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30884  * @static
30885  */
30886 Roo.form.VTypes = function(){
30887     // closure these in so they are only created once.
30888     var alpha = /^[a-zA-Z_]+$/;
30889     var alphanum = /^[a-zA-Z0-9_]+$/;
30890     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30891     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30892
30893     // All these messages and functions are configurable
30894     return {
30895         /**
30896          * The function used to validate email addresses
30897          * @param {String} value The email address
30898          */
30899         'email' : function(v){
30900             return email.test(v);
30901         },
30902         /**
30903          * The error text to display when the email validation function returns false
30904          * @type String
30905          */
30906         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30907         /**
30908          * The keystroke filter mask to be applied on email input
30909          * @type RegExp
30910          */
30911         'emailMask' : /[a-z0-9_\.\-@]/i,
30912
30913         /**
30914          * The function used to validate URLs
30915          * @param {String} value The URL
30916          */
30917         'url' : function(v){
30918             return url.test(v);
30919         },
30920         /**
30921          * The error text to display when the url validation function returns false
30922          * @type String
30923          */
30924         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30925         
30926         /**
30927          * The function used to validate alpha values
30928          * @param {String} value The value
30929          */
30930         'alpha' : function(v){
30931             return alpha.test(v);
30932         },
30933         /**
30934          * The error text to display when the alpha validation function returns false
30935          * @type String
30936          */
30937         'alphaText' : 'This field should only contain letters and _',
30938         /**
30939          * The keystroke filter mask to be applied on alpha input
30940          * @type RegExp
30941          */
30942         'alphaMask' : /[a-z_]/i,
30943
30944         /**
30945          * The function used to validate alphanumeric values
30946          * @param {String} value The value
30947          */
30948         'alphanum' : function(v){
30949             return alphanum.test(v);
30950         },
30951         /**
30952          * The error text to display when the alphanumeric validation function returns false
30953          * @type String
30954          */
30955         'alphanumText' : 'This field should only contain letters, numbers and _',
30956         /**
30957          * The keystroke filter mask to be applied on alphanumeric input
30958          * @type RegExp
30959          */
30960         'alphanumMask' : /[a-z0-9_]/i
30961     };
30962 }();//<script type="text/javascript">
30963
30964 /**
30965  * @class Roo.form.FCKeditor
30966  * @extends Roo.form.TextArea
30967  * Wrapper around the FCKEditor http://www.fckeditor.net
30968  * @constructor
30969  * Creates a new FCKeditor
30970  * @param {Object} config Configuration options
30971  */
30972 Roo.form.FCKeditor = function(config){
30973     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30974     this.addEvents({
30975          /**
30976          * @event editorinit
30977          * Fired when the editor is initialized - you can add extra handlers here..
30978          * @param {FCKeditor} this
30979          * @param {Object} the FCK object.
30980          */
30981         editorinit : true
30982     });
30983     
30984     
30985 };
30986 Roo.form.FCKeditor.editors = { };
30987 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30988 {
30989     //defaultAutoCreate : {
30990     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30991     //},
30992     // private
30993     /**
30994      * @cfg {Object} fck options - see fck manual for details.
30995      */
30996     fckconfig : false,
30997     
30998     /**
30999      * @cfg {Object} fck toolbar set (Basic or Default)
31000      */
31001     toolbarSet : 'Basic',
31002     /**
31003      * @cfg {Object} fck BasePath
31004      */ 
31005     basePath : '/fckeditor/',
31006     
31007     
31008     frame : false,
31009     
31010     value : '',
31011     
31012    
31013     onRender : function(ct, position)
31014     {
31015         if(!this.el){
31016             this.defaultAutoCreate = {
31017                 tag: "textarea",
31018                 style:"width:300px;height:60px;",
31019                 autocomplete: "new-password"
31020             };
31021         }
31022         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31023         /*
31024         if(this.grow){
31025             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31026             if(this.preventScrollbars){
31027                 this.el.setStyle("overflow", "hidden");
31028             }
31029             this.el.setHeight(this.growMin);
31030         }
31031         */
31032         //console.log('onrender' + this.getId() );
31033         Roo.form.FCKeditor.editors[this.getId()] = this;
31034          
31035
31036         this.replaceTextarea() ;
31037         
31038     },
31039     
31040     getEditor : function() {
31041         return this.fckEditor;
31042     },
31043     /**
31044      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31045      * @param {Mixed} value The value to set
31046      */
31047     
31048     
31049     setValue : function(value)
31050     {
31051         //console.log('setValue: ' + value);
31052         
31053         if(typeof(value) == 'undefined') { // not sure why this is happending...
31054             return;
31055         }
31056         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31057         
31058         //if(!this.el || !this.getEditor()) {
31059         //    this.value = value;
31060             //this.setValue.defer(100,this,[value]);    
31061         //    return;
31062         //} 
31063         
31064         if(!this.getEditor()) {
31065             return;
31066         }
31067         
31068         this.getEditor().SetData(value);
31069         
31070         //
31071
31072     },
31073
31074     /**
31075      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31076      * @return {Mixed} value The field value
31077      */
31078     getValue : function()
31079     {
31080         
31081         if (this.frame && this.frame.dom.style.display == 'none') {
31082             return Roo.form.FCKeditor.superclass.getValue.call(this);
31083         }
31084         
31085         if(!this.el || !this.getEditor()) {
31086            
31087            // this.getValue.defer(100,this); 
31088             return this.value;
31089         }
31090        
31091         
31092         var value=this.getEditor().GetData();
31093         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31094         return Roo.form.FCKeditor.superclass.getValue.call(this);
31095         
31096
31097     },
31098
31099     /**
31100      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31101      * @return {Mixed} value The field value
31102      */
31103     getRawValue : function()
31104     {
31105         if (this.frame && this.frame.dom.style.display == 'none') {
31106             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31107         }
31108         
31109         if(!this.el || !this.getEditor()) {
31110             //this.getRawValue.defer(100,this); 
31111             return this.value;
31112             return;
31113         }
31114         
31115         
31116         
31117         var value=this.getEditor().GetData();
31118         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31119         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31120          
31121     },
31122     
31123     setSize : function(w,h) {
31124         
31125         
31126         
31127         //if (this.frame && this.frame.dom.style.display == 'none') {
31128         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31129         //    return;
31130         //}
31131         //if(!this.el || !this.getEditor()) {
31132         //    this.setSize.defer(100,this, [w,h]); 
31133         //    return;
31134         //}
31135         
31136         
31137         
31138         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31139         
31140         this.frame.dom.setAttribute('width', w);
31141         this.frame.dom.setAttribute('height', h);
31142         this.frame.setSize(w,h);
31143         
31144     },
31145     
31146     toggleSourceEdit : function(value) {
31147         
31148       
31149          
31150         this.el.dom.style.display = value ? '' : 'none';
31151         this.frame.dom.style.display = value ?  'none' : '';
31152         
31153     },
31154     
31155     
31156     focus: function(tag)
31157     {
31158         if (this.frame.dom.style.display == 'none') {
31159             return Roo.form.FCKeditor.superclass.focus.call(this);
31160         }
31161         if(!this.el || !this.getEditor()) {
31162             this.focus.defer(100,this, [tag]); 
31163             return;
31164         }
31165         
31166         
31167         
31168         
31169         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31170         this.getEditor().Focus();
31171         if (tgs.length) {
31172             if (!this.getEditor().Selection.GetSelection()) {
31173                 this.focus.defer(100,this, [tag]); 
31174                 return;
31175             }
31176             
31177             
31178             var r = this.getEditor().EditorDocument.createRange();
31179             r.setStart(tgs[0],0);
31180             r.setEnd(tgs[0],0);
31181             this.getEditor().Selection.GetSelection().removeAllRanges();
31182             this.getEditor().Selection.GetSelection().addRange(r);
31183             this.getEditor().Focus();
31184         }
31185         
31186     },
31187     
31188     
31189     
31190     replaceTextarea : function()
31191     {
31192         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31193             return ;
31194         }
31195         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31196         //{
31197             // We must check the elements firstly using the Id and then the name.
31198         var oTextarea = document.getElementById( this.getId() );
31199         
31200         var colElementsByName = document.getElementsByName( this.getId() ) ;
31201          
31202         oTextarea.style.display = 'none' ;
31203
31204         if ( oTextarea.tabIndex ) {            
31205             this.TabIndex = oTextarea.tabIndex ;
31206         }
31207         
31208         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31209         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31210         this.frame = Roo.get(this.getId() + '___Frame')
31211     },
31212     
31213     _getConfigHtml : function()
31214     {
31215         var sConfig = '' ;
31216
31217         for ( var o in this.fckconfig ) {
31218             sConfig += sConfig.length > 0  ? '&amp;' : '';
31219             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31220         }
31221
31222         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31223     },
31224     
31225     
31226     _getIFrameHtml : function()
31227     {
31228         var sFile = 'fckeditor.html' ;
31229         /* no idea what this is about..
31230         try
31231         {
31232             if ( (/fcksource=true/i).test( window.top.location.search ) )
31233                 sFile = 'fckeditor.original.html' ;
31234         }
31235         catch (e) { 
31236         */
31237
31238         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31239         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31240         
31241         
31242         var html = '<iframe id="' + this.getId() +
31243             '___Frame" src="' + sLink +
31244             '" width="' + this.width +
31245             '" height="' + this.height + '"' +
31246             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31247             ' frameborder="0" scrolling="no"></iframe>' ;
31248
31249         return html ;
31250     },
31251     
31252     _insertHtmlBefore : function( html, element )
31253     {
31254         if ( element.insertAdjacentHTML )       {
31255             // IE
31256             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31257         } else { // Gecko
31258             var oRange = document.createRange() ;
31259             oRange.setStartBefore( element ) ;
31260             var oFragment = oRange.createContextualFragment( html );
31261             element.parentNode.insertBefore( oFragment, element ) ;
31262         }
31263     }
31264     
31265     
31266   
31267     
31268     
31269     
31270     
31271
31272 });
31273
31274 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31275
31276 function FCKeditor_OnComplete(editorInstance){
31277     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31278     f.fckEditor = editorInstance;
31279     //console.log("loaded");
31280     f.fireEvent('editorinit', f, editorInstance);
31281
31282   
31283
31284  
31285
31286
31287
31288
31289
31290
31291
31292
31293
31294
31295
31296
31297
31298
31299
31300 //<script type="text/javascript">
31301 /**
31302  * @class Roo.form.GridField
31303  * @extends Roo.form.Field
31304  * Embed a grid (or editable grid into a form)
31305  * STATUS ALPHA
31306  * 
31307  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31308  * it needs 
31309  * xgrid.store = Roo.data.Store
31310  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31311  * xgrid.store.reader = Roo.data.JsonReader 
31312  * 
31313  * 
31314  * @constructor
31315  * Creates a new GridField
31316  * @param {Object} config Configuration options
31317  */
31318 Roo.form.GridField = function(config){
31319     Roo.form.GridField.superclass.constructor.call(this, config);
31320      
31321 };
31322
31323 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31324     /**
31325      * @cfg {Number} width  - used to restrict width of grid..
31326      */
31327     width : 100,
31328     /**
31329      * @cfg {Number} height - used to restrict height of grid..
31330      */
31331     height : 50,
31332      /**
31333      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31334          * 
31335          *}
31336      */
31337     xgrid : false, 
31338     /**
31339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31340      * {tag: "input", type: "checkbox", autocomplete: "off"})
31341      */
31342    // defaultAutoCreate : { tag: 'div' },
31343     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31344     /**
31345      * @cfg {String} addTitle Text to include for adding a title.
31346      */
31347     addTitle : false,
31348     //
31349     onResize : function(){
31350         Roo.form.Field.superclass.onResize.apply(this, arguments);
31351     },
31352
31353     initEvents : function(){
31354         // Roo.form.Checkbox.superclass.initEvents.call(this);
31355         // has no events...
31356        
31357     },
31358
31359
31360     getResizeEl : function(){
31361         return this.wrap;
31362     },
31363
31364     getPositionEl : function(){
31365         return this.wrap;
31366     },
31367
31368     // private
31369     onRender : function(ct, position){
31370         
31371         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31372         var style = this.style;
31373         delete this.style;
31374         
31375         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31376         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31377         this.viewEl = this.wrap.createChild({ tag: 'div' });
31378         if (style) {
31379             this.viewEl.applyStyles(style);
31380         }
31381         if (this.width) {
31382             this.viewEl.setWidth(this.width);
31383         }
31384         if (this.height) {
31385             this.viewEl.setHeight(this.height);
31386         }
31387         //if(this.inputValue !== undefined){
31388         //this.setValue(this.value);
31389         
31390         
31391         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31392         
31393         
31394         this.grid.render();
31395         this.grid.getDataSource().on('remove', this.refreshValue, this);
31396         this.grid.getDataSource().on('update', this.refreshValue, this);
31397         this.grid.on('afteredit', this.refreshValue, this);
31398  
31399     },
31400      
31401     
31402     /**
31403      * Sets the value of the item. 
31404      * @param {String} either an object  or a string..
31405      */
31406     setValue : function(v){
31407         //this.value = v;
31408         v = v || []; // empty set..
31409         // this does not seem smart - it really only affects memoryproxy grids..
31410         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31411             var ds = this.grid.getDataSource();
31412             // assumes a json reader..
31413             var data = {}
31414             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31415             ds.loadData( data);
31416         }
31417         // clear selection so it does not get stale.
31418         if (this.grid.sm) { 
31419             this.grid.sm.clearSelections();
31420         }
31421         
31422         Roo.form.GridField.superclass.setValue.call(this, v);
31423         this.refreshValue();
31424         // should load data in the grid really....
31425     },
31426     
31427     // private
31428     refreshValue: function() {
31429          var val = [];
31430         this.grid.getDataSource().each(function(r) {
31431             val.push(r.data);
31432         });
31433         this.el.dom.value = Roo.encode(val);
31434     }
31435     
31436      
31437     
31438     
31439 });/*
31440  * Based on:
31441  * Ext JS Library 1.1.1
31442  * Copyright(c) 2006-2007, Ext JS, LLC.
31443  *
31444  * Originally Released Under LGPL - original licence link has changed is not relivant.
31445  *
31446  * Fork - LGPL
31447  * <script type="text/javascript">
31448  */
31449 /**
31450  * @class Roo.form.DisplayField
31451  * @extends Roo.form.Field
31452  * A generic Field to display non-editable data.
31453  * @cfg {Boolean} closable (true|false) default false
31454  * @constructor
31455  * Creates a new Display Field item.
31456  * @param {Object} config Configuration options
31457  */
31458 Roo.form.DisplayField = function(config){
31459     Roo.form.DisplayField.superclass.constructor.call(this, config);
31460     
31461     this.addEvents({
31462         /**
31463          * @event close
31464          * Fires after the click the close btn
31465              * @param {Roo.form.DisplayField} this
31466              */
31467         close : true
31468     });
31469 };
31470
31471 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31472     inputType:      'hidden',
31473     allowBlank:     true,
31474     readOnly:         true,
31475     
31476  
31477     /**
31478      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31479      */
31480     focusClass : undefined,
31481     /**
31482      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31483      */
31484     fieldClass: 'x-form-field',
31485     
31486      /**
31487      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31488      */
31489     valueRenderer: undefined,
31490     
31491     width: 100,
31492     /**
31493      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31494      * {tag: "input", type: "checkbox", autocomplete: "off"})
31495      */
31496      
31497  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31498  
31499     closable : false,
31500     
31501     onResize : function(){
31502         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31503         
31504     },
31505
31506     initEvents : function(){
31507         // Roo.form.Checkbox.superclass.initEvents.call(this);
31508         // has no events...
31509         
31510         if(this.closable){
31511             this.closeEl.on('click', this.onClose, this);
31512         }
31513        
31514     },
31515
31516
31517     getResizeEl : function(){
31518         return this.wrap;
31519     },
31520
31521     getPositionEl : function(){
31522         return this.wrap;
31523     },
31524
31525     // private
31526     onRender : function(ct, position){
31527         
31528         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31529         //if(this.inputValue !== undefined){
31530         this.wrap = this.el.wrap();
31531         
31532         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31533         
31534         if(this.closable){
31535             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31536         }
31537         
31538         if (this.bodyStyle) {
31539             this.viewEl.applyStyles(this.bodyStyle);
31540         }
31541         //this.viewEl.setStyle('padding', '2px');
31542         
31543         this.setValue(this.value);
31544         
31545     },
31546 /*
31547     // private
31548     initValue : Roo.emptyFn,
31549
31550   */
31551
31552         // private
31553     onClick : function(){
31554         
31555     },
31556
31557     /**
31558      * Sets the checked state of the checkbox.
31559      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31560      */
31561     setValue : function(v){
31562         this.value = v;
31563         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31564         // this might be called before we have a dom element..
31565         if (!this.viewEl) {
31566             return;
31567         }
31568         this.viewEl.dom.innerHTML = html;
31569         Roo.form.DisplayField.superclass.setValue.call(this, v);
31570
31571     },
31572     
31573     onClose : function(e)
31574     {
31575         e.preventDefault();
31576         
31577         this.fireEvent('close', this);
31578     }
31579 });/*
31580  * 
31581  * Licence- LGPL
31582  * 
31583  */
31584
31585 /**
31586  * @class Roo.form.DayPicker
31587  * @extends Roo.form.Field
31588  * A Day picker show [M] [T] [W] ....
31589  * @constructor
31590  * Creates a new Day Picker
31591  * @param {Object} config Configuration options
31592  */
31593 Roo.form.DayPicker= function(config){
31594     Roo.form.DayPicker.superclass.constructor.call(this, config);
31595      
31596 };
31597
31598 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31599     /**
31600      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31601      */
31602     focusClass : undefined,
31603     /**
31604      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31605      */
31606     fieldClass: "x-form-field",
31607    
31608     /**
31609      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31610      * {tag: "input", type: "checkbox", autocomplete: "off"})
31611      */
31612     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31613     
31614    
31615     actionMode : 'viewEl', 
31616     //
31617     // private
31618  
31619     inputType : 'hidden',
31620     
31621      
31622     inputElement: false, // real input element?
31623     basedOn: false, // ????
31624     
31625     isFormField: true, // not sure where this is needed!!!!
31626
31627     onResize : function(){
31628         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31629         if(!this.boxLabel){
31630             this.el.alignTo(this.wrap, 'c-c');
31631         }
31632     },
31633
31634     initEvents : function(){
31635         Roo.form.Checkbox.superclass.initEvents.call(this);
31636         this.el.on("click", this.onClick,  this);
31637         this.el.on("change", this.onClick,  this);
31638     },
31639
31640
31641     getResizeEl : function(){
31642         return this.wrap;
31643     },
31644
31645     getPositionEl : function(){
31646         return this.wrap;
31647     },
31648
31649     
31650     // private
31651     onRender : function(ct, position){
31652         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31653        
31654         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31655         
31656         var r1 = '<table><tr>';
31657         var r2 = '<tr class="x-form-daypick-icons">';
31658         for (var i=0; i < 7; i++) {
31659             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31660             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31661         }
31662         
31663         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31664         viewEl.select('img').on('click', this.onClick, this);
31665         this.viewEl = viewEl;   
31666         
31667         
31668         // this will not work on Chrome!!!
31669         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31670         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31671         
31672         
31673           
31674
31675     },
31676
31677     // private
31678     initValue : Roo.emptyFn,
31679
31680     /**
31681      * Returns the checked state of the checkbox.
31682      * @return {Boolean} True if checked, else false
31683      */
31684     getValue : function(){
31685         return this.el.dom.value;
31686         
31687     },
31688
31689         // private
31690     onClick : function(e){ 
31691         //this.setChecked(!this.checked);
31692         Roo.get(e.target).toggleClass('x-menu-item-checked');
31693         this.refreshValue();
31694         //if(this.el.dom.checked != this.checked){
31695         //    this.setValue(this.el.dom.checked);
31696        // }
31697     },
31698     
31699     // private
31700     refreshValue : function()
31701     {
31702         var val = '';
31703         this.viewEl.select('img',true).each(function(e,i,n)  {
31704             val += e.is(".x-menu-item-checked") ? String(n) : '';
31705         });
31706         this.setValue(val, true);
31707     },
31708
31709     /**
31710      * Sets the checked state of the checkbox.
31711      * On is always based on a string comparison between inputValue and the param.
31712      * @param {Boolean/String} value - the value to set 
31713      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31714      */
31715     setValue : function(v,suppressEvent){
31716         if (!this.el.dom) {
31717             return;
31718         }
31719         var old = this.el.dom.value ;
31720         this.el.dom.value = v;
31721         if (suppressEvent) {
31722             return ;
31723         }
31724          
31725         // update display..
31726         this.viewEl.select('img',true).each(function(e,i,n)  {
31727             
31728             var on = e.is(".x-menu-item-checked");
31729             var newv = v.indexOf(String(n)) > -1;
31730             if (on != newv) {
31731                 e.toggleClass('x-menu-item-checked');
31732             }
31733             
31734         });
31735         
31736         
31737         this.fireEvent('change', this, v, old);
31738         
31739         
31740     },
31741    
31742     // handle setting of hidden value by some other method!!?!?
31743     setFromHidden: function()
31744     {
31745         if(!this.el){
31746             return;
31747         }
31748         //console.log("SET FROM HIDDEN");
31749         //alert('setFrom hidden');
31750         this.setValue(this.el.dom.value);
31751     },
31752     
31753     onDestroy : function()
31754     {
31755         if(this.viewEl){
31756             Roo.get(this.viewEl).remove();
31757         }
31758          
31759         Roo.form.DayPicker.superclass.onDestroy.call(this);
31760     }
31761
31762 });/*
31763  * RooJS Library 1.1.1
31764  * Copyright(c) 2008-2011  Alan Knowles
31765  *
31766  * License - LGPL
31767  */
31768  
31769
31770 /**
31771  * @class Roo.form.ComboCheck
31772  * @extends Roo.form.ComboBox
31773  * A combobox for multiple select items.
31774  *
31775  * FIXME - could do with a reset button..
31776  * 
31777  * @constructor
31778  * Create a new ComboCheck
31779  * @param {Object} config Configuration options
31780  */
31781 Roo.form.ComboCheck = function(config){
31782     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31783     // should verify some data...
31784     // like
31785     // hiddenName = required..
31786     // displayField = required
31787     // valudField == required
31788     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31789     var _t = this;
31790     Roo.each(req, function(e) {
31791         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31792             throw "Roo.form.ComboCheck : missing value for: " + e;
31793         }
31794     });
31795     
31796     
31797 };
31798
31799 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31800      
31801      
31802     editable : false,
31803      
31804     selectedClass: 'x-menu-item-checked', 
31805     
31806     // private
31807     onRender : function(ct, position){
31808         var _t = this;
31809         
31810         
31811         
31812         if(!this.tpl){
31813             var cls = 'x-combo-list';
31814
31815             
31816             this.tpl =  new Roo.Template({
31817                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31818                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31819                    '<span>{' + this.displayField + '}</span>' +
31820                     '</div>' 
31821                 
31822             });
31823         }
31824  
31825         
31826         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31827         this.view.singleSelect = false;
31828         this.view.multiSelect = true;
31829         this.view.toggleSelect = true;
31830         this.pageTb.add(new Roo.Toolbar.Fill(), {
31831             
31832             text: 'Done',
31833             handler: function()
31834             {
31835                 _t.collapse();
31836             }
31837         });
31838     },
31839     
31840     onViewOver : function(e, t){
31841         // do nothing...
31842         return;
31843         
31844     },
31845     
31846     onViewClick : function(doFocus,index){
31847         return;
31848         
31849     },
31850     select: function () {
31851         //Roo.log("SELECT CALLED");
31852     },
31853      
31854     selectByValue : function(xv, scrollIntoView){
31855         var ar = this.getValueArray();
31856         var sels = [];
31857         
31858         Roo.each(ar, function(v) {
31859             if(v === undefined || v === null){
31860                 return;
31861             }
31862             var r = this.findRecord(this.valueField, v);
31863             if(r){
31864                 sels.push(this.store.indexOf(r))
31865                 
31866             }
31867         },this);
31868         this.view.select(sels);
31869         return false;
31870     },
31871     
31872     
31873     
31874     onSelect : function(record, index){
31875        // Roo.log("onselect Called");
31876        // this is only called by the clear button now..
31877         this.view.clearSelections();
31878         this.setValue('[]');
31879         if (this.value != this.valueBefore) {
31880             this.fireEvent('change', this, this.value, this.valueBefore);
31881             this.valueBefore = this.value;
31882         }
31883     },
31884     getValueArray : function()
31885     {
31886         var ar = [] ;
31887         
31888         try {
31889             //Roo.log(this.value);
31890             if (typeof(this.value) == 'undefined') {
31891                 return [];
31892             }
31893             var ar = Roo.decode(this.value);
31894             return  ar instanceof Array ? ar : []; //?? valid?
31895             
31896         } catch(e) {
31897             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31898             return [];
31899         }
31900          
31901     },
31902     expand : function ()
31903     {
31904         
31905         Roo.form.ComboCheck.superclass.expand.call(this);
31906         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31907         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31908         
31909
31910     },
31911     
31912     collapse : function(){
31913         Roo.form.ComboCheck.superclass.collapse.call(this);
31914         var sl = this.view.getSelectedIndexes();
31915         var st = this.store;
31916         var nv = [];
31917         var tv = [];
31918         var r;
31919         Roo.each(sl, function(i) {
31920             r = st.getAt(i);
31921             nv.push(r.get(this.valueField));
31922         },this);
31923         this.setValue(Roo.encode(nv));
31924         if (this.value != this.valueBefore) {
31925
31926             this.fireEvent('change', this, this.value, this.valueBefore);
31927             this.valueBefore = this.value;
31928         }
31929         
31930     },
31931     
31932     setValue : function(v){
31933         // Roo.log(v);
31934         this.value = v;
31935         
31936         var vals = this.getValueArray();
31937         var tv = [];
31938         Roo.each(vals, function(k) {
31939             var r = this.findRecord(this.valueField, k);
31940             if(r){
31941                 tv.push(r.data[this.displayField]);
31942             }else if(this.valueNotFoundText !== undefined){
31943                 tv.push( this.valueNotFoundText );
31944             }
31945         },this);
31946        // Roo.log(tv);
31947         
31948         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31949         this.hiddenField.value = v;
31950         this.value = v;
31951     }
31952     
31953 });/*
31954  * Based on:
31955  * Ext JS Library 1.1.1
31956  * Copyright(c) 2006-2007, Ext JS, LLC.
31957  *
31958  * Originally Released Under LGPL - original licence link has changed is not relivant.
31959  *
31960  * Fork - LGPL
31961  * <script type="text/javascript">
31962  */
31963  
31964 /**
31965  * @class Roo.form.Signature
31966  * @extends Roo.form.Field
31967  * Signature field.  
31968  * @constructor
31969  * 
31970  * @param {Object} config Configuration options
31971  */
31972
31973 Roo.form.Signature = function(config){
31974     Roo.form.Signature.superclass.constructor.call(this, config);
31975     
31976     this.addEvents({// not in used??
31977          /**
31978          * @event confirm
31979          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31980              * @param {Roo.form.Signature} combo This combo box
31981              */
31982         'confirm' : true,
31983         /**
31984          * @event reset
31985          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31986              * @param {Roo.form.ComboBox} combo This combo box
31987              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31988              */
31989         'reset' : true
31990     });
31991 };
31992
31993 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31994     /**
31995      * @cfg {Object} labels Label to use when rendering a form.
31996      * defaults to 
31997      * labels : { 
31998      *      clear : "Clear",
31999      *      confirm : "Confirm"
32000      *  }
32001      */
32002     labels : { 
32003         clear : "Clear",
32004         confirm : "Confirm"
32005     },
32006     /**
32007      * @cfg {Number} width The signature panel width (defaults to 300)
32008      */
32009     width: 300,
32010     /**
32011      * @cfg {Number} height The signature panel height (defaults to 100)
32012      */
32013     height : 100,
32014     /**
32015      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32016      */
32017     allowBlank : false,
32018     
32019     //private
32020     // {Object} signPanel The signature SVG panel element (defaults to {})
32021     signPanel : {},
32022     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32023     isMouseDown : false,
32024     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32025     isConfirmed : false,
32026     // {String} signatureTmp SVG mapping string (defaults to empty string)
32027     signatureTmp : '',
32028     
32029     
32030     defaultAutoCreate : { // modified by initCompnoent..
32031         tag: "input",
32032         type:"hidden"
32033     },
32034
32035     // private
32036     onRender : function(ct, position){
32037         
32038         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32039         
32040         this.wrap = this.el.wrap({
32041             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32042         });
32043         
32044         this.createToolbar(this);
32045         this.signPanel = this.wrap.createChild({
32046                 tag: 'div',
32047                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32048             }, this.el
32049         );
32050             
32051         this.svgID = Roo.id();
32052         this.svgEl = this.signPanel.createChild({
32053               xmlns : 'http://www.w3.org/2000/svg',
32054               tag : 'svg',
32055               id : this.svgID + "-svg",
32056               width: this.width,
32057               height: this.height,
32058               viewBox: '0 0 '+this.width+' '+this.height,
32059               cn : [
32060                 {
32061                     tag: "rect",
32062                     id: this.svgID + "-svg-r",
32063                     width: this.width,
32064                     height: this.height,
32065                     fill: "#ffa"
32066                 },
32067                 {
32068                     tag: "line",
32069                     id: this.svgID + "-svg-l",
32070                     x1: "0", // start
32071                     y1: (this.height*0.8), // start set the line in 80% of height
32072                     x2: this.width, // end
32073                     y2: (this.height*0.8), // end set the line in 80% of height
32074                     'stroke': "#666",
32075                     'stroke-width': "1",
32076                     'stroke-dasharray': "3",
32077                     'shape-rendering': "crispEdges",
32078                     'pointer-events': "none"
32079                 },
32080                 {
32081                     tag: "path",
32082                     id: this.svgID + "-svg-p",
32083                     'stroke': "navy",
32084                     'stroke-width': "3",
32085                     'fill': "none",
32086                     'pointer-events': 'none'
32087                 }
32088               ]
32089         });
32090         this.createSVG();
32091         this.svgBox = this.svgEl.dom.getScreenCTM();
32092     },
32093     createSVG : function(){ 
32094         var svg = this.signPanel;
32095         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32096         var t = this;
32097
32098         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32099         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32100         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32101         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32102         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32103         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32104         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32105         
32106     },
32107     isTouchEvent : function(e){
32108         return e.type.match(/^touch/);
32109     },
32110     getCoords : function (e) {
32111         var pt    = this.svgEl.dom.createSVGPoint();
32112         pt.x = e.clientX; 
32113         pt.y = e.clientY;
32114         if (this.isTouchEvent(e)) {
32115             pt.x =  e.targetTouches[0].clientX;
32116             pt.y = e.targetTouches[0].clientY;
32117         }
32118         var a = this.svgEl.dom.getScreenCTM();
32119         var b = a.inverse();
32120         var mx = pt.matrixTransform(b);
32121         return mx.x + ',' + mx.y;
32122     },
32123     //mouse event headler 
32124     down : function (e) {
32125         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32126         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32127         
32128         this.isMouseDown = true;
32129         
32130         e.preventDefault();
32131     },
32132     move : function (e) {
32133         if (this.isMouseDown) {
32134             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32135             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32136         }
32137         
32138         e.preventDefault();
32139     },
32140     up : function (e) {
32141         this.isMouseDown = false;
32142         var sp = this.signatureTmp.split(' ');
32143         
32144         if(sp.length > 1){
32145             if(!sp[sp.length-2].match(/^L/)){
32146                 sp.pop();
32147                 sp.pop();
32148                 sp.push("");
32149                 this.signatureTmp = sp.join(" ");
32150             }
32151         }
32152         if(this.getValue() != this.signatureTmp){
32153             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32154             this.isConfirmed = false;
32155         }
32156         e.preventDefault();
32157     },
32158     
32159     /**
32160      * Protected method that will not generally be called directly. It
32161      * is called when the editor creates its toolbar. Override this method if you need to
32162      * add custom toolbar buttons.
32163      * @param {HtmlEditor} editor
32164      */
32165     createToolbar : function(editor){
32166          function btn(id, toggle, handler){
32167             var xid = fid + '-'+ id ;
32168             return {
32169                 id : xid,
32170                 cmd : id,
32171                 cls : 'x-btn-icon x-edit-'+id,
32172                 enableToggle:toggle !== false,
32173                 scope: editor, // was editor...
32174                 handler:handler||editor.relayBtnCmd,
32175                 clickEvent:'mousedown',
32176                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32177                 tabIndex:-1
32178             };
32179         }
32180         
32181         
32182         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32183         this.tb = tb;
32184         this.tb.add(
32185            {
32186                 cls : ' x-signature-btn x-signature-'+id,
32187                 scope: editor, // was editor...
32188                 handler: this.reset,
32189                 clickEvent:'mousedown',
32190                 text: this.labels.clear
32191             },
32192             {
32193                  xtype : 'Fill',
32194                  xns: Roo.Toolbar
32195             }, 
32196             {
32197                 cls : '  x-signature-btn x-signature-'+id,
32198                 scope: editor, // was editor...
32199                 handler: this.confirmHandler,
32200                 clickEvent:'mousedown',
32201                 text: this.labels.confirm
32202             }
32203         );
32204     
32205     },
32206     //public
32207     /**
32208      * when user is clicked confirm then show this image.....
32209      * 
32210      * @return {String} Image Data URI
32211      */
32212     getImageDataURI : function(){
32213         var svg = this.svgEl.dom.parentNode.innerHTML;
32214         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32215         return src; 
32216     },
32217     /**
32218      * 
32219      * @return {Boolean} this.isConfirmed
32220      */
32221     getConfirmed : function(){
32222         return this.isConfirmed;
32223     },
32224     /**
32225      * 
32226      * @return {Number} this.width
32227      */
32228     getWidth : function(){
32229         return this.width;
32230     },
32231     /**
32232      * 
32233      * @return {Number} this.height
32234      */
32235     getHeight : function(){
32236         return this.height;
32237     },
32238     // private
32239     getSignature : function(){
32240         return this.signatureTmp;
32241     },
32242     // private
32243     reset : function(){
32244         this.signatureTmp = '';
32245         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32246         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32247         this.isConfirmed = false;
32248         Roo.form.Signature.superclass.reset.call(this);
32249     },
32250     setSignature : function(s){
32251         this.signatureTmp = s;
32252         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32253         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32254         this.setValue(s);
32255         this.isConfirmed = false;
32256         Roo.form.Signature.superclass.reset.call(this);
32257     }, 
32258     test : function(){
32259 //        Roo.log(this.signPanel.dom.contentWindow.up())
32260     },
32261     //private
32262     setConfirmed : function(){
32263         
32264         
32265         
32266 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32267     },
32268     // private
32269     confirmHandler : function(){
32270         if(!this.getSignature()){
32271             return;
32272         }
32273         
32274         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32275         this.setValue(this.getSignature());
32276         this.isConfirmed = true;
32277         
32278         this.fireEvent('confirm', this);
32279     },
32280     // private
32281     // Subclasses should provide the validation implementation by overriding this
32282     validateValue : function(value){
32283         if(this.allowBlank){
32284             return true;
32285         }
32286         
32287         if(this.isConfirmed){
32288             return true;
32289         }
32290         return false;
32291     }
32292 });/*
32293  * Based on:
32294  * Ext JS Library 1.1.1
32295  * Copyright(c) 2006-2007, Ext JS, LLC.
32296  *
32297  * Originally Released Under LGPL - original licence link has changed is not relivant.
32298  *
32299  * Fork - LGPL
32300  * <script type="text/javascript">
32301  */
32302  
32303
32304 /**
32305  * @class Roo.form.ComboBox
32306  * @extends Roo.form.TriggerField
32307  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32308  * @constructor
32309  * Create a new ComboBox.
32310  * @param {Object} config Configuration options
32311  */
32312 Roo.form.Select = function(config){
32313     Roo.form.Select.superclass.constructor.call(this, config);
32314      
32315 };
32316
32317 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32318     /**
32319      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32320      */
32321     /**
32322      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32323      * rendering into an Roo.Editor, defaults to false)
32324      */
32325     /**
32326      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32327      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32328      */
32329     /**
32330      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32331      */
32332     /**
32333      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32334      * the dropdown list (defaults to undefined, with no header element)
32335      */
32336
32337      /**
32338      * @cfg {String/Roo.Template} tpl The template to use to render the output
32339      */
32340      
32341     // private
32342     defaultAutoCreate : {tag: "select"  },
32343     /**
32344      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32345      */
32346     listWidth: undefined,
32347     /**
32348      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32349      * mode = 'remote' or 'text' if mode = 'local')
32350      */
32351     displayField: undefined,
32352     /**
32353      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32354      * mode = 'remote' or 'value' if mode = 'local'). 
32355      * Note: use of a valueField requires the user make a selection
32356      * in order for a value to be mapped.
32357      */
32358     valueField: undefined,
32359     
32360     
32361     /**
32362      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32363      * field's data value (defaults to the underlying DOM element's name)
32364      */
32365     hiddenName: undefined,
32366     /**
32367      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32368      */
32369     listClass: '',
32370     /**
32371      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32372      */
32373     selectedClass: 'x-combo-selected',
32374     /**
32375      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32376      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32377      * which displays a downward arrow icon).
32378      */
32379     triggerClass : 'x-form-arrow-trigger',
32380     /**
32381      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32382      */
32383     shadow:'sides',
32384     /**
32385      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32386      * anchor positions (defaults to 'tl-bl')
32387      */
32388     listAlign: 'tl-bl?',
32389     /**
32390      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32391      */
32392     maxHeight: 300,
32393     /**
32394      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32395      * query specified by the allQuery config option (defaults to 'query')
32396      */
32397     triggerAction: 'query',
32398     /**
32399      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32400      * (defaults to 4, does not apply if editable = false)
32401      */
32402     minChars : 4,
32403     /**
32404      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32405      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32406      */
32407     typeAhead: false,
32408     /**
32409      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32410      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32411      */
32412     queryDelay: 500,
32413     /**
32414      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32415      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32416      */
32417     pageSize: 0,
32418     /**
32419      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32420      * when editable = true (defaults to false)
32421      */
32422     selectOnFocus:false,
32423     /**
32424      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32425      */
32426     queryParam: 'query',
32427     /**
32428      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32429      * when mode = 'remote' (defaults to 'Loading...')
32430      */
32431     loadingText: 'Loading...',
32432     /**
32433      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32434      */
32435     resizable: false,
32436     /**
32437      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32438      */
32439     handleHeight : 8,
32440     /**
32441      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32442      * traditional select (defaults to true)
32443      */
32444     editable: true,
32445     /**
32446      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32447      */
32448     allQuery: '',
32449     /**
32450      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32451      */
32452     mode: 'remote',
32453     /**
32454      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32455      * listWidth has a higher value)
32456      */
32457     minListWidth : 70,
32458     /**
32459      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32460      * allow the user to set arbitrary text into the field (defaults to false)
32461      */
32462     forceSelection:false,
32463     /**
32464      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32465      * if typeAhead = true (defaults to 250)
32466      */
32467     typeAheadDelay : 250,
32468     /**
32469      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32470      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32471      */
32472     valueNotFoundText : undefined,
32473     
32474     /**
32475      * @cfg {String} defaultValue The value displayed after loading the store.
32476      */
32477     defaultValue: '',
32478     
32479     /**
32480      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32481      */
32482     blockFocus : false,
32483     
32484     /**
32485      * @cfg {Boolean} disableClear Disable showing of clear button.
32486      */
32487     disableClear : false,
32488     /**
32489      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32490      */
32491     alwaysQuery : false,
32492     
32493     //private
32494     addicon : false,
32495     editicon: false,
32496     
32497     // element that contains real text value.. (when hidden is used..)
32498      
32499     // private
32500     onRender : function(ct, position){
32501         Roo.form.Field.prototype.onRender.call(this, ct, position);
32502         
32503         if(this.store){
32504             this.store.on('beforeload', this.onBeforeLoad, this);
32505             this.store.on('load', this.onLoad, this);
32506             this.store.on('loadexception', this.onLoadException, this);
32507             this.store.load({});
32508         }
32509         
32510         
32511         
32512     },
32513
32514     // private
32515     initEvents : function(){
32516         //Roo.form.ComboBox.superclass.initEvents.call(this);
32517  
32518     },
32519
32520     onDestroy : function(){
32521        
32522         if(this.store){
32523             this.store.un('beforeload', this.onBeforeLoad, this);
32524             this.store.un('load', this.onLoad, this);
32525             this.store.un('loadexception', this.onLoadException, this);
32526         }
32527         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32528     },
32529
32530     // private
32531     fireKey : function(e){
32532         if(e.isNavKeyPress() && !this.list.isVisible()){
32533             this.fireEvent("specialkey", this, e);
32534         }
32535     },
32536
32537     // private
32538     onResize: function(w, h){
32539         
32540         return; 
32541     
32542         
32543     },
32544
32545     /**
32546      * Allow or prevent the user from directly editing the field text.  If false is passed,
32547      * the user will only be able to select from the items defined in the dropdown list.  This method
32548      * is the runtime equivalent of setting the 'editable' config option at config time.
32549      * @param {Boolean} value True to allow the user to directly edit the field text
32550      */
32551     setEditable : function(value){
32552          
32553     },
32554
32555     // private
32556     onBeforeLoad : function(){
32557         
32558         Roo.log("Select before load");
32559         return;
32560     
32561         this.innerList.update(this.loadingText ?
32562                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32563         //this.restrictHeight();
32564         this.selectedIndex = -1;
32565     },
32566
32567     // private
32568     onLoad : function(){
32569
32570     
32571         var dom = this.el.dom;
32572         dom.innerHTML = '';
32573          var od = dom.ownerDocument;
32574          
32575         if (this.emptyText) {
32576             var op = od.createElement('option');
32577             op.setAttribute('value', '');
32578             op.innerHTML = String.format('{0}', this.emptyText);
32579             dom.appendChild(op);
32580         }
32581         if(this.store.getCount() > 0){
32582            
32583             var vf = this.valueField;
32584             var df = this.displayField;
32585             this.store.data.each(function(r) {
32586                 // which colmsn to use... testing - cdoe / title..
32587                 var op = od.createElement('option');
32588                 op.setAttribute('value', r.data[vf]);
32589                 op.innerHTML = String.format('{0}', r.data[df]);
32590                 dom.appendChild(op);
32591             });
32592             if (typeof(this.defaultValue != 'undefined')) {
32593                 this.setValue(this.defaultValue);
32594             }
32595             
32596              
32597         }else{
32598             //this.onEmptyResults();
32599         }
32600         //this.el.focus();
32601     },
32602     // private
32603     onLoadException : function()
32604     {
32605         dom.innerHTML = '';
32606             
32607         Roo.log("Select on load exception");
32608         return;
32609     
32610         this.collapse();
32611         Roo.log(this.store.reader.jsonData);
32612         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32613             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32614         }
32615         
32616         
32617     },
32618     // private
32619     onTypeAhead : function(){
32620          
32621     },
32622
32623     // private
32624     onSelect : function(record, index){
32625         Roo.log('on select?');
32626         return;
32627         if(this.fireEvent('beforeselect', this, record, index) !== false){
32628             this.setFromData(index > -1 ? record.data : false);
32629             this.collapse();
32630             this.fireEvent('select', this, record, index);
32631         }
32632     },
32633
32634     /**
32635      * Returns the currently selected field value or empty string if no value is set.
32636      * @return {String} value The selected value
32637      */
32638     getValue : function(){
32639         var dom = this.el.dom;
32640         this.value = dom.options[dom.selectedIndex].value;
32641         return this.value;
32642         
32643     },
32644
32645     /**
32646      * Clears any text/value currently set in the field
32647      */
32648     clearValue : function(){
32649         this.value = '';
32650         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32651         
32652     },
32653
32654     /**
32655      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32656      * will be displayed in the field.  If the value does not match the data value of an existing item,
32657      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32658      * Otherwise the field will be blank (although the value will still be set).
32659      * @param {String} value The value to match
32660      */
32661     setValue : function(v){
32662         var d = this.el.dom;
32663         for (var i =0; i < d.options.length;i++) {
32664             if (v == d.options[i].value) {
32665                 d.selectedIndex = i;
32666                 this.value = v;
32667                 return;
32668             }
32669         }
32670         this.clearValue();
32671     },
32672     /**
32673      * @property {Object} the last set data for the element
32674      */
32675     
32676     lastData : false,
32677     /**
32678      * Sets the value of the field based on a object which is related to the record format for the store.
32679      * @param {Object} value the value to set as. or false on reset?
32680      */
32681     setFromData : function(o){
32682         Roo.log('setfrom data?');
32683          
32684         
32685         
32686     },
32687     // private
32688     reset : function(){
32689         this.clearValue();
32690     },
32691     // private
32692     findRecord : function(prop, value){
32693         
32694         return false;
32695     
32696         var record;
32697         if(this.store.getCount() > 0){
32698             this.store.each(function(r){
32699                 if(r.data[prop] == value){
32700                     record = r;
32701                     return false;
32702                 }
32703                 return true;
32704             });
32705         }
32706         return record;
32707     },
32708     
32709     getName: function()
32710     {
32711         // returns hidden if it's set..
32712         if (!this.rendered) {return ''};
32713         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32714         
32715     },
32716      
32717
32718     
32719
32720     // private
32721     onEmptyResults : function(){
32722         Roo.log('empty results');
32723         //this.collapse();
32724     },
32725
32726     /**
32727      * Returns true if the dropdown list is expanded, else false.
32728      */
32729     isExpanded : function(){
32730         return false;
32731     },
32732
32733     /**
32734      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32735      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32736      * @param {String} value The data value of the item to select
32737      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32738      * selected item if it is not currently in view (defaults to true)
32739      * @return {Boolean} True if the value matched an item in the list, else false
32740      */
32741     selectByValue : function(v, scrollIntoView){
32742         Roo.log('select By Value');
32743         return false;
32744     
32745         if(v !== undefined && v !== null){
32746             var r = this.findRecord(this.valueField || this.displayField, v);
32747             if(r){
32748                 this.select(this.store.indexOf(r), scrollIntoView);
32749                 return true;
32750             }
32751         }
32752         return false;
32753     },
32754
32755     /**
32756      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32757      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32758      * @param {Number} index The zero-based index of the list item to select
32759      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32760      * selected item if it is not currently in view (defaults to true)
32761      */
32762     select : function(index, scrollIntoView){
32763         Roo.log('select ');
32764         return  ;
32765         
32766         this.selectedIndex = index;
32767         this.view.select(index);
32768         if(scrollIntoView !== false){
32769             var el = this.view.getNode(index);
32770             if(el){
32771                 this.innerList.scrollChildIntoView(el, false);
32772             }
32773         }
32774     },
32775
32776       
32777
32778     // private
32779     validateBlur : function(){
32780         
32781         return;
32782         
32783     },
32784
32785     // private
32786     initQuery : function(){
32787         this.doQuery(this.getRawValue());
32788     },
32789
32790     // private
32791     doForce : function(){
32792         if(this.el.dom.value.length > 0){
32793             this.el.dom.value =
32794                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32795              
32796         }
32797     },
32798
32799     /**
32800      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32801      * query allowing the query action to be canceled if needed.
32802      * @param {String} query The SQL query to execute
32803      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32804      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32805      * saved in the current store (defaults to false)
32806      */
32807     doQuery : function(q, forceAll){
32808         
32809         Roo.log('doQuery?');
32810         if(q === undefined || q === null){
32811             q = '';
32812         }
32813         var qe = {
32814             query: q,
32815             forceAll: forceAll,
32816             combo: this,
32817             cancel:false
32818         };
32819         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32820             return false;
32821         }
32822         q = qe.query;
32823         forceAll = qe.forceAll;
32824         if(forceAll === true || (q.length >= this.minChars)){
32825             if(this.lastQuery != q || this.alwaysQuery){
32826                 this.lastQuery = q;
32827                 if(this.mode == 'local'){
32828                     this.selectedIndex = -1;
32829                     if(forceAll){
32830                         this.store.clearFilter();
32831                     }else{
32832                         this.store.filter(this.displayField, q);
32833                     }
32834                     this.onLoad();
32835                 }else{
32836                     this.store.baseParams[this.queryParam] = q;
32837                     this.store.load({
32838                         params: this.getParams(q)
32839                     });
32840                     this.expand();
32841                 }
32842             }else{
32843                 this.selectedIndex = -1;
32844                 this.onLoad();   
32845             }
32846         }
32847     },
32848
32849     // private
32850     getParams : function(q){
32851         var p = {};
32852         //p[this.queryParam] = q;
32853         if(this.pageSize){
32854             p.start = 0;
32855             p.limit = this.pageSize;
32856         }
32857         return p;
32858     },
32859
32860     /**
32861      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32862      */
32863     collapse : function(){
32864         
32865     },
32866
32867     // private
32868     collapseIf : function(e){
32869         
32870     },
32871
32872     /**
32873      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32874      */
32875     expand : function(){
32876         
32877     } ,
32878
32879     // private
32880      
32881
32882     /** 
32883     * @cfg {Boolean} grow 
32884     * @hide 
32885     */
32886     /** 
32887     * @cfg {Number} growMin 
32888     * @hide 
32889     */
32890     /** 
32891     * @cfg {Number} growMax 
32892     * @hide 
32893     */
32894     /**
32895      * @hide
32896      * @method autoSize
32897      */
32898     
32899     setWidth : function()
32900     {
32901         
32902     },
32903     getResizeEl : function(){
32904         return this.el;
32905     }
32906 });//<script type="text/javasscript">
32907  
32908
32909 /**
32910  * @class Roo.DDView
32911  * A DnD enabled version of Roo.View.
32912  * @param {Element/String} container The Element in which to create the View.
32913  * @param {String} tpl The template string used to create the markup for each element of the View
32914  * @param {Object} config The configuration properties. These include all the config options of
32915  * {@link Roo.View} plus some specific to this class.<br>
32916  * <p>
32917  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32918  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32919  * <p>
32920  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32921 .x-view-drag-insert-above {
32922         border-top:1px dotted #3366cc;
32923 }
32924 .x-view-drag-insert-below {
32925         border-bottom:1px dotted #3366cc;
32926 }
32927 </code></pre>
32928  * 
32929  */
32930  
32931 Roo.DDView = function(container, tpl, config) {
32932     Roo.DDView.superclass.constructor.apply(this, arguments);
32933     this.getEl().setStyle("outline", "0px none");
32934     this.getEl().unselectable();
32935     if (this.dragGroup) {
32936         this.setDraggable(this.dragGroup.split(","));
32937     }
32938     if (this.dropGroup) {
32939         this.setDroppable(this.dropGroup.split(","));
32940     }
32941     if (this.deletable) {
32942         this.setDeletable();
32943     }
32944     this.isDirtyFlag = false;
32945         this.addEvents({
32946                 "drop" : true
32947         });
32948 };
32949
32950 Roo.extend(Roo.DDView, Roo.View, {
32951 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32952 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32953 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32954 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32955
32956         isFormField: true,
32957
32958         reset: Roo.emptyFn,
32959         
32960         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32961
32962         validate: function() {
32963                 return true;
32964         },
32965         
32966         destroy: function() {
32967                 this.purgeListeners();
32968                 this.getEl.removeAllListeners();
32969                 this.getEl().remove();
32970                 if (this.dragZone) {
32971                         if (this.dragZone.destroy) {
32972                                 this.dragZone.destroy();
32973                         }
32974                 }
32975                 if (this.dropZone) {
32976                         if (this.dropZone.destroy) {
32977                                 this.dropZone.destroy();
32978                         }
32979                 }
32980         },
32981
32982 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32983         getName: function() {
32984                 return this.name;
32985         },
32986
32987 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32988         setValue: function(v) {
32989                 if (!this.store) {
32990                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32991                 }
32992                 var data = {};
32993                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32994                 this.store.proxy = new Roo.data.MemoryProxy(data);
32995                 this.store.load();
32996         },
32997
32998 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32999         getValue: function() {
33000                 var result = '(';
33001                 this.store.each(function(rec) {
33002                         result += rec.id + ',';
33003                 });
33004                 return result.substr(0, result.length - 1) + ')';
33005         },
33006         
33007         getIds: function() {
33008                 var i = 0, result = new Array(this.store.getCount());
33009                 this.store.each(function(rec) {
33010                         result[i++] = rec.id;
33011                 });
33012                 return result;
33013         },
33014         
33015         isDirty: function() {
33016                 return this.isDirtyFlag;
33017         },
33018
33019 /**
33020  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33021  *      whole Element becomes the target, and this causes the drop gesture to append.
33022  */
33023     getTargetFromEvent : function(e) {
33024                 var target = e.getTarget();
33025                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33026                 target = target.parentNode;
33027                 }
33028                 if (!target) {
33029                         target = this.el.dom.lastChild || this.el.dom;
33030                 }
33031                 return target;
33032     },
33033
33034 /**
33035  *      Create the drag data which consists of an object which has the property "ddel" as
33036  *      the drag proxy element. 
33037  */
33038     getDragData : function(e) {
33039         var target = this.findItemFromChild(e.getTarget());
33040                 if(target) {
33041                         this.handleSelection(e);
33042                         var selNodes = this.getSelectedNodes();
33043             var dragData = {
33044                 source: this,
33045                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33046                 nodes: selNodes,
33047                 records: []
33048                         };
33049                         var selectedIndices = this.getSelectedIndexes();
33050                         for (var i = 0; i < selectedIndices.length; i++) {
33051                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33052                         }
33053                         if (selNodes.length == 1) {
33054                                 dragData.ddel = target.cloneNode(true); // the div element
33055                         } else {
33056                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33057                                 div.className = 'multi-proxy';
33058                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33059                                         div.appendChild(selNodes[i].cloneNode(true));
33060                                 }
33061                                 dragData.ddel = div;
33062                         }
33063             //console.log(dragData)
33064             //console.log(dragData.ddel.innerHTML)
33065                         return dragData;
33066                 }
33067         //console.log('nodragData')
33068                 return false;
33069     },
33070     
33071 /**     Specify to which ddGroup items in this DDView may be dragged. */
33072     setDraggable: function(ddGroup) {
33073         if (ddGroup instanceof Array) {
33074                 Roo.each(ddGroup, this.setDraggable, this);
33075                 return;
33076         }
33077         if (this.dragZone) {
33078                 this.dragZone.addToGroup(ddGroup);
33079         } else {
33080                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33081                                 containerScroll: true,
33082                                 ddGroup: ddGroup 
33083
33084                         });
33085 //                      Draggability implies selection. DragZone's mousedown selects the element.
33086                         if (!this.multiSelect) { this.singleSelect = true; }
33087
33088 //                      Wire the DragZone's handlers up to methods in *this*
33089                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33090                 }
33091     },
33092
33093 /**     Specify from which ddGroup this DDView accepts drops. */
33094     setDroppable: function(ddGroup) {
33095         if (ddGroup instanceof Array) {
33096                 Roo.each(ddGroup, this.setDroppable, this);
33097                 return;
33098         }
33099         if (this.dropZone) {
33100                 this.dropZone.addToGroup(ddGroup);
33101         } else {
33102                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33103                                 containerScroll: true,
33104                                 ddGroup: ddGroup
33105                         });
33106
33107 //                      Wire the DropZone's handlers up to methods in *this*
33108                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33109                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33110                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33111                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33112                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33113                 }
33114     },
33115
33116 /**     Decide whether to drop above or below a View node. */
33117     getDropPoint : function(e, n, dd){
33118         if (n == this.el.dom) { return "above"; }
33119                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33120                 var c = t + (b - t) / 2;
33121                 var y = Roo.lib.Event.getPageY(e);
33122                 if(y <= c) {
33123                         return "above";
33124                 }else{
33125                         return "below";
33126                 }
33127     },
33128
33129     onNodeEnter : function(n, dd, e, data){
33130                 return false;
33131     },
33132     
33133     onNodeOver : function(n, dd, e, data){
33134                 var pt = this.getDropPoint(e, n, dd);
33135                 // set the insert point style on the target node
33136                 var dragElClass = this.dropNotAllowed;
33137                 if (pt) {
33138                         var targetElClass;
33139                         if (pt == "above"){
33140                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33141                                 targetElClass = "x-view-drag-insert-above";
33142                         } else {
33143                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33144                                 targetElClass = "x-view-drag-insert-below";
33145                         }
33146                         if (this.lastInsertClass != targetElClass){
33147                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33148                                 this.lastInsertClass = targetElClass;
33149                         }
33150                 }
33151                 return dragElClass;
33152         },
33153
33154     onNodeOut : function(n, dd, e, data){
33155                 this.removeDropIndicators(n);
33156     },
33157
33158     onNodeDrop : function(n, dd, e, data){
33159         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33160                 return false;
33161         }
33162         var pt = this.getDropPoint(e, n, dd);
33163                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33164                 if (pt == "below") { insertAt++; }
33165                 for (var i = 0; i < data.records.length; i++) {
33166                         var r = data.records[i];
33167                         var dup = this.store.getById(r.id);
33168                         if (dup && (dd != this.dragZone)) {
33169                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33170                         } else {
33171                                 if (data.copy) {
33172                                         this.store.insert(insertAt++, r.copy());
33173                                 } else {
33174                                         data.source.isDirtyFlag = true;
33175                                         r.store.remove(r);
33176                                         this.store.insert(insertAt++, r);
33177                                 }
33178                                 this.isDirtyFlag = true;
33179                         }
33180                 }
33181                 this.dragZone.cachedTarget = null;
33182                 return true;
33183     },
33184
33185     removeDropIndicators : function(n){
33186                 if(n){
33187                         Roo.fly(n).removeClass([
33188                                 "x-view-drag-insert-above",
33189                                 "x-view-drag-insert-below"]);
33190                         this.lastInsertClass = "_noclass";
33191                 }
33192     },
33193
33194 /**
33195  *      Utility method. Add a delete option to the DDView's context menu.
33196  *      @param {String} imageUrl The URL of the "delete" icon image.
33197  */
33198         setDeletable: function(imageUrl) {
33199                 if (!this.singleSelect && !this.multiSelect) {
33200                         this.singleSelect = true;
33201                 }
33202                 var c = this.getContextMenu();
33203                 this.contextMenu.on("itemclick", function(item) {
33204                         switch (item.id) {
33205                                 case "delete":
33206                                         this.remove(this.getSelectedIndexes());
33207                                         break;
33208                         }
33209                 }, this);
33210                 this.contextMenu.add({
33211                         icon: imageUrl,
33212                         id: "delete",
33213                         text: 'Delete'
33214                 });
33215         },
33216         
33217 /**     Return the context menu for this DDView. */
33218         getContextMenu: function() {
33219                 if (!this.contextMenu) {
33220 //                      Create the View's context menu
33221                         this.contextMenu = new Roo.menu.Menu({
33222                                 id: this.id + "-contextmenu"
33223                         });
33224                         this.el.on("contextmenu", this.showContextMenu, this);
33225                 }
33226                 return this.contextMenu;
33227         },
33228         
33229         disableContextMenu: function() {
33230                 if (this.contextMenu) {
33231                         this.el.un("contextmenu", this.showContextMenu, this);
33232                 }
33233         },
33234
33235         showContextMenu: function(e, item) {
33236         item = this.findItemFromChild(e.getTarget());
33237                 if (item) {
33238                         e.stopEvent();
33239                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33240                         this.contextMenu.showAt(e.getXY());
33241             }
33242     },
33243
33244 /**
33245  *      Remove {@link Roo.data.Record}s at the specified indices.
33246  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33247  */
33248     remove: function(selectedIndices) {
33249                 selectedIndices = [].concat(selectedIndices);
33250                 for (var i = 0; i < selectedIndices.length; i++) {
33251                         var rec = this.store.getAt(selectedIndices[i]);
33252                         this.store.remove(rec);
33253                 }
33254     },
33255
33256 /**
33257  *      Double click fires the event, but also, if this is draggable, and there is only one other
33258  *      related DropZone, it transfers the selected node.
33259  */
33260     onDblClick : function(e){
33261         var item = this.findItemFromChild(e.getTarget());
33262         if(item){
33263             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33264                 return false;
33265             }
33266             if (this.dragGroup) {
33267                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33268                     while (targets.indexOf(this.dropZone) > -1) {
33269                             targets.remove(this.dropZone);
33270                                 }
33271                     if (targets.length == 1) {
33272                                         this.dragZone.cachedTarget = null;
33273                         var el = Roo.get(targets[0].getEl());
33274                         var box = el.getBox(true);
33275                         targets[0].onNodeDrop(el.dom, {
33276                                 target: el.dom,
33277                                 xy: [box.x, box.y + box.height - 1]
33278                         }, null, this.getDragData(e));
33279                     }
33280                 }
33281         }
33282     },
33283     
33284     handleSelection: function(e) {
33285                 this.dragZone.cachedTarget = null;
33286         var item = this.findItemFromChild(e.getTarget());
33287         if (!item) {
33288                 this.clearSelections(true);
33289                 return;
33290         }
33291                 if (item && (this.multiSelect || this.singleSelect)){
33292                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33293                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33294                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33295                                 this.unselect(item);
33296                         } else {
33297                                 this.select(item, this.multiSelect && e.ctrlKey);
33298                                 this.lastSelection = item;
33299                         }
33300                 }
33301     },
33302
33303     onItemClick : function(item, index, e){
33304                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33305                         return false;
33306                 }
33307                 return true;
33308     },
33309
33310     unselect : function(nodeInfo, suppressEvent){
33311                 var node = this.getNode(nodeInfo);
33312                 if(node && this.isSelected(node)){
33313                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33314                                 Roo.fly(node).removeClass(this.selectedClass);
33315                                 this.selections.remove(node);
33316                                 if(!suppressEvent){
33317                                         this.fireEvent("selectionchange", this, this.selections);
33318                                 }
33319                         }
33320                 }
33321     }
33322 });
33323 /*
33324  * Based on:
33325  * Ext JS Library 1.1.1
33326  * Copyright(c) 2006-2007, Ext JS, LLC.
33327  *
33328  * Originally Released Under LGPL - original licence link has changed is not relivant.
33329  *
33330  * Fork - LGPL
33331  * <script type="text/javascript">
33332  */
33333  
33334 /**
33335  * @class Roo.LayoutManager
33336  * @extends Roo.util.Observable
33337  * Base class for layout managers.
33338  */
33339 Roo.LayoutManager = function(container, config){
33340     Roo.LayoutManager.superclass.constructor.call(this);
33341     this.el = Roo.get(container);
33342     // ie scrollbar fix
33343     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33344         document.body.scroll = "no";
33345     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33346         this.el.position('relative');
33347     }
33348     this.id = this.el.id;
33349     this.el.addClass("x-layout-container");
33350     /** false to disable window resize monitoring @type Boolean */
33351     this.monitorWindowResize = true;
33352     this.regions = {};
33353     this.addEvents({
33354         /**
33355          * @event layout
33356          * Fires when a layout is performed. 
33357          * @param {Roo.LayoutManager} this
33358          */
33359         "layout" : true,
33360         /**
33361          * @event regionresized
33362          * Fires when the user resizes a region. 
33363          * @param {Roo.LayoutRegion} region The resized region
33364          * @param {Number} newSize The new size (width for east/west, height for north/south)
33365          */
33366         "regionresized" : true,
33367         /**
33368          * @event regioncollapsed
33369          * Fires when a region is collapsed. 
33370          * @param {Roo.LayoutRegion} region The collapsed region
33371          */
33372         "regioncollapsed" : true,
33373         /**
33374          * @event regionexpanded
33375          * Fires when a region is expanded.  
33376          * @param {Roo.LayoutRegion} region The expanded region
33377          */
33378         "regionexpanded" : true
33379     });
33380     this.updating = false;
33381     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33382 };
33383
33384 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33385     /**
33386      * Returns true if this layout is currently being updated
33387      * @return {Boolean}
33388      */
33389     isUpdating : function(){
33390         return this.updating; 
33391     },
33392     
33393     /**
33394      * Suspend the LayoutManager from doing auto-layouts while
33395      * making multiple add or remove calls
33396      */
33397     beginUpdate : function(){
33398         this.updating = true;    
33399     },
33400     
33401     /**
33402      * Restore auto-layouts and optionally disable the manager from performing a layout
33403      * @param {Boolean} noLayout true to disable a layout update 
33404      */
33405     endUpdate : function(noLayout){
33406         this.updating = false;
33407         if(!noLayout){
33408             this.layout();
33409         }    
33410     },
33411     
33412     layout: function(){
33413         
33414     },
33415     
33416     onRegionResized : function(region, newSize){
33417         this.fireEvent("regionresized", region, newSize);
33418         this.layout();
33419     },
33420     
33421     onRegionCollapsed : function(region){
33422         this.fireEvent("regioncollapsed", region);
33423     },
33424     
33425     onRegionExpanded : function(region){
33426         this.fireEvent("regionexpanded", region);
33427     },
33428         
33429     /**
33430      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33431      * performs box-model adjustments.
33432      * @return {Object} The size as an object {width: (the width), height: (the height)}
33433      */
33434     getViewSize : function(){
33435         var size;
33436         if(this.el.dom != document.body){
33437             size = this.el.getSize();
33438         }else{
33439             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33440         }
33441         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33442         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33443         return size;
33444     },
33445     
33446     /**
33447      * Returns the Element this layout is bound to.
33448      * @return {Roo.Element}
33449      */
33450     getEl : function(){
33451         return this.el;
33452     },
33453     
33454     /**
33455      * Returns the specified region.
33456      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33457      * @return {Roo.LayoutRegion}
33458      */
33459     getRegion : function(target){
33460         return this.regions[target.toLowerCase()];
33461     },
33462     
33463     onWindowResize : function(){
33464         if(this.monitorWindowResize){
33465             this.layout();
33466         }
33467     }
33468 });/*
33469  * Based on:
33470  * Ext JS Library 1.1.1
33471  * Copyright(c) 2006-2007, Ext JS, LLC.
33472  *
33473  * Originally Released Under LGPL - original licence link has changed is not relivant.
33474  *
33475  * Fork - LGPL
33476  * <script type="text/javascript">
33477  */
33478 /**
33479  * @class Roo.BorderLayout
33480  * @extends Roo.LayoutManager
33481  * @children Roo.ContentPanel
33482  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33483  * please see: <br><br>
33484  * <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>
33485  * <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>
33486  * Example:
33487  <pre><code>
33488  var layout = new Roo.BorderLayout(document.body, {
33489     north: {
33490         initialSize: 25,
33491         titlebar: false
33492     },
33493     west: {
33494         split:true,
33495         initialSize: 200,
33496         minSize: 175,
33497         maxSize: 400,
33498         titlebar: true,
33499         collapsible: true
33500     },
33501     east: {
33502         split:true,
33503         initialSize: 202,
33504         minSize: 175,
33505         maxSize: 400,
33506         titlebar: true,
33507         collapsible: true
33508     },
33509     south: {
33510         split:true,
33511         initialSize: 100,
33512         minSize: 100,
33513         maxSize: 200,
33514         titlebar: true,
33515         collapsible: true
33516     },
33517     center: {
33518         titlebar: true,
33519         autoScroll:true,
33520         resizeTabs: true,
33521         minTabWidth: 50,
33522         preferredTabWidth: 150
33523     }
33524 });
33525
33526 // shorthand
33527 var CP = Roo.ContentPanel;
33528
33529 layout.beginUpdate();
33530 layout.add("north", new CP("north", "North"));
33531 layout.add("south", new CP("south", {title: "South", closable: true}));
33532 layout.add("west", new CP("west", {title: "West"}));
33533 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33534 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33535 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33536 layout.getRegion("center").showPanel("center1");
33537 layout.endUpdate();
33538 </code></pre>
33539
33540 <b>The container the layout is rendered into can be either the body element or any other element.
33541 If it is not the body element, the container needs to either be an absolute positioned element,
33542 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33543 the container size if it is not the body element.</b>
33544
33545 * @constructor
33546 * Create a new BorderLayout
33547 * @param {String/HTMLElement/Element} container The container this layout is bound to
33548 * @param {Object} config Configuration options
33549  */
33550 Roo.BorderLayout = function(container, config){
33551     config = config || {};
33552     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33553     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33554     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33555         var target = this.factory.validRegions[i];
33556         if(config[target]){
33557             this.addRegion(target, config[target]);
33558         }
33559     }
33560 };
33561
33562 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33563         
33564         /**
33565          * @cfg {Roo.LayoutRegion} east
33566          */
33567         /**
33568          * @cfg {Roo.LayoutRegion} west
33569          */
33570         /**
33571          * @cfg {Roo.LayoutRegion} north
33572          */
33573         /**
33574          * @cfg {Roo.LayoutRegion} south
33575          */
33576         /**
33577          * @cfg {Roo.LayoutRegion} center
33578          */
33579     /**
33580      * Creates and adds a new region if it doesn't already exist.
33581      * @param {String} target The target region key (north, south, east, west or center).
33582      * @param {Object} config The regions config object
33583      * @return {BorderLayoutRegion} The new region
33584      */
33585     addRegion : function(target, config){
33586         if(!this.regions[target]){
33587             var r = this.factory.create(target, this, config);
33588             this.bindRegion(target, r);
33589         }
33590         return this.regions[target];
33591     },
33592
33593     // private (kinda)
33594     bindRegion : function(name, r){
33595         this.regions[name] = r;
33596         r.on("visibilitychange", this.layout, this);
33597         r.on("paneladded", this.layout, this);
33598         r.on("panelremoved", this.layout, this);
33599         r.on("invalidated", this.layout, this);
33600         r.on("resized", this.onRegionResized, this);
33601         r.on("collapsed", this.onRegionCollapsed, this);
33602         r.on("expanded", this.onRegionExpanded, this);
33603     },
33604
33605     /**
33606      * Performs a layout update.
33607      */
33608     layout : function(){
33609         if(this.updating) {
33610             return;
33611         }
33612         var size = this.getViewSize();
33613         var w = size.width;
33614         var h = size.height;
33615         var centerW = w;
33616         var centerH = h;
33617         var centerY = 0;
33618         var centerX = 0;
33619         //var x = 0, y = 0;
33620
33621         var rs = this.regions;
33622         var north = rs["north"];
33623         var south = rs["south"]; 
33624         var west = rs["west"];
33625         var east = rs["east"];
33626         var center = rs["center"];
33627         //if(this.hideOnLayout){ // not supported anymore
33628             //c.el.setStyle("display", "none");
33629         //}
33630         if(north && north.isVisible()){
33631             var b = north.getBox();
33632             var m = north.getMargins();
33633             b.width = w - (m.left+m.right);
33634             b.x = m.left;
33635             b.y = m.top;
33636             centerY = b.height + b.y + m.bottom;
33637             centerH -= centerY;
33638             north.updateBox(this.safeBox(b));
33639         }
33640         if(south && south.isVisible()){
33641             var b = south.getBox();
33642             var m = south.getMargins();
33643             b.width = w - (m.left+m.right);
33644             b.x = m.left;
33645             var totalHeight = (b.height + m.top + m.bottom);
33646             b.y = h - totalHeight + m.top;
33647             centerH -= totalHeight;
33648             south.updateBox(this.safeBox(b));
33649         }
33650         if(west && west.isVisible()){
33651             var b = west.getBox();
33652             var m = west.getMargins();
33653             b.height = centerH - (m.top+m.bottom);
33654             b.x = m.left;
33655             b.y = centerY + m.top;
33656             var totalWidth = (b.width + m.left + m.right);
33657             centerX += totalWidth;
33658             centerW -= totalWidth;
33659             west.updateBox(this.safeBox(b));
33660         }
33661         if(east && east.isVisible()){
33662             var b = east.getBox();
33663             var m = east.getMargins();
33664             b.height = centerH - (m.top+m.bottom);
33665             var totalWidth = (b.width + m.left + m.right);
33666             b.x = w - totalWidth + m.left;
33667             b.y = centerY + m.top;
33668             centerW -= totalWidth;
33669             east.updateBox(this.safeBox(b));
33670         }
33671         if(center){
33672             var m = center.getMargins();
33673             var centerBox = {
33674                 x: centerX + m.left,
33675                 y: centerY + m.top,
33676                 width: centerW - (m.left+m.right),
33677                 height: centerH - (m.top+m.bottom)
33678             };
33679             //if(this.hideOnLayout){
33680                 //center.el.setStyle("display", "block");
33681             //}
33682             center.updateBox(this.safeBox(centerBox));
33683         }
33684         this.el.repaint();
33685         this.fireEvent("layout", this);
33686     },
33687
33688     // private
33689     safeBox : function(box){
33690         box.width = Math.max(0, box.width);
33691         box.height = Math.max(0, box.height);
33692         return box;
33693     },
33694
33695     /**
33696      * Adds a ContentPanel (or subclass) to this layout.
33697      * @param {String} target The target region key (north, south, east, west or center).
33698      * @param {Roo.ContentPanel} panel The panel to add
33699      * @return {Roo.ContentPanel} The added panel
33700      */
33701     add : function(target, panel){
33702          
33703         target = target.toLowerCase();
33704         return this.regions[target].add(panel);
33705     },
33706
33707     /**
33708      * Remove a ContentPanel (or subclass) to this layout.
33709      * @param {String} target The target region key (north, south, east, west or center).
33710      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33711      * @return {Roo.ContentPanel} The removed panel
33712      */
33713     remove : function(target, panel){
33714         target = target.toLowerCase();
33715         return this.regions[target].remove(panel);
33716     },
33717
33718     /**
33719      * Searches all regions for a panel with the specified id
33720      * @param {String} panelId
33721      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33722      */
33723     findPanel : function(panelId){
33724         var rs = this.regions;
33725         for(var target in rs){
33726             if(typeof rs[target] != "function"){
33727                 var p = rs[target].getPanel(panelId);
33728                 if(p){
33729                     return p;
33730                 }
33731             }
33732         }
33733         return null;
33734     },
33735
33736     /**
33737      * Searches all regions for a panel with the specified id and activates (shows) it.
33738      * @param {String/ContentPanel} panelId The panels id or the panel itself
33739      * @return {Roo.ContentPanel} The shown panel or null
33740      */
33741     showPanel : function(panelId) {
33742       var rs = this.regions;
33743       for(var target in rs){
33744          var r = rs[target];
33745          if(typeof r != "function"){
33746             if(r.hasPanel(panelId)){
33747                return r.showPanel(panelId);
33748             }
33749          }
33750       }
33751       return null;
33752    },
33753
33754    /**
33755      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33756      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33757      */
33758     restoreState : function(provider){
33759         if(!provider){
33760             provider = Roo.state.Manager;
33761         }
33762         var sm = new Roo.LayoutStateManager();
33763         sm.init(this, provider);
33764     },
33765
33766     /**
33767      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33768      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33769      * a valid ContentPanel config object.  Example:
33770      * <pre><code>
33771 // Create the main layout
33772 var layout = new Roo.BorderLayout('main-ct', {
33773     west: {
33774         split:true,
33775         minSize: 175,
33776         titlebar: true
33777     },
33778     center: {
33779         title:'Components'
33780     }
33781 }, 'main-ct');
33782
33783 // Create and add multiple ContentPanels at once via configs
33784 layout.batchAdd({
33785    west: {
33786        id: 'source-files',
33787        autoCreate:true,
33788        title:'Ext Source Files',
33789        autoScroll:true,
33790        fitToFrame:true
33791    },
33792    center : {
33793        el: cview,
33794        autoScroll:true,
33795        fitToFrame:true,
33796        toolbar: tb,
33797        resizeEl:'cbody'
33798    }
33799 });
33800 </code></pre>
33801      * @param {Object} regions An object containing ContentPanel configs by region name
33802      */
33803     batchAdd : function(regions){
33804         this.beginUpdate();
33805         for(var rname in regions){
33806             var lr = this.regions[rname];
33807             if(lr){
33808                 this.addTypedPanels(lr, regions[rname]);
33809             }
33810         }
33811         this.endUpdate();
33812     },
33813
33814     // private
33815     addTypedPanels : function(lr, ps){
33816         if(typeof ps == 'string'){
33817             lr.add(new Roo.ContentPanel(ps));
33818         }
33819         else if(ps instanceof Array){
33820             for(var i =0, len = ps.length; i < len; i++){
33821                 this.addTypedPanels(lr, ps[i]);
33822             }
33823         }
33824         else if(!ps.events){ // raw config?
33825             var el = ps.el;
33826             delete ps.el; // prevent conflict
33827             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33828         }
33829         else {  // panel object assumed!
33830             lr.add(ps);
33831         }
33832     },
33833     /**
33834      * Adds a xtype elements to the layout.
33835      * <pre><code>
33836
33837 layout.addxtype({
33838        xtype : 'ContentPanel',
33839        region: 'west',
33840        items: [ .... ]
33841    }
33842 );
33843
33844 layout.addxtype({
33845         xtype : 'NestedLayoutPanel',
33846         region: 'west',
33847         layout: {
33848            center: { },
33849            west: { }   
33850         },
33851         items : [ ... list of content panels or nested layout panels.. ]
33852    }
33853 );
33854 </code></pre>
33855      * @param {Object} cfg Xtype definition of item to add.
33856      */
33857     addxtype : function(cfg)
33858     {
33859         // basically accepts a pannel...
33860         // can accept a layout region..!?!?
33861         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33862         
33863         if (!cfg.xtype.match(/Panel$/)) {
33864             return false;
33865         }
33866         var ret = false;
33867         
33868         if (typeof(cfg.region) == 'undefined') {
33869             Roo.log("Failed to add Panel, region was not set");
33870             Roo.log(cfg);
33871             return false;
33872         }
33873         var region = cfg.region;
33874         delete cfg.region;
33875         
33876           
33877         var xitems = [];
33878         if (cfg.items) {
33879             xitems = cfg.items;
33880             delete cfg.items;
33881         }
33882         var nb = false;
33883         
33884         switch(cfg.xtype) 
33885         {
33886             case 'ContentPanel':  // ContentPanel (el, cfg)
33887             case 'ScrollPanel':  // ContentPanel (el, cfg)
33888             case 'ViewPanel': 
33889                 if(cfg.autoCreate) {
33890                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33891                 } else {
33892                     var el = this.el.createChild();
33893                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33894                 }
33895                 
33896                 this.add(region, ret);
33897                 break;
33898             
33899             
33900             case 'TreePanel': // our new panel!
33901                 cfg.el = this.el.createChild();
33902                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33903                 this.add(region, ret);
33904                 break;
33905             
33906             case 'NestedLayoutPanel': 
33907                 // create a new Layout (which is  a Border Layout...
33908                 var el = this.el.createChild();
33909                 var clayout = cfg.layout;
33910                 delete cfg.layout;
33911                 clayout.items   = clayout.items  || [];
33912                 // replace this exitems with the clayout ones..
33913                 xitems = clayout.items;
33914                  
33915                 
33916                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33917                     cfg.background = false;
33918                 }
33919                 var layout = new Roo.BorderLayout(el, clayout);
33920                 
33921                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33922                 //console.log('adding nested layout panel '  + cfg.toSource());
33923                 this.add(region, ret);
33924                 nb = {}; /// find first...
33925                 break;
33926                 
33927             case 'GridPanel': 
33928             
33929                 // needs grid and region
33930                 
33931                 //var el = this.getRegion(region).el.createChild();
33932                 var el = this.el.createChild();
33933                 // create the grid first...
33934                 
33935                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33936                 delete cfg.grid;
33937                 if (region == 'center' && this.active ) {
33938                     cfg.background = false;
33939                 }
33940                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33941                 
33942                 this.add(region, ret);
33943                 if (cfg.background) {
33944                     ret.on('activate', function(gp) {
33945                         if (!gp.grid.rendered) {
33946                             gp.grid.render();
33947                         }
33948                     });
33949                 } else {
33950                     grid.render();
33951                 }
33952                 break;
33953            
33954            
33955            
33956                 
33957                 
33958                 
33959             default:
33960                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33961                     
33962                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33963                     this.add(region, ret);
33964                 } else {
33965                 
33966                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33967                     return null;
33968                 }
33969                 
33970              // GridPanel (grid, cfg)
33971             
33972         }
33973         this.beginUpdate();
33974         // add children..
33975         var region = '';
33976         var abn = {};
33977         Roo.each(xitems, function(i)  {
33978             region = nb && i.region ? i.region : false;
33979             
33980             var add = ret.addxtype(i);
33981            
33982             if (region) {
33983                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33984                 if (!i.background) {
33985                     abn[region] = nb[region] ;
33986                 }
33987             }
33988             
33989         });
33990         this.endUpdate();
33991
33992         // make the last non-background panel active..
33993         //if (nb) { Roo.log(abn); }
33994         if (nb) {
33995             
33996             for(var r in abn) {
33997                 region = this.getRegion(r);
33998                 if (region) {
33999                     // tried using nb[r], but it does not work..
34000                      
34001                     region.showPanel(abn[r]);
34002                    
34003                 }
34004             }
34005         }
34006         return ret;
34007         
34008     }
34009 });
34010
34011 /**
34012  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34013  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34014  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34015  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34016  * <pre><code>
34017 // shorthand
34018 var CP = Roo.ContentPanel;
34019
34020 var layout = Roo.BorderLayout.create({
34021     north: {
34022         initialSize: 25,
34023         titlebar: false,
34024         panels: [new CP("north", "North")]
34025     },
34026     west: {
34027         split:true,
34028         initialSize: 200,
34029         minSize: 175,
34030         maxSize: 400,
34031         titlebar: true,
34032         collapsible: true,
34033         panels: [new CP("west", {title: "West"})]
34034     },
34035     east: {
34036         split:true,
34037         initialSize: 202,
34038         minSize: 175,
34039         maxSize: 400,
34040         titlebar: true,
34041         collapsible: true,
34042         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34043     },
34044     south: {
34045         split:true,
34046         initialSize: 100,
34047         minSize: 100,
34048         maxSize: 200,
34049         titlebar: true,
34050         collapsible: true,
34051         panels: [new CP("south", {title: "South", closable: true})]
34052     },
34053     center: {
34054         titlebar: true,
34055         autoScroll:true,
34056         resizeTabs: true,
34057         minTabWidth: 50,
34058         preferredTabWidth: 150,
34059         panels: [
34060             new CP("center1", {title: "Close Me", closable: true}),
34061             new CP("center2", {title: "Center Panel", closable: false})
34062         ]
34063     }
34064 }, document.body);
34065
34066 layout.getRegion("center").showPanel("center1");
34067 </code></pre>
34068  * @param config
34069  * @param targetEl
34070  */
34071 Roo.BorderLayout.create = function(config, targetEl){
34072     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34073     layout.beginUpdate();
34074     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34075     for(var j = 0, jlen = regions.length; j < jlen; j++){
34076         var lr = regions[j];
34077         if(layout.regions[lr] && config[lr].panels){
34078             var r = layout.regions[lr];
34079             var ps = config[lr].panels;
34080             layout.addTypedPanels(r, ps);
34081         }
34082     }
34083     layout.endUpdate();
34084     return layout;
34085 };
34086
34087 // private
34088 Roo.BorderLayout.RegionFactory = {
34089     // private
34090     validRegions : ["north","south","east","west","center"],
34091
34092     // private
34093     create : function(target, mgr, config){
34094         target = target.toLowerCase();
34095         if(config.lightweight || config.basic){
34096             return new Roo.BasicLayoutRegion(mgr, config, target);
34097         }
34098         switch(target){
34099             case "north":
34100                 return new Roo.NorthLayoutRegion(mgr, config);
34101             case "south":
34102                 return new Roo.SouthLayoutRegion(mgr, config);
34103             case "east":
34104                 return new Roo.EastLayoutRegion(mgr, config);
34105             case "west":
34106                 return new Roo.WestLayoutRegion(mgr, config);
34107             case "center":
34108                 return new Roo.CenterLayoutRegion(mgr, config);
34109         }
34110         throw 'Layout region "'+target+'" not supported.';
34111     }
34112 };/*
34113  * Based on:
34114  * Ext JS Library 1.1.1
34115  * Copyright(c) 2006-2007, Ext JS, LLC.
34116  *
34117  * Originally Released Under LGPL - original licence link has changed is not relivant.
34118  *
34119  * Fork - LGPL
34120  * <script type="text/javascript">
34121  */
34122  
34123 /**
34124  * @class Roo.BasicLayoutRegion
34125  * @extends Roo.util.Observable
34126  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34127  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34128  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34129  */
34130 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34131     this.mgr = mgr;
34132     this.position  = pos;
34133     this.events = {
34134         /**
34135          * @scope Roo.BasicLayoutRegion
34136          */
34137         
34138         /**
34139          * @event beforeremove
34140          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34141          * @param {Roo.LayoutRegion} this
34142          * @param {Roo.ContentPanel} panel The panel
34143          * @param {Object} e The cancel event object
34144          */
34145         "beforeremove" : true,
34146         /**
34147          * @event invalidated
34148          * Fires when the layout for this region is changed.
34149          * @param {Roo.LayoutRegion} this
34150          */
34151         "invalidated" : true,
34152         /**
34153          * @event visibilitychange
34154          * Fires when this region is shown or hidden 
34155          * @param {Roo.LayoutRegion} this
34156          * @param {Boolean} visibility true or false
34157          */
34158         "visibilitychange" : true,
34159         /**
34160          * @event paneladded
34161          * Fires when a panel is added. 
34162          * @param {Roo.LayoutRegion} this
34163          * @param {Roo.ContentPanel} panel The panel
34164          */
34165         "paneladded" : true,
34166         /**
34167          * @event panelremoved
34168          * Fires when a panel is removed. 
34169          * @param {Roo.LayoutRegion} this
34170          * @param {Roo.ContentPanel} panel The panel
34171          */
34172         "panelremoved" : true,
34173         /**
34174          * @event beforecollapse
34175          * Fires when this region before collapse.
34176          * @param {Roo.LayoutRegion} this
34177          */
34178         "beforecollapse" : true,
34179         /**
34180          * @event collapsed
34181          * Fires when this region is collapsed.
34182          * @param {Roo.LayoutRegion} this
34183          */
34184         "collapsed" : true,
34185         /**
34186          * @event expanded
34187          * Fires when this region is expanded.
34188          * @param {Roo.LayoutRegion} this
34189          */
34190         "expanded" : true,
34191         /**
34192          * @event slideshow
34193          * Fires when this region is slid into view.
34194          * @param {Roo.LayoutRegion} this
34195          */
34196         "slideshow" : true,
34197         /**
34198          * @event slidehide
34199          * Fires when this region slides out of view. 
34200          * @param {Roo.LayoutRegion} this
34201          */
34202         "slidehide" : true,
34203         /**
34204          * @event panelactivated
34205          * Fires when a panel is activated. 
34206          * @param {Roo.LayoutRegion} this
34207          * @param {Roo.ContentPanel} panel The activated panel
34208          */
34209         "panelactivated" : true,
34210         /**
34211          * @event resized
34212          * Fires when the user resizes this region. 
34213          * @param {Roo.LayoutRegion} this
34214          * @param {Number} newSize The new size (width for east/west, height for north/south)
34215          */
34216         "resized" : true
34217     };
34218     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34219     this.panels = new Roo.util.MixedCollection();
34220     this.panels.getKey = this.getPanelId.createDelegate(this);
34221     this.box = null;
34222     this.activePanel = null;
34223     // ensure listeners are added...
34224     
34225     if (config.listeners || config.events) {
34226         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34227             listeners : config.listeners || {},
34228             events : config.events || {}
34229         });
34230     }
34231     
34232     if(skipConfig !== true){
34233         this.applyConfig(config);
34234     }
34235 };
34236
34237 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34238     getPanelId : function(p){
34239         return p.getId();
34240     },
34241     
34242     applyConfig : function(config){
34243         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34244         this.config = config;
34245         
34246     },
34247     
34248     /**
34249      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34250      * the width, for horizontal (north, south) the height.
34251      * @param {Number} newSize The new width or height
34252      */
34253     resizeTo : function(newSize){
34254         var el = this.el ? this.el :
34255                  (this.activePanel ? this.activePanel.getEl() : null);
34256         if(el){
34257             switch(this.position){
34258                 case "east":
34259                 case "west":
34260                     el.setWidth(newSize);
34261                     this.fireEvent("resized", this, newSize);
34262                 break;
34263                 case "north":
34264                 case "south":
34265                     el.setHeight(newSize);
34266                     this.fireEvent("resized", this, newSize);
34267                 break;                
34268             }
34269         }
34270     },
34271     
34272     getBox : function(){
34273         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34274     },
34275     
34276     getMargins : function(){
34277         return this.margins;
34278     },
34279     
34280     updateBox : function(box){
34281         this.box = box;
34282         var el = this.activePanel.getEl();
34283         el.dom.style.left = box.x + "px";
34284         el.dom.style.top = box.y + "px";
34285         this.activePanel.setSize(box.width, box.height);
34286     },
34287     
34288     /**
34289      * Returns the container element for this region.
34290      * @return {Roo.Element}
34291      */
34292     getEl : function(){
34293         return this.activePanel;
34294     },
34295     
34296     /**
34297      * Returns true if this region is currently visible.
34298      * @return {Boolean}
34299      */
34300     isVisible : function(){
34301         return this.activePanel ? true : false;
34302     },
34303     
34304     setActivePanel : function(panel){
34305         panel = this.getPanel(panel);
34306         if(this.activePanel && this.activePanel != panel){
34307             this.activePanel.setActiveState(false);
34308             this.activePanel.getEl().setLeftTop(-10000,-10000);
34309         }
34310         this.activePanel = panel;
34311         panel.setActiveState(true);
34312         if(this.box){
34313             panel.setSize(this.box.width, this.box.height);
34314         }
34315         this.fireEvent("panelactivated", this, panel);
34316         this.fireEvent("invalidated");
34317     },
34318     
34319     /**
34320      * Show the specified panel.
34321      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34322      * @return {Roo.ContentPanel} The shown panel or null
34323      */
34324     showPanel : function(panel){
34325         if(panel = this.getPanel(panel)){
34326             this.setActivePanel(panel);
34327         }
34328         return panel;
34329     },
34330     
34331     /**
34332      * Get the active panel for this region.
34333      * @return {Roo.ContentPanel} The active panel or null
34334      */
34335     getActivePanel : function(){
34336         return this.activePanel;
34337     },
34338     
34339     /**
34340      * Add the passed ContentPanel(s)
34341      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34342      * @return {Roo.ContentPanel} The panel added (if only one was added)
34343      */
34344     add : function(panel){
34345         if(arguments.length > 1){
34346             for(var i = 0, len = arguments.length; i < len; i++) {
34347                 this.add(arguments[i]);
34348             }
34349             return null;
34350         }
34351         if(this.hasPanel(panel)){
34352             this.showPanel(panel);
34353             return panel;
34354         }
34355         var el = panel.getEl();
34356         if(el.dom.parentNode != this.mgr.el.dom){
34357             this.mgr.el.dom.appendChild(el.dom);
34358         }
34359         if(panel.setRegion){
34360             panel.setRegion(this);
34361         }
34362         this.panels.add(panel);
34363         el.setStyle("position", "absolute");
34364         if(!panel.background){
34365             this.setActivePanel(panel);
34366             if(this.config.initialSize && this.panels.getCount()==1){
34367                 this.resizeTo(this.config.initialSize);
34368             }
34369         }
34370         this.fireEvent("paneladded", this, panel);
34371         return panel;
34372     },
34373     
34374     /**
34375      * Returns true if the panel is in this region.
34376      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34377      * @return {Boolean}
34378      */
34379     hasPanel : function(panel){
34380         if(typeof panel == "object"){ // must be panel obj
34381             panel = panel.getId();
34382         }
34383         return this.getPanel(panel) ? true : false;
34384     },
34385     
34386     /**
34387      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34388      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34389      * @param {Boolean} preservePanel Overrides the config preservePanel option
34390      * @return {Roo.ContentPanel} The panel that was removed
34391      */
34392     remove : function(panel, preservePanel){
34393         panel = this.getPanel(panel);
34394         if(!panel){
34395             return null;
34396         }
34397         var e = {};
34398         this.fireEvent("beforeremove", this, panel, e);
34399         if(e.cancel === true){
34400             return null;
34401         }
34402         var panelId = panel.getId();
34403         this.panels.removeKey(panelId);
34404         return panel;
34405     },
34406     
34407     /**
34408      * Returns the panel specified or null if it's not in this region.
34409      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34410      * @return {Roo.ContentPanel}
34411      */
34412     getPanel : function(id){
34413         if(typeof id == "object"){ // must be panel obj
34414             return id;
34415         }
34416         return this.panels.get(id);
34417     },
34418     
34419     /**
34420      * Returns this regions position (north/south/east/west/center).
34421      * @return {String} 
34422      */
34423     getPosition: function(){
34424         return this.position;    
34425     }
34426 });/*
34427  * Based on:
34428  * Ext JS Library 1.1.1
34429  * Copyright(c) 2006-2007, Ext JS, LLC.
34430  *
34431  * Originally Released Under LGPL - original licence link has changed is not relivant.
34432  *
34433  * Fork - LGPL
34434  * <script type="text/javascript">
34435  */
34436  
34437 /**
34438  * @class Roo.LayoutRegion
34439  * @extends Roo.BasicLayoutRegion
34440  * This class represents a region in a layout manager.
34441  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34442  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34443  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34444  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34445  * @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})
34446  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34447  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34448  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34449  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34450  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34451  * @cfg {String}    title           The title for the region (overrides panel titles)
34452  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34453  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34454  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34455  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34456  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34457  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34458  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34459  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34460  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34461  * @cfg {Boolean}   showPin         True to show a pin button
34462  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34463  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34464  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34465  * @cfg {Number}    width           For East/West panels
34466  * @cfg {Number}    height          For North/South panels
34467  * @cfg {Boolean}   split           To show the splitter
34468  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34469  */
34470 Roo.LayoutRegion = function(mgr, config, pos){
34471     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34472     var dh = Roo.DomHelper;
34473     /** This region's container element 
34474     * @type Roo.Element */
34475     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34476     /** This region's title element 
34477     * @type Roo.Element */
34478
34479     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34480         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34481         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34482     ]}, true);
34483     this.titleEl.enableDisplayMode();
34484     /** This region's title text element 
34485     * @type HTMLElement */
34486     this.titleTextEl = this.titleEl.dom.firstChild;
34487     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34488     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34489     this.closeBtn.enableDisplayMode();
34490     this.closeBtn.on("click", this.closeClicked, this);
34491     this.closeBtn.hide();
34492
34493     this.createBody(config);
34494     this.visible = true;
34495     this.collapsed = false;
34496
34497     if(config.hideWhenEmpty){
34498         this.hide();
34499         this.on("paneladded", this.validateVisibility, this);
34500         this.on("panelremoved", this.validateVisibility, this);
34501     }
34502     this.applyConfig(config);
34503 };
34504
34505 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34506
34507     createBody : function(){
34508         /** This region's body element 
34509         * @type Roo.Element */
34510         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34511     },
34512
34513     applyConfig : function(c){
34514         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34515             var dh = Roo.DomHelper;
34516             if(c.titlebar !== false){
34517                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34518                 this.collapseBtn.on("click", this.collapse, this);
34519                 this.collapseBtn.enableDisplayMode();
34520
34521                 if(c.showPin === true || this.showPin){
34522                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34523                     this.stickBtn.enableDisplayMode();
34524                     this.stickBtn.on("click", this.expand, this);
34525                     this.stickBtn.hide();
34526                 }
34527             }
34528             /** This region's collapsed element
34529             * @type Roo.Element */
34530             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34531                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34532             ]}, true);
34533             if(c.floatable !== false){
34534                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34535                this.collapsedEl.on("click", this.collapseClick, this);
34536             }
34537
34538             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34539                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34540                    id: "message", unselectable: "on", style:{"float":"left"}});
34541                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34542              }
34543             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34544             this.expandBtn.on("click", this.expand, this);
34545         }
34546         if(this.collapseBtn){
34547             this.collapseBtn.setVisible(c.collapsible == true);
34548         }
34549         this.cmargins = c.cmargins || this.cmargins ||
34550                          (this.position == "west" || this.position == "east" ?
34551                              {top: 0, left: 2, right:2, bottom: 0} :
34552                              {top: 2, left: 0, right:0, bottom: 2});
34553         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34554         this.bottomTabs = c.tabPosition != "top";
34555         this.autoScroll = c.autoScroll || false;
34556         if(this.autoScroll){
34557             this.bodyEl.setStyle("overflow", "auto");
34558         }else{
34559             this.bodyEl.setStyle("overflow", "hidden");
34560         }
34561         //if(c.titlebar !== false){
34562             if((!c.titlebar && !c.title) || c.titlebar === false){
34563                 this.titleEl.hide();
34564             }else{
34565                 this.titleEl.show();
34566                 if(c.title){
34567                     this.titleTextEl.innerHTML = c.title;
34568                 }
34569             }
34570         //}
34571         this.duration = c.duration || .30;
34572         this.slideDuration = c.slideDuration || .45;
34573         this.config = c;
34574         if(c.collapsed){
34575             this.collapse(true);
34576         }
34577         if(c.hidden){
34578             this.hide();
34579         }
34580     },
34581     /**
34582      * Returns true if this region is currently visible.
34583      * @return {Boolean}
34584      */
34585     isVisible : function(){
34586         return this.visible;
34587     },
34588
34589     /**
34590      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34591      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34592      */
34593     setCollapsedTitle : function(title){
34594         title = title || "&#160;";
34595         if(this.collapsedTitleTextEl){
34596             this.collapsedTitleTextEl.innerHTML = title;
34597         }
34598     },
34599
34600     getBox : function(){
34601         var b;
34602         if(!this.collapsed){
34603             b = this.el.getBox(false, true);
34604         }else{
34605             b = this.collapsedEl.getBox(false, true);
34606         }
34607         return b;
34608     },
34609
34610     getMargins : function(){
34611         return this.collapsed ? this.cmargins : this.margins;
34612     },
34613
34614     highlight : function(){
34615         this.el.addClass("x-layout-panel-dragover");
34616     },
34617
34618     unhighlight : function(){
34619         this.el.removeClass("x-layout-panel-dragover");
34620     },
34621
34622     updateBox : function(box){
34623         this.box = box;
34624         if(!this.collapsed){
34625             this.el.dom.style.left = box.x + "px";
34626             this.el.dom.style.top = box.y + "px";
34627             this.updateBody(box.width, box.height);
34628         }else{
34629             this.collapsedEl.dom.style.left = box.x + "px";
34630             this.collapsedEl.dom.style.top = box.y + "px";
34631             this.collapsedEl.setSize(box.width, box.height);
34632         }
34633         if(this.tabs){
34634             this.tabs.autoSizeTabs();
34635         }
34636     },
34637
34638     updateBody : function(w, h){
34639         if(w !== null){
34640             this.el.setWidth(w);
34641             w -= this.el.getBorderWidth("rl");
34642             if(this.config.adjustments){
34643                 w += this.config.adjustments[0];
34644             }
34645         }
34646         if(h !== null){
34647             this.el.setHeight(h);
34648             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34649             h -= this.el.getBorderWidth("tb");
34650             if(this.config.adjustments){
34651                 h += this.config.adjustments[1];
34652             }
34653             this.bodyEl.setHeight(h);
34654             if(this.tabs){
34655                 h = this.tabs.syncHeight(h);
34656             }
34657         }
34658         if(this.panelSize){
34659             w = w !== null ? w : this.panelSize.width;
34660             h = h !== null ? h : this.panelSize.height;
34661         }
34662         if(this.activePanel){
34663             var el = this.activePanel.getEl();
34664             w = w !== null ? w : el.getWidth();
34665             h = h !== null ? h : el.getHeight();
34666             this.panelSize = {width: w, height: h};
34667             this.activePanel.setSize(w, h);
34668         }
34669         if(Roo.isIE && this.tabs){
34670             this.tabs.el.repaint();
34671         }
34672     },
34673
34674     /**
34675      * Returns the container element for this region.
34676      * @return {Roo.Element}
34677      */
34678     getEl : function(){
34679         return this.el;
34680     },
34681
34682     /**
34683      * Hides this region.
34684      */
34685     hide : function(){
34686         if(!this.collapsed){
34687             this.el.dom.style.left = "-2000px";
34688             this.el.hide();
34689         }else{
34690             this.collapsedEl.dom.style.left = "-2000px";
34691             this.collapsedEl.hide();
34692         }
34693         this.visible = false;
34694         this.fireEvent("visibilitychange", this, false);
34695     },
34696
34697     /**
34698      * Shows this region if it was previously hidden.
34699      */
34700     show : function(){
34701         if(!this.collapsed){
34702             this.el.show();
34703         }else{
34704             this.collapsedEl.show();
34705         }
34706         this.visible = true;
34707         this.fireEvent("visibilitychange", this, true);
34708     },
34709
34710     closeClicked : function(){
34711         if(this.activePanel){
34712             this.remove(this.activePanel);
34713         }
34714     },
34715
34716     collapseClick : function(e){
34717         if(this.isSlid){
34718            e.stopPropagation();
34719            this.slideIn();
34720         }else{
34721            e.stopPropagation();
34722            this.slideOut();
34723         }
34724     },
34725
34726     /**
34727      * Collapses this region.
34728      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34729      */
34730     collapse : function(skipAnim, skipCheck){
34731         if(this.collapsed) {
34732             return;
34733         }
34734         
34735         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34736             
34737             this.collapsed = true;
34738             if(this.split){
34739                 this.split.el.hide();
34740             }
34741             if(this.config.animate && skipAnim !== true){
34742                 this.fireEvent("invalidated", this);
34743                 this.animateCollapse();
34744             }else{
34745                 this.el.setLocation(-20000,-20000);
34746                 this.el.hide();
34747                 this.collapsedEl.show();
34748                 this.fireEvent("collapsed", this);
34749                 this.fireEvent("invalidated", this);
34750             }
34751         }
34752         
34753     },
34754
34755     animateCollapse : function(){
34756         // overridden
34757     },
34758
34759     /**
34760      * Expands this region if it was previously collapsed.
34761      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34762      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34763      */
34764     expand : function(e, skipAnim){
34765         if(e) {
34766             e.stopPropagation();
34767         }
34768         if(!this.collapsed || this.el.hasActiveFx()) {
34769             return;
34770         }
34771         if(this.isSlid){
34772             this.afterSlideIn();
34773             skipAnim = true;
34774         }
34775         this.collapsed = false;
34776         if(this.config.animate && skipAnim !== true){
34777             this.animateExpand();
34778         }else{
34779             this.el.show();
34780             if(this.split){
34781                 this.split.el.show();
34782             }
34783             this.collapsedEl.setLocation(-2000,-2000);
34784             this.collapsedEl.hide();
34785             this.fireEvent("invalidated", this);
34786             this.fireEvent("expanded", this);
34787         }
34788     },
34789
34790     animateExpand : function(){
34791         // overridden
34792     },
34793
34794     initTabs : function()
34795     {
34796         this.bodyEl.setStyle("overflow", "hidden");
34797         var ts = new Roo.TabPanel(
34798                 this.bodyEl.dom,
34799                 {
34800                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34801                     disableTooltips: this.config.disableTabTips,
34802                     toolbar : this.config.toolbar
34803                 }
34804         );
34805         if(this.config.hideTabs){
34806             ts.stripWrap.setDisplayed(false);
34807         }
34808         this.tabs = ts;
34809         ts.resizeTabs = this.config.resizeTabs === true;
34810         ts.minTabWidth = this.config.minTabWidth || 40;
34811         ts.maxTabWidth = this.config.maxTabWidth || 250;
34812         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34813         ts.monitorResize = false;
34814         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34815         ts.bodyEl.addClass('x-layout-tabs-body');
34816         this.panels.each(this.initPanelAsTab, this);
34817     },
34818
34819     initPanelAsTab : function(panel){
34820         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34821                     this.config.closeOnTab && panel.isClosable());
34822         if(panel.tabTip !== undefined){
34823             ti.setTooltip(panel.tabTip);
34824         }
34825         ti.on("activate", function(){
34826               this.setActivePanel(panel);
34827         }, this);
34828         if(this.config.closeOnTab){
34829             ti.on("beforeclose", function(t, e){
34830                 e.cancel = true;
34831                 this.remove(panel);
34832             }, this);
34833         }
34834         return ti;
34835     },
34836
34837     updatePanelTitle : function(panel, title){
34838         if(this.activePanel == panel){
34839             this.updateTitle(title);
34840         }
34841         if(this.tabs){
34842             var ti = this.tabs.getTab(panel.getEl().id);
34843             ti.setText(title);
34844             if(panel.tabTip !== undefined){
34845                 ti.setTooltip(panel.tabTip);
34846             }
34847         }
34848     },
34849
34850     updateTitle : function(title){
34851         if(this.titleTextEl && !this.config.title){
34852             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34853         }
34854     },
34855
34856     setActivePanel : function(panel){
34857         panel = this.getPanel(panel);
34858         if(this.activePanel && this.activePanel != panel){
34859             this.activePanel.setActiveState(false);
34860         }
34861         this.activePanel = panel;
34862         panel.setActiveState(true);
34863         if(this.panelSize){
34864             panel.setSize(this.panelSize.width, this.panelSize.height);
34865         }
34866         if(this.closeBtn){
34867             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34868         }
34869         this.updateTitle(panel.getTitle());
34870         if(this.tabs){
34871             this.fireEvent("invalidated", this);
34872         }
34873         this.fireEvent("panelactivated", this, panel);
34874     },
34875
34876     /**
34877      * Shows the specified panel.
34878      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34879      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34880      */
34881     showPanel : function(panel)
34882     {
34883         panel = this.getPanel(panel);
34884         if(panel){
34885             if(this.tabs){
34886                 var tab = this.tabs.getTab(panel.getEl().id);
34887                 if(tab.isHidden()){
34888                     this.tabs.unhideTab(tab.id);
34889                 }
34890                 tab.activate();
34891             }else{
34892                 this.setActivePanel(panel);
34893             }
34894         }
34895         return panel;
34896     },
34897
34898     /**
34899      * Get the active panel for this region.
34900      * @return {Roo.ContentPanel} The active panel or null
34901      */
34902     getActivePanel : function(){
34903         return this.activePanel;
34904     },
34905
34906     validateVisibility : function(){
34907         if(this.panels.getCount() < 1){
34908             this.updateTitle("&#160;");
34909             this.closeBtn.hide();
34910             this.hide();
34911         }else{
34912             if(!this.isVisible()){
34913                 this.show();
34914             }
34915         }
34916     },
34917
34918     /**
34919      * Adds the passed ContentPanel(s) to this region.
34920      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34921      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34922      */
34923     add : function(panel){
34924         if(arguments.length > 1){
34925             for(var i = 0, len = arguments.length; i < len; i++) {
34926                 this.add(arguments[i]);
34927             }
34928             return null;
34929         }
34930         if(this.hasPanel(panel)){
34931             this.showPanel(panel);
34932             return panel;
34933         }
34934         panel.setRegion(this);
34935         this.panels.add(panel);
34936         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34937             this.bodyEl.dom.appendChild(panel.getEl().dom);
34938             if(panel.background !== true){
34939                 this.setActivePanel(panel);
34940             }
34941             this.fireEvent("paneladded", this, panel);
34942             return panel;
34943         }
34944         if(!this.tabs){
34945             this.initTabs();
34946         }else{
34947             this.initPanelAsTab(panel);
34948         }
34949         if(panel.background !== true){
34950             this.tabs.activate(panel.getEl().id);
34951         }
34952         this.fireEvent("paneladded", this, panel);
34953         return panel;
34954     },
34955
34956     /**
34957      * Hides the tab for the specified panel.
34958      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34959      */
34960     hidePanel : function(panel){
34961         if(this.tabs && (panel = this.getPanel(panel))){
34962             this.tabs.hideTab(panel.getEl().id);
34963         }
34964     },
34965
34966     /**
34967      * Unhides the tab for a previously hidden panel.
34968      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34969      */
34970     unhidePanel : function(panel){
34971         if(this.tabs && (panel = this.getPanel(panel))){
34972             this.tabs.unhideTab(panel.getEl().id);
34973         }
34974     },
34975
34976     clearPanels : function(){
34977         while(this.panels.getCount() > 0){
34978              this.remove(this.panels.first());
34979         }
34980     },
34981
34982     /**
34983      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34984      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34985      * @param {Boolean} preservePanel Overrides the config preservePanel option
34986      * @return {Roo.ContentPanel} The panel that was removed
34987      */
34988     remove : function(panel, preservePanel){
34989         panel = this.getPanel(panel);
34990         if(!panel){
34991             return null;
34992         }
34993         var e = {};
34994         this.fireEvent("beforeremove", this, panel, e);
34995         if(e.cancel === true){
34996             return null;
34997         }
34998         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34999         var panelId = panel.getId();
35000         this.panels.removeKey(panelId);
35001         if(preservePanel){
35002             document.body.appendChild(panel.getEl().dom);
35003         }
35004         if(this.tabs){
35005             this.tabs.removeTab(panel.getEl().id);
35006         }else if (!preservePanel){
35007             this.bodyEl.dom.removeChild(panel.getEl().dom);
35008         }
35009         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35010             var p = this.panels.first();
35011             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35012             tempEl.appendChild(p.getEl().dom);
35013             this.bodyEl.update("");
35014             this.bodyEl.dom.appendChild(p.getEl().dom);
35015             tempEl = null;
35016             this.updateTitle(p.getTitle());
35017             this.tabs = null;
35018             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35019             this.setActivePanel(p);
35020         }
35021         panel.setRegion(null);
35022         if(this.activePanel == panel){
35023             this.activePanel = null;
35024         }
35025         if(this.config.autoDestroy !== false && preservePanel !== true){
35026             try{panel.destroy();}catch(e){}
35027         }
35028         this.fireEvent("panelremoved", this, panel);
35029         return panel;
35030     },
35031
35032     /**
35033      * Returns the TabPanel component used by this region
35034      * @return {Roo.TabPanel}
35035      */
35036     getTabs : function(){
35037         return this.tabs;
35038     },
35039
35040     createTool : function(parentEl, className){
35041         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35042             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35043         btn.addClassOnOver("x-layout-tools-button-over");
35044         return btn;
35045     }
35046 });/*
35047  * Based on:
35048  * Ext JS Library 1.1.1
35049  * Copyright(c) 2006-2007, Ext JS, LLC.
35050  *
35051  * Originally Released Under LGPL - original licence link has changed is not relivant.
35052  *
35053  * Fork - LGPL
35054  * <script type="text/javascript">
35055  */
35056  
35057
35058
35059 /**
35060  * @class Roo.SplitLayoutRegion
35061  * @extends Roo.LayoutRegion
35062  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35063  */
35064 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35065     this.cursor = cursor;
35066     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35067 };
35068
35069 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35070     splitTip : "Drag to resize.",
35071     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35072     useSplitTips : false,
35073
35074     applyConfig : function(config){
35075         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35076         if(config.split){
35077             if(!this.split){
35078                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35079                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35080                 /** The SplitBar for this region 
35081                 * @type Roo.SplitBar */
35082                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35083                 this.split.on("moved", this.onSplitMove, this);
35084                 this.split.useShim = config.useShim === true;
35085                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35086                 if(this.useSplitTips){
35087                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35088                 }
35089                 if(config.collapsible){
35090                     this.split.el.on("dblclick", this.collapse,  this);
35091                 }
35092             }
35093             if(typeof config.minSize != "undefined"){
35094                 this.split.minSize = config.minSize;
35095             }
35096             if(typeof config.maxSize != "undefined"){
35097                 this.split.maxSize = config.maxSize;
35098             }
35099             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35100                 this.hideSplitter();
35101             }
35102         }
35103     },
35104
35105     getHMaxSize : function(){
35106          var cmax = this.config.maxSize || 10000;
35107          var center = this.mgr.getRegion("center");
35108          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35109     },
35110
35111     getVMaxSize : function(){
35112          var cmax = this.config.maxSize || 10000;
35113          var center = this.mgr.getRegion("center");
35114          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35115     },
35116
35117     onSplitMove : function(split, newSize){
35118         this.fireEvent("resized", this, newSize);
35119     },
35120     
35121     /** 
35122      * Returns the {@link Roo.SplitBar} for this region.
35123      * @return {Roo.SplitBar}
35124      */
35125     getSplitBar : function(){
35126         return this.split;
35127     },
35128     
35129     hide : function(){
35130         this.hideSplitter();
35131         Roo.SplitLayoutRegion.superclass.hide.call(this);
35132     },
35133
35134     hideSplitter : function(){
35135         if(this.split){
35136             this.split.el.setLocation(-2000,-2000);
35137             this.split.el.hide();
35138         }
35139     },
35140
35141     show : function(){
35142         if(this.split){
35143             this.split.el.show();
35144         }
35145         Roo.SplitLayoutRegion.superclass.show.call(this);
35146     },
35147     
35148     beforeSlide: function(){
35149         if(Roo.isGecko){// firefox overflow auto bug workaround
35150             this.bodyEl.clip();
35151             if(this.tabs) {
35152                 this.tabs.bodyEl.clip();
35153             }
35154             if(this.activePanel){
35155                 this.activePanel.getEl().clip();
35156                 
35157                 if(this.activePanel.beforeSlide){
35158                     this.activePanel.beforeSlide();
35159                 }
35160             }
35161         }
35162     },
35163     
35164     afterSlide : function(){
35165         if(Roo.isGecko){// firefox overflow auto bug workaround
35166             this.bodyEl.unclip();
35167             if(this.tabs) {
35168                 this.tabs.bodyEl.unclip();
35169             }
35170             if(this.activePanel){
35171                 this.activePanel.getEl().unclip();
35172                 if(this.activePanel.afterSlide){
35173                     this.activePanel.afterSlide();
35174                 }
35175             }
35176         }
35177     },
35178
35179     initAutoHide : function(){
35180         if(this.autoHide !== false){
35181             if(!this.autoHideHd){
35182                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35183                 this.autoHideHd = {
35184                     "mouseout": function(e){
35185                         if(!e.within(this.el, true)){
35186                             st.delay(500);
35187                         }
35188                     },
35189                     "mouseover" : function(e){
35190                         st.cancel();
35191                     },
35192                     scope : this
35193                 };
35194             }
35195             this.el.on(this.autoHideHd);
35196         }
35197     },
35198
35199     clearAutoHide : function(){
35200         if(this.autoHide !== false){
35201             this.el.un("mouseout", this.autoHideHd.mouseout);
35202             this.el.un("mouseover", this.autoHideHd.mouseover);
35203         }
35204     },
35205
35206     clearMonitor : function(){
35207         Roo.get(document).un("click", this.slideInIf, this);
35208     },
35209
35210     // these names are backwards but not changed for compat
35211     slideOut : function(){
35212         if(this.isSlid || this.el.hasActiveFx()){
35213             return;
35214         }
35215         this.isSlid = true;
35216         if(this.collapseBtn){
35217             this.collapseBtn.hide();
35218         }
35219         this.closeBtnState = this.closeBtn.getStyle('display');
35220         this.closeBtn.hide();
35221         if(this.stickBtn){
35222             this.stickBtn.show();
35223         }
35224         this.el.show();
35225         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35226         this.beforeSlide();
35227         this.el.setStyle("z-index", 10001);
35228         this.el.slideIn(this.getSlideAnchor(), {
35229             callback: function(){
35230                 this.afterSlide();
35231                 this.initAutoHide();
35232                 Roo.get(document).on("click", this.slideInIf, this);
35233                 this.fireEvent("slideshow", this);
35234             },
35235             scope: this,
35236             block: true
35237         });
35238     },
35239
35240     afterSlideIn : function(){
35241         this.clearAutoHide();
35242         this.isSlid = false;
35243         this.clearMonitor();
35244         this.el.setStyle("z-index", "");
35245         if(this.collapseBtn){
35246             this.collapseBtn.show();
35247         }
35248         this.closeBtn.setStyle('display', this.closeBtnState);
35249         if(this.stickBtn){
35250             this.stickBtn.hide();
35251         }
35252         this.fireEvent("slidehide", this);
35253     },
35254
35255     slideIn : function(cb){
35256         if(!this.isSlid || this.el.hasActiveFx()){
35257             Roo.callback(cb);
35258             return;
35259         }
35260         this.isSlid = false;
35261         this.beforeSlide();
35262         this.el.slideOut(this.getSlideAnchor(), {
35263             callback: function(){
35264                 this.el.setLeftTop(-10000, -10000);
35265                 this.afterSlide();
35266                 this.afterSlideIn();
35267                 Roo.callback(cb);
35268             },
35269             scope: this,
35270             block: true
35271         });
35272     },
35273     
35274     slideInIf : function(e){
35275         if(!e.within(this.el)){
35276             this.slideIn();
35277         }
35278     },
35279
35280     animateCollapse : function(){
35281         this.beforeSlide();
35282         this.el.setStyle("z-index", 20000);
35283         var anchor = this.getSlideAnchor();
35284         this.el.slideOut(anchor, {
35285             callback : function(){
35286                 this.el.setStyle("z-index", "");
35287                 this.collapsedEl.slideIn(anchor, {duration:.3});
35288                 this.afterSlide();
35289                 this.el.setLocation(-10000,-10000);
35290                 this.el.hide();
35291                 this.fireEvent("collapsed", this);
35292             },
35293             scope: this,
35294             block: true
35295         });
35296     },
35297
35298     animateExpand : function(){
35299         this.beforeSlide();
35300         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35301         this.el.setStyle("z-index", 20000);
35302         this.collapsedEl.hide({
35303             duration:.1
35304         });
35305         this.el.slideIn(this.getSlideAnchor(), {
35306             callback : function(){
35307                 this.el.setStyle("z-index", "");
35308                 this.afterSlide();
35309                 if(this.split){
35310                     this.split.el.show();
35311                 }
35312                 this.fireEvent("invalidated", this);
35313                 this.fireEvent("expanded", this);
35314             },
35315             scope: this,
35316             block: true
35317         });
35318     },
35319
35320     anchors : {
35321         "west" : "left",
35322         "east" : "right",
35323         "north" : "top",
35324         "south" : "bottom"
35325     },
35326
35327     sanchors : {
35328         "west" : "l",
35329         "east" : "r",
35330         "north" : "t",
35331         "south" : "b"
35332     },
35333
35334     canchors : {
35335         "west" : "tl-tr",
35336         "east" : "tr-tl",
35337         "north" : "tl-bl",
35338         "south" : "bl-tl"
35339     },
35340
35341     getAnchor : function(){
35342         return this.anchors[this.position];
35343     },
35344
35345     getCollapseAnchor : function(){
35346         return this.canchors[this.position];
35347     },
35348
35349     getSlideAnchor : function(){
35350         return this.sanchors[this.position];
35351     },
35352
35353     getAlignAdj : function(){
35354         var cm = this.cmargins;
35355         switch(this.position){
35356             case "west":
35357                 return [0, 0];
35358             break;
35359             case "east":
35360                 return [0, 0];
35361             break;
35362             case "north":
35363                 return [0, 0];
35364             break;
35365             case "south":
35366                 return [0, 0];
35367             break;
35368         }
35369     },
35370
35371     getExpandAdj : function(){
35372         var c = this.collapsedEl, cm = this.cmargins;
35373         switch(this.position){
35374             case "west":
35375                 return [-(cm.right+c.getWidth()+cm.left), 0];
35376             break;
35377             case "east":
35378                 return [cm.right+c.getWidth()+cm.left, 0];
35379             break;
35380             case "north":
35381                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35382             break;
35383             case "south":
35384                 return [0, cm.top+cm.bottom+c.getHeight()];
35385             break;
35386         }
35387     }
35388 });/*
35389  * Based on:
35390  * Ext JS Library 1.1.1
35391  * Copyright(c) 2006-2007, Ext JS, LLC.
35392  *
35393  * Originally Released Under LGPL - original licence link has changed is not relivant.
35394  *
35395  * Fork - LGPL
35396  * <script type="text/javascript">
35397  */
35398 /*
35399  * These classes are private internal classes
35400  */
35401 Roo.CenterLayoutRegion = function(mgr, config){
35402     Roo.LayoutRegion.call(this, mgr, config, "center");
35403     this.visible = true;
35404     this.minWidth = config.minWidth || 20;
35405     this.minHeight = config.minHeight || 20;
35406 };
35407
35408 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35409     hide : function(){
35410         // center panel can't be hidden
35411     },
35412     
35413     show : function(){
35414         // center panel can't be hidden
35415     },
35416     
35417     getMinWidth: function(){
35418         return this.minWidth;
35419     },
35420     
35421     getMinHeight: function(){
35422         return this.minHeight;
35423     }
35424 });
35425
35426
35427 Roo.NorthLayoutRegion = function(mgr, config){
35428     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35429     if(this.split){
35430         this.split.placement = Roo.SplitBar.TOP;
35431         this.split.orientation = Roo.SplitBar.VERTICAL;
35432         this.split.el.addClass("x-layout-split-v");
35433     }
35434     var size = config.initialSize || config.height;
35435     if(typeof size != "undefined"){
35436         this.el.setHeight(size);
35437     }
35438 };
35439 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35440     orientation: Roo.SplitBar.VERTICAL,
35441     getBox : function(){
35442         if(this.collapsed){
35443             return this.collapsedEl.getBox();
35444         }
35445         var box = this.el.getBox();
35446         if(this.split){
35447             box.height += this.split.el.getHeight();
35448         }
35449         return box;
35450     },
35451     
35452     updateBox : function(box){
35453         if(this.split && !this.collapsed){
35454             box.height -= this.split.el.getHeight();
35455             this.split.el.setLeft(box.x);
35456             this.split.el.setTop(box.y+box.height);
35457             this.split.el.setWidth(box.width);
35458         }
35459         if(this.collapsed){
35460             this.updateBody(box.width, null);
35461         }
35462         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35463     }
35464 });
35465
35466 Roo.SouthLayoutRegion = function(mgr, config){
35467     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35468     if(this.split){
35469         this.split.placement = Roo.SplitBar.BOTTOM;
35470         this.split.orientation = Roo.SplitBar.VERTICAL;
35471         this.split.el.addClass("x-layout-split-v");
35472     }
35473     var size = config.initialSize || config.height;
35474     if(typeof size != "undefined"){
35475         this.el.setHeight(size);
35476     }
35477 };
35478 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35479     orientation: Roo.SplitBar.VERTICAL,
35480     getBox : function(){
35481         if(this.collapsed){
35482             return this.collapsedEl.getBox();
35483         }
35484         var box = this.el.getBox();
35485         if(this.split){
35486             var sh = this.split.el.getHeight();
35487             box.height += sh;
35488             box.y -= sh;
35489         }
35490         return box;
35491     },
35492     
35493     updateBox : function(box){
35494         if(this.split && !this.collapsed){
35495             var sh = this.split.el.getHeight();
35496             box.height -= sh;
35497             box.y += sh;
35498             this.split.el.setLeft(box.x);
35499             this.split.el.setTop(box.y-sh);
35500             this.split.el.setWidth(box.width);
35501         }
35502         if(this.collapsed){
35503             this.updateBody(box.width, null);
35504         }
35505         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35506     }
35507 });
35508
35509 Roo.EastLayoutRegion = function(mgr, config){
35510     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35511     if(this.split){
35512         this.split.placement = Roo.SplitBar.RIGHT;
35513         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35514         this.split.el.addClass("x-layout-split-h");
35515     }
35516     var size = config.initialSize || config.width;
35517     if(typeof size != "undefined"){
35518         this.el.setWidth(size);
35519     }
35520 };
35521 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35522     orientation: Roo.SplitBar.HORIZONTAL,
35523     getBox : function(){
35524         if(this.collapsed){
35525             return this.collapsedEl.getBox();
35526         }
35527         var box = this.el.getBox();
35528         if(this.split){
35529             var sw = this.split.el.getWidth();
35530             box.width += sw;
35531             box.x -= sw;
35532         }
35533         return box;
35534     },
35535
35536     updateBox : function(box){
35537         if(this.split && !this.collapsed){
35538             var sw = this.split.el.getWidth();
35539             box.width -= sw;
35540             this.split.el.setLeft(box.x);
35541             this.split.el.setTop(box.y);
35542             this.split.el.setHeight(box.height);
35543             box.x += sw;
35544         }
35545         if(this.collapsed){
35546             this.updateBody(null, box.height);
35547         }
35548         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35549     }
35550 });
35551
35552 Roo.WestLayoutRegion = function(mgr, config){
35553     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35554     if(this.split){
35555         this.split.placement = Roo.SplitBar.LEFT;
35556         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35557         this.split.el.addClass("x-layout-split-h");
35558     }
35559     var size = config.initialSize || config.width;
35560     if(typeof size != "undefined"){
35561         this.el.setWidth(size);
35562     }
35563 };
35564 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35565     orientation: Roo.SplitBar.HORIZONTAL,
35566     getBox : function(){
35567         if(this.collapsed){
35568             return this.collapsedEl.getBox();
35569         }
35570         var box = this.el.getBox();
35571         if(this.split){
35572             box.width += this.split.el.getWidth();
35573         }
35574         return box;
35575     },
35576     
35577     updateBox : function(box){
35578         if(this.split && !this.collapsed){
35579             var sw = this.split.el.getWidth();
35580             box.width -= sw;
35581             this.split.el.setLeft(box.x+box.width);
35582             this.split.el.setTop(box.y);
35583             this.split.el.setHeight(box.height);
35584         }
35585         if(this.collapsed){
35586             this.updateBody(null, box.height);
35587         }
35588         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35589     }
35590 });
35591 /*
35592  * Based on:
35593  * Ext JS Library 1.1.1
35594  * Copyright(c) 2006-2007, Ext JS, LLC.
35595  *
35596  * Originally Released Under LGPL - original licence link has changed is not relivant.
35597  *
35598  * Fork - LGPL
35599  * <script type="text/javascript">
35600  */
35601  
35602  
35603 /*
35604  * Private internal class for reading and applying state
35605  */
35606 Roo.LayoutStateManager = function(layout){
35607      // default empty state
35608      this.state = {
35609         north: {},
35610         south: {},
35611         east: {},
35612         west: {}       
35613     };
35614 };
35615
35616 Roo.LayoutStateManager.prototype = {
35617     init : function(layout, provider){
35618         this.provider = provider;
35619         var state = provider.get(layout.id+"-layout-state");
35620         if(state){
35621             var wasUpdating = layout.isUpdating();
35622             if(!wasUpdating){
35623                 layout.beginUpdate();
35624             }
35625             for(var key in state){
35626                 if(typeof state[key] != "function"){
35627                     var rstate = state[key];
35628                     var r = layout.getRegion(key);
35629                     if(r && rstate){
35630                         if(rstate.size){
35631                             r.resizeTo(rstate.size);
35632                         }
35633                         if(rstate.collapsed == true){
35634                             r.collapse(true);
35635                         }else{
35636                             r.expand(null, true);
35637                         }
35638                     }
35639                 }
35640             }
35641             if(!wasUpdating){
35642                 layout.endUpdate();
35643             }
35644             this.state = state; 
35645         }
35646         this.layout = layout;
35647         layout.on("regionresized", this.onRegionResized, this);
35648         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35649         layout.on("regionexpanded", this.onRegionExpanded, this);
35650     },
35651     
35652     storeState : function(){
35653         this.provider.set(this.layout.id+"-layout-state", this.state);
35654     },
35655     
35656     onRegionResized : function(region, newSize){
35657         this.state[region.getPosition()].size = newSize;
35658         this.storeState();
35659     },
35660     
35661     onRegionCollapsed : function(region){
35662         this.state[region.getPosition()].collapsed = true;
35663         this.storeState();
35664     },
35665     
35666     onRegionExpanded : function(region){
35667         this.state[region.getPosition()].collapsed = false;
35668         this.storeState();
35669     }
35670 };/*
35671  * Based on:
35672  * Ext JS Library 1.1.1
35673  * Copyright(c) 2006-2007, Ext JS, LLC.
35674  *
35675  * Originally Released Under LGPL - original licence link has changed is not relivant.
35676  *
35677  * Fork - LGPL
35678  * <script type="text/javascript">
35679  */
35680 /**
35681  * @class Roo.ContentPanel
35682  * @extends Roo.util.Observable
35683  * @children Roo.form.Form Roo.JsonView Roo.View
35684  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35685  * A basic ContentPanel element.
35686  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35687  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35688  * @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
35689  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35690  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35691  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35692  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35693  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35694  * @cfg {String} title          The title for this panel
35695  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35696  * @cfg {String} url            Calls {@link #setUrl} with this value
35697  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35698  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35699  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35700  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35701  * @cfg {String}    style  Extra style to add to the content panel
35702  * @cfg {Roo.menu.Menu} menu  popup menu
35703
35704  * @constructor
35705  * Create a new ContentPanel.
35706  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35707  * @param {String/Object} config A string to set only the title or a config object
35708  * @param {String} content (optional) Set the HTML content for this panel
35709  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35710  */
35711 Roo.ContentPanel = function(el, config, content){
35712     
35713      
35714     /*
35715     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35716         config = el;
35717         el = Roo.id();
35718     }
35719     if (config && config.parentLayout) { 
35720         el = config.parentLayout.el.createChild(); 
35721     }
35722     */
35723     if(el.autoCreate){ // xtype is available if this is called from factory
35724         config = el;
35725         el = Roo.id();
35726     }
35727     this.el = Roo.get(el);
35728     if(!this.el && config && config.autoCreate){
35729         if(typeof config.autoCreate == "object"){
35730             if(!config.autoCreate.id){
35731                 config.autoCreate.id = config.id||el;
35732             }
35733             this.el = Roo.DomHelper.append(document.body,
35734                         config.autoCreate, true);
35735         }else{
35736             this.el = Roo.DomHelper.append(document.body,
35737                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35738         }
35739     }
35740     
35741     
35742     this.closable = false;
35743     this.loaded = false;
35744     this.active = false;
35745     if(typeof config == "string"){
35746         this.title = config;
35747     }else{
35748         Roo.apply(this, config);
35749     }
35750     
35751     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35752         this.wrapEl = this.el.wrap();
35753         this.toolbar.container = this.el.insertSibling(false, 'before');
35754         this.toolbar = new Roo.Toolbar(this.toolbar);
35755     }
35756     
35757     // xtype created footer. - not sure if will work as we normally have to render first..
35758     if (this.footer && !this.footer.el && this.footer.xtype) {
35759         if (!this.wrapEl) {
35760             this.wrapEl = this.el.wrap();
35761         }
35762     
35763         this.footer.container = this.wrapEl.createChild();
35764          
35765         this.footer = Roo.factory(this.footer, Roo);
35766         
35767     }
35768     
35769     if(this.resizeEl){
35770         this.resizeEl = Roo.get(this.resizeEl, true);
35771     }else{
35772         this.resizeEl = this.el;
35773     }
35774     // handle view.xtype
35775     
35776  
35777     
35778     
35779     this.addEvents({
35780         /**
35781          * @event activate
35782          * Fires when this panel is activated. 
35783          * @param {Roo.ContentPanel} this
35784          */
35785         "activate" : true,
35786         /**
35787          * @event deactivate
35788          * Fires when this panel is activated. 
35789          * @param {Roo.ContentPanel} this
35790          */
35791         "deactivate" : true,
35792
35793         /**
35794          * @event resize
35795          * Fires when this panel is resized if fitToFrame is true.
35796          * @param {Roo.ContentPanel} this
35797          * @param {Number} width The width after any component adjustments
35798          * @param {Number} height The height after any component adjustments
35799          */
35800         "resize" : true,
35801         
35802          /**
35803          * @event render
35804          * Fires when this tab is created
35805          * @param {Roo.ContentPanel} this
35806          */
35807         "render" : true
35808          
35809         
35810     });
35811     
35812
35813     
35814     
35815     if(this.autoScroll){
35816         this.resizeEl.setStyle("overflow", "auto");
35817     } else {
35818         // fix randome scrolling
35819         this.el.on('scroll', function() {
35820             Roo.log('fix random scolling');
35821             this.scrollTo('top',0); 
35822         });
35823     }
35824     content = content || this.content;
35825     if(content){
35826         this.setContent(content);
35827     }
35828     if(config && config.url){
35829         this.setUrl(this.url, this.params, this.loadOnce);
35830     }
35831     
35832     
35833     
35834     Roo.ContentPanel.superclass.constructor.call(this);
35835     
35836     if (this.view && typeof(this.view.xtype) != 'undefined') {
35837         this.view.el = this.el.appendChild(document.createElement("div"));
35838         this.view = Roo.factory(this.view); 
35839         this.view.render  &&  this.view.render(false, '');  
35840     }
35841     
35842     
35843     this.fireEvent('render', this);
35844 };
35845
35846 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35847     tabTip:'',
35848     setRegion : function(region){
35849         this.region = region;
35850         if(region){
35851            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35852         }else{
35853            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35854         } 
35855     },
35856     
35857     /**
35858      * Returns the toolbar for this Panel if one was configured. 
35859      * @return {Roo.Toolbar} 
35860      */
35861     getToolbar : function(){
35862         return this.toolbar;
35863     },
35864     
35865     setActiveState : function(active){
35866         this.active = active;
35867         if(!active){
35868             this.fireEvent("deactivate", this);
35869         }else{
35870             this.fireEvent("activate", this);
35871         }
35872     },
35873     /**
35874      * Updates this panel's element
35875      * @param {String} content The new content
35876      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35877     */
35878     setContent : function(content, loadScripts){
35879         this.el.update(content, loadScripts);
35880     },
35881
35882     ignoreResize : function(w, h){
35883         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35884             return true;
35885         }else{
35886             this.lastSize = {width: w, height: h};
35887             return false;
35888         }
35889     },
35890     /**
35891      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35892      * @return {Roo.UpdateManager} The UpdateManager
35893      */
35894     getUpdateManager : function(){
35895         return this.el.getUpdateManager();
35896     },
35897      /**
35898      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35899      * @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:
35900 <pre><code>
35901 panel.load({
35902     url: "your-url.php",
35903     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35904     callback: yourFunction,
35905     scope: yourObject, //(optional scope)
35906     discardUrl: false,
35907     nocache: false,
35908     text: "Loading...",
35909     timeout: 30,
35910     scripts: false
35911 });
35912 </code></pre>
35913      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35914      * 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.
35915      * @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}
35916      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35917      * @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.
35918      * @return {Roo.ContentPanel} this
35919      */
35920     load : function(){
35921         var um = this.el.getUpdateManager();
35922         um.update.apply(um, arguments);
35923         return this;
35924     },
35925
35926
35927     /**
35928      * 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.
35929      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35930      * @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)
35931      * @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)
35932      * @return {Roo.UpdateManager} The UpdateManager
35933      */
35934     setUrl : function(url, params, loadOnce){
35935         if(this.refreshDelegate){
35936             this.removeListener("activate", this.refreshDelegate);
35937         }
35938         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35939         this.on("activate", this.refreshDelegate);
35940         return this.el.getUpdateManager();
35941     },
35942     
35943     _handleRefresh : function(url, params, loadOnce){
35944         if(!loadOnce || !this.loaded){
35945             var updater = this.el.getUpdateManager();
35946             updater.update(url, params, this._setLoaded.createDelegate(this));
35947         }
35948     },
35949     
35950     _setLoaded : function(){
35951         this.loaded = true;
35952     }, 
35953     
35954     /**
35955      * Returns this panel's id
35956      * @return {String} 
35957      */
35958     getId : function(){
35959         return this.el.id;
35960     },
35961     
35962     /** 
35963      * Returns this panel's element - used by regiosn to add.
35964      * @return {Roo.Element} 
35965      */
35966     getEl : function(){
35967         return this.wrapEl || this.el;
35968     },
35969     
35970     adjustForComponents : function(width, height)
35971     {
35972         //Roo.log('adjustForComponents ');
35973         if(this.resizeEl != this.el){
35974             width -= this.el.getFrameWidth('lr');
35975             height -= this.el.getFrameWidth('tb');
35976         }
35977         if(this.toolbar){
35978             var te = this.toolbar.getEl();
35979             height -= te.getHeight();
35980             te.setWidth(width);
35981         }
35982         if(this.footer){
35983             var te = this.footer.getEl();
35984             //Roo.log("footer:" + te.getHeight());
35985             
35986             height -= te.getHeight();
35987             te.setWidth(width);
35988         }
35989         
35990         
35991         if(this.adjustments){
35992             width += this.adjustments[0];
35993             height += this.adjustments[1];
35994         }
35995         return {"width": width, "height": height};
35996     },
35997     
35998     setSize : function(width, height){
35999         if(this.fitToFrame && !this.ignoreResize(width, height)){
36000             if(this.fitContainer && this.resizeEl != this.el){
36001                 this.el.setSize(width, height);
36002             }
36003             var size = this.adjustForComponents(width, height);
36004             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36005             this.fireEvent('resize', this, size.width, size.height);
36006         }
36007     },
36008     
36009     /**
36010      * Returns this panel's title
36011      * @return {String} 
36012      */
36013     getTitle : function(){
36014         return this.title;
36015     },
36016     
36017     /**
36018      * Set this panel's title
36019      * @param {String} title
36020      */
36021     setTitle : function(title){
36022         this.title = title;
36023         if(this.region){
36024             this.region.updatePanelTitle(this, title);
36025         }
36026     },
36027     
36028     /**
36029      * Returns true is this panel was configured to be closable
36030      * @return {Boolean} 
36031      */
36032     isClosable : function(){
36033         return this.closable;
36034     },
36035     
36036     beforeSlide : function(){
36037         this.el.clip();
36038         this.resizeEl.clip();
36039     },
36040     
36041     afterSlide : function(){
36042         this.el.unclip();
36043         this.resizeEl.unclip();
36044     },
36045     
36046     /**
36047      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36048      *   Will fail silently if the {@link #setUrl} method has not been called.
36049      *   This does not activate the panel, just updates its content.
36050      */
36051     refresh : function(){
36052         if(this.refreshDelegate){
36053            this.loaded = false;
36054            this.refreshDelegate();
36055         }
36056     },
36057     
36058     /**
36059      * Destroys this panel
36060      */
36061     destroy : function(){
36062         this.el.removeAllListeners();
36063         var tempEl = document.createElement("span");
36064         tempEl.appendChild(this.el.dom);
36065         tempEl.innerHTML = "";
36066         this.el.remove();
36067         this.el = null;
36068     },
36069     
36070     /**
36071      * form - if the content panel contains a form - this is a reference to it.
36072      * @type {Roo.form.Form}
36073      */
36074     form : false,
36075     /**
36076      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36077      *    This contains a reference to it.
36078      * @type {Roo.View}
36079      */
36080     view : false,
36081     
36082       /**
36083      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36084      * <pre><code>
36085
36086 layout.addxtype({
36087        xtype : 'Form',
36088        items: [ .... ]
36089    }
36090 );
36091
36092 </code></pre>
36093      * @param {Object} cfg Xtype definition of item to add.
36094      */
36095     
36096     addxtype : function(cfg) {
36097         // add form..
36098         if (cfg.xtype.match(/^Form$/)) {
36099             
36100             var el;
36101             //if (this.footer) {
36102             //    el = this.footer.container.insertSibling(false, 'before');
36103             //} else {
36104                 el = this.el.createChild();
36105             //}
36106
36107             this.form = new  Roo.form.Form(cfg);
36108             
36109             
36110             if ( this.form.allItems.length) {
36111                 this.form.render(el.dom);
36112             }
36113             return this.form;
36114         }
36115         // should only have one of theses..
36116         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36117             // views.. should not be just added - used named prop 'view''
36118             
36119             cfg.el = this.el.appendChild(document.createElement("div"));
36120             // factory?
36121             
36122             var ret = new Roo.factory(cfg);
36123              
36124              ret.render && ret.render(false, ''); // render blank..
36125             this.view = ret;
36126             return ret;
36127         }
36128         return false;
36129     }
36130 });
36131
36132 /**
36133  * @class Roo.GridPanel
36134  * @extends Roo.ContentPanel
36135  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36136  * @constructor
36137  * Create a new GridPanel.
36138  * @cfg {Roo.grid.Grid} grid The grid for this panel
36139  */
36140 Roo.GridPanel = function(grid, config){
36141     
36142     // universal ctor...
36143     if (typeof(grid.grid) != 'undefined') {
36144         config = grid;
36145         grid = config.grid;
36146     }
36147     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36148         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36149         
36150     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36151     
36152     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36153     
36154     if(this.toolbar){
36155         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36156     }
36157     // xtype created footer. - not sure if will work as we normally have to render first..
36158     if (this.footer && !this.footer.el && this.footer.xtype) {
36159         
36160         this.footer.container = this.grid.getView().getFooterPanel(true);
36161         this.footer.dataSource = this.grid.dataSource;
36162         this.footer = Roo.factory(this.footer, Roo);
36163         
36164     }
36165     
36166     grid.monitorWindowResize = false; // turn off autosizing
36167     grid.autoHeight = false;
36168     grid.autoWidth = false;
36169     this.grid = grid;
36170     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36171 };
36172
36173 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36174     getId : function(){
36175         return this.grid.id;
36176     },
36177     
36178     /**
36179      * Returns the grid for this panel
36180      * @return {Roo.grid.Grid} 
36181      */
36182     getGrid : function(){
36183         return this.grid;    
36184     },
36185     
36186     setSize : function(width, height){
36187         if(!this.ignoreResize(width, height)){
36188             var grid = this.grid;
36189             var size = this.adjustForComponents(width, height);
36190             grid.getGridEl().setSize(size.width, size.height);
36191             grid.autoSize();
36192         }
36193     },
36194     
36195     beforeSlide : function(){
36196         this.grid.getView().scroller.clip();
36197     },
36198     
36199     afterSlide : function(){
36200         this.grid.getView().scroller.unclip();
36201     },
36202     
36203     destroy : function(){
36204         this.grid.destroy();
36205         delete this.grid;
36206         Roo.GridPanel.superclass.destroy.call(this); 
36207     }
36208 });
36209
36210
36211 /**
36212  * @class Roo.NestedLayoutPanel
36213  * @extends Roo.ContentPanel
36214  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36215  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
36216  *
36217  * 
36218  * @constructor
36219  * Create a new NestedLayoutPanel.
36220  * 
36221  * 
36222  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36223  * @param {String/Object} config A string to set only the title or a config object
36224  */
36225 Roo.NestedLayoutPanel = function(layout, config)
36226 {
36227     // construct with only one argument..
36228     /* FIXME - implement nicer consturctors
36229     if (layout.layout) {
36230         config = layout;
36231         layout = config.layout;
36232         delete config.layout;
36233     }
36234     if (layout.xtype && !layout.getEl) {
36235         // then layout needs constructing..
36236         layout = Roo.factory(layout, Roo);
36237     }
36238     */
36239     
36240     
36241     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36242     
36243     layout.monitorWindowResize = false; // turn off autosizing
36244     this.layout = layout;
36245     this.layout.getEl().addClass("x-layout-nested-layout");
36246     
36247     
36248     
36249     
36250 };
36251
36252 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36253
36254     setSize : function(width, height){
36255         if(!this.ignoreResize(width, height)){
36256             var size = this.adjustForComponents(width, height);
36257             var el = this.layout.getEl();
36258             el.setSize(size.width, size.height);
36259             var touch = el.dom.offsetWidth;
36260             this.layout.layout();
36261             // ie requires a double layout on the first pass
36262             if(Roo.isIE && !this.initialized){
36263                 this.initialized = true;
36264                 this.layout.layout();
36265             }
36266         }
36267     },
36268     
36269     // activate all subpanels if not currently active..
36270     
36271     setActiveState : function(active){
36272         this.active = active;
36273         if(!active){
36274             this.fireEvent("deactivate", this);
36275             return;
36276         }
36277         
36278         this.fireEvent("activate", this);
36279         // not sure if this should happen before or after..
36280         if (!this.layout) {
36281             return; // should not happen..
36282         }
36283         var reg = false;
36284         for (var r in this.layout.regions) {
36285             reg = this.layout.getRegion(r);
36286             if (reg.getActivePanel()) {
36287                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36288                 reg.setActivePanel(reg.getActivePanel());
36289                 continue;
36290             }
36291             if (!reg.panels.length) {
36292                 continue;
36293             }
36294             reg.showPanel(reg.getPanel(0));
36295         }
36296         
36297         
36298         
36299         
36300     },
36301     
36302     /**
36303      * Returns the nested BorderLayout for this panel
36304      * @return {Roo.BorderLayout} 
36305      */
36306     getLayout : function(){
36307         return this.layout;
36308     },
36309     
36310      /**
36311      * Adds a xtype elements to the layout of the nested panel
36312      * <pre><code>
36313
36314 panel.addxtype({
36315        xtype : 'ContentPanel',
36316        region: 'west',
36317        items: [ .... ]
36318    }
36319 );
36320
36321 panel.addxtype({
36322         xtype : 'NestedLayoutPanel',
36323         region: 'west',
36324         layout: {
36325            center: { },
36326            west: { }   
36327         },
36328         items : [ ... list of content panels or nested layout panels.. ]
36329    }
36330 );
36331 </code></pre>
36332      * @param {Object} cfg Xtype definition of item to add.
36333      */
36334     addxtype : function(cfg) {
36335         return this.layout.addxtype(cfg);
36336     
36337     }
36338 });
36339
36340 Roo.ScrollPanel = function(el, config, content){
36341     config = config || {};
36342     config.fitToFrame = true;
36343     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36344     
36345     this.el.dom.style.overflow = "hidden";
36346     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36347     this.el.removeClass("x-layout-inactive-content");
36348     this.el.on("mousewheel", this.onWheel, this);
36349
36350     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36351     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36352     up.unselectable(); down.unselectable();
36353     up.on("click", this.scrollUp, this);
36354     down.on("click", this.scrollDown, this);
36355     up.addClassOnOver("x-scroller-btn-over");
36356     down.addClassOnOver("x-scroller-btn-over");
36357     up.addClassOnClick("x-scroller-btn-click");
36358     down.addClassOnClick("x-scroller-btn-click");
36359     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36360
36361     this.resizeEl = this.el;
36362     this.el = wrap; this.up = up; this.down = down;
36363 };
36364
36365 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36366     increment : 100,
36367     wheelIncrement : 5,
36368     scrollUp : function(){
36369         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36370     },
36371
36372     scrollDown : function(){
36373         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36374     },
36375
36376     afterScroll : function(){
36377         var el = this.resizeEl;
36378         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36379         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36380         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36381     },
36382
36383     setSize : function(){
36384         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36385         this.afterScroll();
36386     },
36387
36388     onWheel : function(e){
36389         var d = e.getWheelDelta();
36390         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36391         this.afterScroll();
36392         e.stopEvent();
36393     },
36394
36395     setContent : function(content, loadScripts){
36396         this.resizeEl.update(content, loadScripts);
36397     }
36398
36399 });
36400
36401
36402
36403 /**
36404  * @class Roo.TreePanel
36405  * @extends Roo.ContentPanel
36406  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36407  * Treepanel component
36408  * 
36409  * @constructor
36410  * Create a new TreePanel. - defaults to fit/scoll contents.
36411  * @param {String/Object} config A string to set only the panel's title, or a config object
36412  */
36413 Roo.TreePanel = function(config){
36414     var el = config.el;
36415     var tree = config.tree;
36416     delete config.tree; 
36417     delete config.el; // hopefull!
36418     
36419     // wrapper for IE7 strict & safari scroll issue
36420     
36421     var treeEl = el.createChild();
36422     config.resizeEl = treeEl;
36423     
36424     
36425     
36426     Roo.TreePanel.superclass.constructor.call(this, el, config);
36427  
36428  
36429     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36430     //console.log(tree);
36431     this.on('activate', function()
36432     {
36433         if (this.tree.rendered) {
36434             return;
36435         }
36436         //console.log('render tree');
36437         this.tree.render();
36438     });
36439     // this should not be needed.. - it's actually the 'el' that resizes?
36440     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36441     
36442     //this.on('resize',  function (cp, w, h) {
36443     //        this.tree.innerCt.setWidth(w);
36444     //        this.tree.innerCt.setHeight(h);
36445     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36446     //});
36447
36448         
36449     
36450 };
36451
36452 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36453     fitToFrame : true,
36454     autoScroll : true,
36455     /*
36456      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36457      */
36458     tree : false
36459
36460 });
36461
36462
36463
36464
36465
36466
36467
36468
36469
36470
36471
36472 /*
36473  * Based on:
36474  * Ext JS Library 1.1.1
36475  * Copyright(c) 2006-2007, Ext JS, LLC.
36476  *
36477  * Originally Released Under LGPL - original licence link has changed is not relivant.
36478  *
36479  * Fork - LGPL
36480  * <script type="text/javascript">
36481  */
36482  
36483
36484 /**
36485  * @class Roo.ReaderLayout
36486  * @extends Roo.BorderLayout
36487  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36488  * center region containing two nested regions (a top one for a list view and one for item preview below),
36489  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36490  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36491  * expedites the setup of the overall layout and regions for this common application style.
36492  * Example:
36493  <pre><code>
36494 var reader = new Roo.ReaderLayout();
36495 var CP = Roo.ContentPanel;  // shortcut for adding
36496
36497 reader.beginUpdate();
36498 reader.add("north", new CP("north", "North"));
36499 reader.add("west", new CP("west", {title: "West"}));
36500 reader.add("east", new CP("east", {title: "East"}));
36501
36502 reader.regions.listView.add(new CP("listView", "List"));
36503 reader.regions.preview.add(new CP("preview", "Preview"));
36504 reader.endUpdate();
36505 </code></pre>
36506 * @constructor
36507 * Create a new ReaderLayout
36508 * @param {Object} config Configuration options
36509 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36510 * document.body if omitted)
36511 */
36512 Roo.ReaderLayout = function(config, renderTo){
36513     var c = config || {size:{}};
36514     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36515         north: c.north !== false ? Roo.apply({
36516             split:false,
36517             initialSize: 32,
36518             titlebar: false
36519         }, c.north) : false,
36520         west: c.west !== false ? Roo.apply({
36521             split:true,
36522             initialSize: 200,
36523             minSize: 175,
36524             maxSize: 400,
36525             titlebar: true,
36526             collapsible: true,
36527             animate: true,
36528             margins:{left:5,right:0,bottom:5,top:5},
36529             cmargins:{left:5,right:5,bottom:5,top:5}
36530         }, c.west) : false,
36531         east: c.east !== false ? Roo.apply({
36532             split:true,
36533             initialSize: 200,
36534             minSize: 175,
36535             maxSize: 400,
36536             titlebar: true,
36537             collapsible: true,
36538             animate: true,
36539             margins:{left:0,right:5,bottom:5,top:5},
36540             cmargins:{left:5,right:5,bottom:5,top:5}
36541         }, c.east) : false,
36542         center: Roo.apply({
36543             tabPosition: 'top',
36544             autoScroll:false,
36545             closeOnTab: true,
36546             titlebar:false,
36547             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36548         }, c.center)
36549     });
36550
36551     this.el.addClass('x-reader');
36552
36553     this.beginUpdate();
36554
36555     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36556         south: c.preview !== false ? Roo.apply({
36557             split:true,
36558             initialSize: 200,
36559             minSize: 100,
36560             autoScroll:true,
36561             collapsible:true,
36562             titlebar: true,
36563             cmargins:{top:5,left:0, right:0, bottom:0}
36564         }, c.preview) : false,
36565         center: Roo.apply({
36566             autoScroll:false,
36567             titlebar:false,
36568             minHeight:200
36569         }, c.listView)
36570     });
36571     this.add('center', new Roo.NestedLayoutPanel(inner,
36572             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36573
36574     this.endUpdate();
36575
36576     this.regions.preview = inner.getRegion('south');
36577     this.regions.listView = inner.getRegion('center');
36578 };
36579
36580 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36581  * Based on:
36582  * Ext JS Library 1.1.1
36583  * Copyright(c) 2006-2007, Ext JS, LLC.
36584  *
36585  * Originally Released Under LGPL - original licence link has changed is not relivant.
36586  *
36587  * Fork - LGPL
36588  * <script type="text/javascript">
36589  */
36590  
36591 /**
36592  * @class Roo.grid.Grid
36593  * @extends Roo.util.Observable
36594  * This class represents the primary interface of a component based grid control.
36595  * <br><br>Usage:<pre><code>
36596  var grid = new Roo.grid.Grid("my-container-id", {
36597      ds: myDataStore,
36598      cm: myColModel,
36599      selModel: mySelectionModel,
36600      autoSizeColumns: true,
36601      monitorWindowResize: false,
36602      trackMouseOver: true
36603  });
36604  // set any options
36605  grid.render();
36606  * </code></pre>
36607  * <b>Common Problems:</b><br/>
36608  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36609  * element will correct this<br/>
36610  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36611  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36612  * are unpredictable.<br/>
36613  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36614  * grid to calculate dimensions/offsets.<br/>
36615   * @constructor
36616  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36617  * The container MUST have some type of size defined for the grid to fill. The container will be
36618  * automatically set to position relative if it isn't already.
36619  * @param {Object} config A config object that sets properties on this grid.
36620  */
36621 Roo.grid.Grid = function(container, config){
36622         // initialize the container
36623         this.container = Roo.get(container);
36624         this.container.update("");
36625         this.container.setStyle("overflow", "hidden");
36626     this.container.addClass('x-grid-container');
36627
36628     this.id = this.container.id;
36629
36630     Roo.apply(this, config);
36631     // check and correct shorthanded configs
36632     if(this.ds){
36633         this.dataSource = this.ds;
36634         delete this.ds;
36635     }
36636     if(this.cm){
36637         this.colModel = this.cm;
36638         delete this.cm;
36639     }
36640     if(this.sm){
36641         this.selModel = this.sm;
36642         delete this.sm;
36643     }
36644
36645     if (this.selModel) {
36646         this.selModel = Roo.factory(this.selModel, Roo.grid);
36647         this.sm = this.selModel;
36648         this.sm.xmodule = this.xmodule || false;
36649     }
36650     if (typeof(this.colModel.config) == 'undefined') {
36651         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36652         this.cm = this.colModel;
36653         this.cm.xmodule = this.xmodule || false;
36654     }
36655     if (this.dataSource) {
36656         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36657         this.ds = this.dataSource;
36658         this.ds.xmodule = this.xmodule || false;
36659          
36660     }
36661     
36662     
36663     
36664     if(this.width){
36665         this.container.setWidth(this.width);
36666     }
36667
36668     if(this.height){
36669         this.container.setHeight(this.height);
36670     }
36671     /** @private */
36672         this.addEvents({
36673         // raw events
36674         /**
36675          * @event click
36676          * The raw click event for the entire grid.
36677          * @param {Roo.EventObject} e
36678          */
36679         "click" : true,
36680         /**
36681          * @event dblclick
36682          * The raw dblclick event for the entire grid.
36683          * @param {Roo.EventObject} e
36684          */
36685         "dblclick" : true,
36686         /**
36687          * @event contextmenu
36688          * The raw contextmenu event for the entire grid.
36689          * @param {Roo.EventObject} e
36690          */
36691         "contextmenu" : true,
36692         /**
36693          * @event mousedown
36694          * The raw mousedown event for the entire grid.
36695          * @param {Roo.EventObject} e
36696          */
36697         "mousedown" : true,
36698         /**
36699          * @event mouseup
36700          * The raw mouseup event for the entire grid.
36701          * @param {Roo.EventObject} e
36702          */
36703         "mouseup" : true,
36704         /**
36705          * @event mouseover
36706          * The raw mouseover event for the entire grid.
36707          * @param {Roo.EventObject} e
36708          */
36709         "mouseover" : true,
36710         /**
36711          * @event mouseout
36712          * The raw mouseout event for the entire grid.
36713          * @param {Roo.EventObject} e
36714          */
36715         "mouseout" : true,
36716         /**
36717          * @event keypress
36718          * The raw keypress event for the entire grid.
36719          * @param {Roo.EventObject} e
36720          */
36721         "keypress" : true,
36722         /**
36723          * @event keydown
36724          * The raw keydown event for the entire grid.
36725          * @param {Roo.EventObject} e
36726          */
36727         "keydown" : true,
36728
36729         // custom events
36730
36731         /**
36732          * @event cellclick
36733          * Fires when a cell is clicked
36734          * @param {Grid} this
36735          * @param {Number} rowIndex
36736          * @param {Number} columnIndex
36737          * @param {Roo.EventObject} e
36738          */
36739         "cellclick" : true,
36740         /**
36741          * @event celldblclick
36742          * Fires when a cell is double clicked
36743          * @param {Grid} this
36744          * @param {Number} rowIndex
36745          * @param {Number} columnIndex
36746          * @param {Roo.EventObject} e
36747          */
36748         "celldblclick" : true,
36749         /**
36750          * @event rowclick
36751          * Fires when a row is clicked
36752          * @param {Grid} this
36753          * @param {Number} rowIndex
36754          * @param {Roo.EventObject} e
36755          */
36756         "rowclick" : true,
36757         /**
36758          * @event rowdblclick
36759          * Fires when a row is double clicked
36760          * @param {Grid} this
36761          * @param {Number} rowIndex
36762          * @param {Roo.EventObject} e
36763          */
36764         "rowdblclick" : true,
36765         /**
36766          * @event headerclick
36767          * Fires when a header is clicked
36768          * @param {Grid} this
36769          * @param {Number} columnIndex
36770          * @param {Roo.EventObject} e
36771          */
36772         "headerclick" : true,
36773         /**
36774          * @event headerdblclick
36775          * Fires when a header cell is double clicked
36776          * @param {Grid} this
36777          * @param {Number} columnIndex
36778          * @param {Roo.EventObject} e
36779          */
36780         "headerdblclick" : true,
36781         /**
36782          * @event rowcontextmenu
36783          * Fires when a row is right clicked
36784          * @param {Grid} this
36785          * @param {Number} rowIndex
36786          * @param {Roo.EventObject} e
36787          */
36788         "rowcontextmenu" : true,
36789         /**
36790          * @event cellcontextmenu
36791          * Fires when a cell is right clicked
36792          * @param {Grid} this
36793          * @param {Number} rowIndex
36794          * @param {Number} cellIndex
36795          * @param {Roo.EventObject} e
36796          */
36797          "cellcontextmenu" : true,
36798         /**
36799          * @event headercontextmenu
36800          * Fires when a header is right clicked
36801          * @param {Grid} this
36802          * @param {Number} columnIndex
36803          * @param {Roo.EventObject} e
36804          */
36805         "headercontextmenu" : true,
36806         /**
36807          * @event bodyscroll
36808          * Fires when the body element is scrolled
36809          * @param {Number} scrollLeft
36810          * @param {Number} scrollTop
36811          */
36812         "bodyscroll" : true,
36813         /**
36814          * @event columnresize
36815          * Fires when the user resizes a column
36816          * @param {Number} columnIndex
36817          * @param {Number} newSize
36818          */
36819         "columnresize" : true,
36820         /**
36821          * @event columnmove
36822          * Fires when the user moves a column
36823          * @param {Number} oldIndex
36824          * @param {Number} newIndex
36825          */
36826         "columnmove" : true,
36827         /**
36828          * @event startdrag
36829          * Fires when row(s) start being dragged
36830          * @param {Grid} this
36831          * @param {Roo.GridDD} dd The drag drop object
36832          * @param {event} e The raw browser event
36833          */
36834         "startdrag" : true,
36835         /**
36836          * @event enddrag
36837          * Fires when a drag operation is complete
36838          * @param {Grid} this
36839          * @param {Roo.GridDD} dd The drag drop object
36840          * @param {event} e The raw browser event
36841          */
36842         "enddrag" : true,
36843         /**
36844          * @event dragdrop
36845          * Fires when dragged row(s) are dropped on a valid DD target
36846          * @param {Grid} this
36847          * @param {Roo.GridDD} dd The drag drop object
36848          * @param {String} targetId The target drag drop object
36849          * @param {event} e The raw browser event
36850          */
36851         "dragdrop" : true,
36852         /**
36853          * @event dragover
36854          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36855          * @param {Grid} this
36856          * @param {Roo.GridDD} dd The drag drop object
36857          * @param {String} targetId The target drag drop object
36858          * @param {event} e The raw browser event
36859          */
36860         "dragover" : true,
36861         /**
36862          * @event dragenter
36863          *  Fires when the dragged row(s) first cross another DD target while being dragged
36864          * @param {Grid} this
36865          * @param {Roo.GridDD} dd The drag drop object
36866          * @param {String} targetId The target drag drop object
36867          * @param {event} e The raw browser event
36868          */
36869         "dragenter" : true,
36870         /**
36871          * @event dragout
36872          * Fires when the dragged row(s) leave another DD target while being dragged
36873          * @param {Grid} this
36874          * @param {Roo.GridDD} dd The drag drop object
36875          * @param {String} targetId The target drag drop object
36876          * @param {event} e The raw browser event
36877          */
36878         "dragout" : true,
36879         /**
36880          * @event rowclass
36881          * Fires when a row is rendered, so you can change add a style to it.
36882          * @param {GridView} gridview   The grid view
36883          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36884          */
36885         'rowclass' : true,
36886
36887         /**
36888          * @event render
36889          * Fires when the grid is rendered
36890          * @param {Grid} grid
36891          */
36892         'render' : true
36893     });
36894
36895     Roo.grid.Grid.superclass.constructor.call(this);
36896 };
36897 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36898     
36899     /**
36900          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36901          */
36902         /**
36903          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36904          */
36905         /**
36906          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36907          */
36908         /**
36909          * @cfg {Roo.grid.Store} ds The data store for the grid
36910          */
36911         /**
36912          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36913          */
36914         /**
36915      * @cfg {String} ddGroup - drag drop group.
36916      */
36917       /**
36918      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36919      */
36920
36921     /**
36922      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36923      */
36924     minColumnWidth : 25,
36925
36926     /**
36927      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36928      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36929      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36930      */
36931     autoSizeColumns : false,
36932
36933     /**
36934      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36935      */
36936     autoSizeHeaders : true,
36937
36938     /**
36939      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36940      */
36941     monitorWindowResize : true,
36942
36943     /**
36944      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36945      * rows measured to get a columns size. Default is 0 (all rows).
36946      */
36947     maxRowsToMeasure : 0,
36948
36949     /**
36950      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36951      */
36952     trackMouseOver : true,
36953
36954     /**
36955     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36956     */
36957       /**
36958     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
36959     */
36960     
36961     /**
36962     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36963     */
36964     enableDragDrop : false,
36965     
36966     /**
36967     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36968     */
36969     enableColumnMove : true,
36970     
36971     /**
36972     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36973     */
36974     enableColumnHide : true,
36975     
36976     /**
36977     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36978     */
36979     enableRowHeightSync : false,
36980     
36981     /**
36982     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36983     */
36984     stripeRows : true,
36985     
36986     /**
36987     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36988     */
36989     autoHeight : false,
36990
36991     /**
36992      * @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.
36993      */
36994     autoExpandColumn : false,
36995
36996     /**
36997     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36998     * Default is 50.
36999     */
37000     autoExpandMin : 50,
37001
37002     /**
37003     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37004     */
37005     autoExpandMax : 1000,
37006
37007     /**
37008     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37009     */
37010     view : null,
37011
37012     /**
37013     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37014     */
37015     loadMask : false,
37016     /**
37017     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37018     */
37019     dropTarget: false,
37020      /**
37021     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37022     */ 
37023     sortColMenu : false,
37024     
37025     // private
37026     rendered : false,
37027
37028     /**
37029     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37030     * of a fixed width. Default is false.
37031     */
37032     /**
37033     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37034     */
37035     
37036     
37037     /**
37038     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37039     * %0 is replaced with the number of selected rows.
37040     */
37041     ddText : "{0} selected row{1}",
37042     
37043     
37044     /**
37045      * Called once after all setup has been completed and the grid is ready to be rendered.
37046      * @return {Roo.grid.Grid} this
37047      */
37048     render : function()
37049     {
37050         var c = this.container;
37051         // try to detect autoHeight/width mode
37052         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37053             this.autoHeight = true;
37054         }
37055         var view = this.getView();
37056         view.init(this);
37057
37058         c.on("click", this.onClick, this);
37059         c.on("dblclick", this.onDblClick, this);
37060         c.on("contextmenu", this.onContextMenu, this);
37061         c.on("keydown", this.onKeyDown, this);
37062         if (Roo.isTouch) {
37063             c.on("touchstart", this.onTouchStart, this);
37064         }
37065
37066         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37067
37068         this.getSelectionModel().init(this);
37069
37070         view.render();
37071
37072         if(this.loadMask){
37073             this.loadMask = new Roo.LoadMask(this.container,
37074                     Roo.apply({store:this.dataSource}, this.loadMask));
37075         }
37076         
37077         
37078         if (this.toolbar && this.toolbar.xtype) {
37079             this.toolbar.container = this.getView().getHeaderPanel(true);
37080             this.toolbar = new Roo.Toolbar(this.toolbar);
37081         }
37082         if (this.footer && this.footer.xtype) {
37083             this.footer.dataSource = this.getDataSource();
37084             this.footer.container = this.getView().getFooterPanel(true);
37085             this.footer = Roo.factory(this.footer, Roo);
37086         }
37087         if (this.dropTarget && this.dropTarget.xtype) {
37088             delete this.dropTarget.xtype;
37089             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37090         }
37091         
37092         
37093         this.rendered = true;
37094         this.fireEvent('render', this);
37095         return this;
37096     },
37097
37098     /**
37099      * Reconfigures the grid to use a different Store and Column Model.
37100      * The View will be bound to the new objects and refreshed.
37101      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37102      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37103      */
37104     reconfigure : function(dataSource, colModel){
37105         if(this.loadMask){
37106             this.loadMask.destroy();
37107             this.loadMask = new Roo.LoadMask(this.container,
37108                     Roo.apply({store:dataSource}, this.loadMask));
37109         }
37110         this.view.bind(dataSource, colModel);
37111         this.dataSource = dataSource;
37112         this.colModel = colModel;
37113         this.view.refresh(true);
37114     },
37115     /**
37116      * addColumns
37117      * Add's a column, default at the end..
37118      
37119      * @param {int} position to add (default end)
37120      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37121      */
37122     addColumns : function(pos, ar)
37123     {
37124         
37125         for (var i =0;i< ar.length;i++) {
37126             var cfg = ar[i];
37127             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37128             this.cm.lookup[cfg.id] = cfg;
37129         }
37130         
37131         
37132         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37133             pos = this.cm.config.length; //this.cm.config.push(cfg);
37134         } 
37135         pos = Math.max(0,pos);
37136         ar.unshift(0);
37137         ar.unshift(pos);
37138         this.cm.config.splice.apply(this.cm.config, ar);
37139         
37140         
37141         
37142         this.view.generateRules(this.cm);
37143         this.view.refresh(true);
37144         
37145     },
37146     
37147     
37148     
37149     
37150     // private
37151     onKeyDown : function(e){
37152         this.fireEvent("keydown", e);
37153     },
37154
37155     /**
37156      * Destroy this grid.
37157      * @param {Boolean} removeEl True to remove the element
37158      */
37159     destroy : function(removeEl, keepListeners){
37160         if(this.loadMask){
37161             this.loadMask.destroy();
37162         }
37163         var c = this.container;
37164         c.removeAllListeners();
37165         this.view.destroy();
37166         this.colModel.purgeListeners();
37167         if(!keepListeners){
37168             this.purgeListeners();
37169         }
37170         c.update("");
37171         if(removeEl === true){
37172             c.remove();
37173         }
37174     },
37175
37176     // private
37177     processEvent : function(name, e){
37178         // does this fire select???
37179         //Roo.log('grid:processEvent '  + name);
37180         
37181         if (name != 'touchstart' ) {
37182             this.fireEvent(name, e);    
37183         }
37184         
37185         var t = e.getTarget();
37186         var v = this.view;
37187         var header = v.findHeaderIndex(t);
37188         if(header !== false){
37189             var ename = name == 'touchstart' ? 'click' : name;
37190              
37191             this.fireEvent("header" + ename, this, header, e);
37192         }else{
37193             var row = v.findRowIndex(t);
37194             var cell = v.findCellIndex(t);
37195             if (name == 'touchstart') {
37196                 // first touch is always a click.
37197                 // hopefull this happens after selection is updated.?
37198                 name = false;
37199                 
37200                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37201                     var cs = this.selModel.getSelectedCell();
37202                     if (row == cs[0] && cell == cs[1]){
37203                         name = 'dblclick';
37204                     }
37205                 }
37206                 if (typeof(this.selModel.getSelections) != 'undefined') {
37207                     var cs = this.selModel.getSelections();
37208                     var ds = this.dataSource;
37209                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37210                         name = 'dblclick';
37211                     }
37212                 }
37213                 if (!name) {
37214                     return;
37215                 }
37216             }
37217             
37218             
37219             if(row !== false){
37220                 this.fireEvent("row" + name, this, row, e);
37221                 if(cell !== false){
37222                     this.fireEvent("cell" + name, this, row, cell, e);
37223                 }
37224             }
37225         }
37226     },
37227
37228     // private
37229     onClick : function(e){
37230         this.processEvent("click", e);
37231     },
37232    // private
37233     onTouchStart : function(e){
37234         this.processEvent("touchstart", e);
37235     },
37236
37237     // private
37238     onContextMenu : function(e, t){
37239         this.processEvent("contextmenu", e);
37240     },
37241
37242     // private
37243     onDblClick : function(e){
37244         this.processEvent("dblclick", e);
37245     },
37246
37247     // private
37248     walkCells : function(row, col, step, fn, scope){
37249         var cm = this.colModel, clen = cm.getColumnCount();
37250         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37251         if(step < 0){
37252             if(col < 0){
37253                 row--;
37254                 first = false;
37255             }
37256             while(row >= 0){
37257                 if(!first){
37258                     col = clen-1;
37259                 }
37260                 first = false;
37261                 while(col >= 0){
37262                     if(fn.call(scope || this, row, col, cm) === true){
37263                         return [row, col];
37264                     }
37265                     col--;
37266                 }
37267                 row--;
37268             }
37269         } else {
37270             if(col >= clen){
37271                 row++;
37272                 first = false;
37273             }
37274             while(row < rlen){
37275                 if(!first){
37276                     col = 0;
37277                 }
37278                 first = false;
37279                 while(col < clen){
37280                     if(fn.call(scope || this, row, col, cm) === true){
37281                         return [row, col];
37282                     }
37283                     col++;
37284                 }
37285                 row++;
37286             }
37287         }
37288         return null;
37289     },
37290
37291     // private
37292     getSelections : function(){
37293         return this.selModel.getSelections();
37294     },
37295
37296     /**
37297      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37298      * but if manual update is required this method will initiate it.
37299      */
37300     autoSize : function(){
37301         if(this.rendered){
37302             this.view.layout();
37303             if(this.view.adjustForScroll){
37304                 this.view.adjustForScroll();
37305             }
37306         }
37307     },
37308
37309     /**
37310      * Returns the grid's underlying element.
37311      * @return {Element} The element
37312      */
37313     getGridEl : function(){
37314         return this.container;
37315     },
37316
37317     // private for compatibility, overridden by editor grid
37318     stopEditing : function(){},
37319
37320     /**
37321      * Returns the grid's SelectionModel.
37322      * @return {SelectionModel}
37323      */
37324     getSelectionModel : function(){
37325         if(!this.selModel){
37326             this.selModel = new Roo.grid.RowSelectionModel();
37327         }
37328         return this.selModel;
37329     },
37330
37331     /**
37332      * Returns the grid's DataSource.
37333      * @return {DataSource}
37334      */
37335     getDataSource : function(){
37336         return this.dataSource;
37337     },
37338
37339     /**
37340      * Returns the grid's ColumnModel.
37341      * @return {ColumnModel}
37342      */
37343     getColumnModel : function(){
37344         return this.colModel;
37345     },
37346
37347     /**
37348      * Returns the grid's GridView object.
37349      * @return {GridView}
37350      */
37351     getView : function(){
37352         if(!this.view){
37353             this.view = new Roo.grid.GridView(this.viewConfig);
37354             this.relayEvents(this.view, [
37355                 "beforerowremoved", "beforerowsinserted",
37356                 "beforerefresh", "rowremoved",
37357                 "rowsinserted", "rowupdated" ,"refresh"
37358             ]);
37359         }
37360         return this.view;
37361     },
37362     /**
37363      * Called to get grid's drag proxy text, by default returns this.ddText.
37364      * Override this to put something different in the dragged text.
37365      * @return {String}
37366      */
37367     getDragDropText : function(){
37368         var count = this.selModel.getCount();
37369         return String.format(this.ddText, count, count == 1 ? '' : 's');
37370     }
37371 });
37372 /*
37373  * Based on:
37374  * Ext JS Library 1.1.1
37375  * Copyright(c) 2006-2007, Ext JS, LLC.
37376  *
37377  * Originally Released Under LGPL - original licence link has changed is not relivant.
37378  *
37379  * Fork - LGPL
37380  * <script type="text/javascript">
37381  */
37382  /**
37383  * @class Roo.grid.AbstractGridView
37384  * @extends Roo.util.Observable
37385  * @abstract
37386  * Abstract base class for grid Views
37387  * @constructor
37388  */
37389 Roo.grid.AbstractGridView = function(){
37390         this.grid = null;
37391         
37392         this.events = {
37393             "beforerowremoved" : true,
37394             "beforerowsinserted" : true,
37395             "beforerefresh" : true,
37396             "rowremoved" : true,
37397             "rowsinserted" : true,
37398             "rowupdated" : true,
37399             "refresh" : true
37400         };
37401     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37402 };
37403
37404 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37405     rowClass : "x-grid-row",
37406     cellClass : "x-grid-cell",
37407     tdClass : "x-grid-td",
37408     hdClass : "x-grid-hd",
37409     splitClass : "x-grid-hd-split",
37410     
37411     init: function(grid){
37412         this.grid = grid;
37413                 var cid = this.grid.getGridEl().id;
37414         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37415         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37416         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37417         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37418         },
37419         
37420     getColumnRenderers : function(){
37421         var renderers = [];
37422         var cm = this.grid.colModel;
37423         var colCount = cm.getColumnCount();
37424         for(var i = 0; i < colCount; i++){
37425             renderers[i] = cm.getRenderer(i);
37426         }
37427         return renderers;
37428     },
37429     
37430     getColumnIds : function(){
37431         var ids = [];
37432         var cm = this.grid.colModel;
37433         var colCount = cm.getColumnCount();
37434         for(var i = 0; i < colCount; i++){
37435             ids[i] = cm.getColumnId(i);
37436         }
37437         return ids;
37438     },
37439     
37440     getDataIndexes : function(){
37441         if(!this.indexMap){
37442             this.indexMap = this.buildIndexMap();
37443         }
37444         return this.indexMap.colToData;
37445     },
37446     
37447     getColumnIndexByDataIndex : function(dataIndex){
37448         if(!this.indexMap){
37449             this.indexMap = this.buildIndexMap();
37450         }
37451         return this.indexMap.dataToCol[dataIndex];
37452     },
37453     
37454     /**
37455      * Set a css style for a column dynamically. 
37456      * @param {Number} colIndex The index of the column
37457      * @param {String} name The css property name
37458      * @param {String} value The css value
37459      */
37460     setCSSStyle : function(colIndex, name, value){
37461         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37462         Roo.util.CSS.updateRule(selector, name, value);
37463     },
37464     
37465     generateRules : function(cm){
37466         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37467         Roo.util.CSS.removeStyleSheet(rulesId);
37468         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37469             var cid = cm.getColumnId(i);
37470             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37471                          this.tdSelector, cid, " {\n}\n",
37472                          this.hdSelector, cid, " {\n}\n",
37473                          this.splitSelector, cid, " {\n}\n");
37474         }
37475         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37476     }
37477 });/*
37478  * Based on:
37479  * Ext JS Library 1.1.1
37480  * Copyright(c) 2006-2007, Ext JS, LLC.
37481  *
37482  * Originally Released Under LGPL - original licence link has changed is not relivant.
37483  *
37484  * Fork - LGPL
37485  * <script type="text/javascript">
37486  */
37487
37488 // private
37489 // This is a support class used internally by the Grid components
37490 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37491     this.grid = grid;
37492     this.view = grid.getView();
37493     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37494     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37495     if(hd2){
37496         this.setHandleElId(Roo.id(hd));
37497         this.setOuterHandleElId(Roo.id(hd2));
37498     }
37499     this.scroll = false;
37500 };
37501 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37502     maxDragWidth: 120,
37503     getDragData : function(e){
37504         var t = Roo.lib.Event.getTarget(e);
37505         var h = this.view.findHeaderCell(t);
37506         if(h){
37507             return {ddel: h.firstChild, header:h};
37508         }
37509         return false;
37510     },
37511
37512     onInitDrag : function(e){
37513         this.view.headersDisabled = true;
37514         var clone = this.dragData.ddel.cloneNode(true);
37515         clone.id = Roo.id();
37516         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37517         this.proxy.update(clone);
37518         return true;
37519     },
37520
37521     afterValidDrop : function(){
37522         var v = this.view;
37523         setTimeout(function(){
37524             v.headersDisabled = false;
37525         }, 50);
37526     },
37527
37528     afterInvalidDrop : function(){
37529         var v = this.view;
37530         setTimeout(function(){
37531             v.headersDisabled = false;
37532         }, 50);
37533     }
37534 });
37535 /*
37536  * Based on:
37537  * Ext JS Library 1.1.1
37538  * Copyright(c) 2006-2007, Ext JS, LLC.
37539  *
37540  * Originally Released Under LGPL - original licence link has changed is not relivant.
37541  *
37542  * Fork - LGPL
37543  * <script type="text/javascript">
37544  */
37545 // private
37546 // This is a support class used internally by the Grid components
37547 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37548     this.grid = grid;
37549     this.view = grid.getView();
37550     // split the proxies so they don't interfere with mouse events
37551     this.proxyTop = Roo.DomHelper.append(document.body, {
37552         cls:"col-move-top", html:"&#160;"
37553     }, true);
37554     this.proxyBottom = Roo.DomHelper.append(document.body, {
37555         cls:"col-move-bottom", html:"&#160;"
37556     }, true);
37557     this.proxyTop.hide = this.proxyBottom.hide = function(){
37558         this.setLeftTop(-100,-100);
37559         this.setStyle("visibility", "hidden");
37560     };
37561     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37562     // temporarily disabled
37563     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37564     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37565 };
37566 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37567     proxyOffsets : [-4, -9],
37568     fly: Roo.Element.fly,
37569
37570     getTargetFromEvent : function(e){
37571         var t = Roo.lib.Event.getTarget(e);
37572         var cindex = this.view.findCellIndex(t);
37573         if(cindex !== false){
37574             return this.view.getHeaderCell(cindex);
37575         }
37576         return null;
37577     },
37578
37579     nextVisible : function(h){
37580         var v = this.view, cm = this.grid.colModel;
37581         h = h.nextSibling;
37582         while(h){
37583             if(!cm.isHidden(v.getCellIndex(h))){
37584                 return h;
37585             }
37586             h = h.nextSibling;
37587         }
37588         return null;
37589     },
37590
37591     prevVisible : function(h){
37592         var v = this.view, cm = this.grid.colModel;
37593         h = h.prevSibling;
37594         while(h){
37595             if(!cm.isHidden(v.getCellIndex(h))){
37596                 return h;
37597             }
37598             h = h.prevSibling;
37599         }
37600         return null;
37601     },
37602
37603     positionIndicator : function(h, n, e){
37604         var x = Roo.lib.Event.getPageX(e);
37605         var r = Roo.lib.Dom.getRegion(n.firstChild);
37606         var px, pt, py = r.top + this.proxyOffsets[1];
37607         if((r.right - x) <= (r.right-r.left)/2){
37608             px = r.right+this.view.borderWidth;
37609             pt = "after";
37610         }else{
37611             px = r.left;
37612             pt = "before";
37613         }
37614         var oldIndex = this.view.getCellIndex(h);
37615         var newIndex = this.view.getCellIndex(n);
37616
37617         if(this.grid.colModel.isFixed(newIndex)){
37618             return false;
37619         }
37620
37621         var locked = this.grid.colModel.isLocked(newIndex);
37622
37623         if(pt == "after"){
37624             newIndex++;
37625         }
37626         if(oldIndex < newIndex){
37627             newIndex--;
37628         }
37629         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37630             return false;
37631         }
37632         px +=  this.proxyOffsets[0];
37633         this.proxyTop.setLeftTop(px, py);
37634         this.proxyTop.show();
37635         if(!this.bottomOffset){
37636             this.bottomOffset = this.view.mainHd.getHeight();
37637         }
37638         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37639         this.proxyBottom.show();
37640         return pt;
37641     },
37642
37643     onNodeEnter : function(n, dd, e, data){
37644         if(data.header != n){
37645             this.positionIndicator(data.header, n, e);
37646         }
37647     },
37648
37649     onNodeOver : function(n, dd, e, data){
37650         var result = false;
37651         if(data.header != n){
37652             result = this.positionIndicator(data.header, n, e);
37653         }
37654         if(!result){
37655             this.proxyTop.hide();
37656             this.proxyBottom.hide();
37657         }
37658         return result ? this.dropAllowed : this.dropNotAllowed;
37659     },
37660
37661     onNodeOut : function(n, dd, e, data){
37662         this.proxyTop.hide();
37663         this.proxyBottom.hide();
37664     },
37665
37666     onNodeDrop : function(n, dd, e, data){
37667         var h = data.header;
37668         if(h != n){
37669             var cm = this.grid.colModel;
37670             var x = Roo.lib.Event.getPageX(e);
37671             var r = Roo.lib.Dom.getRegion(n.firstChild);
37672             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37673             var oldIndex = this.view.getCellIndex(h);
37674             var newIndex = this.view.getCellIndex(n);
37675             var locked = cm.isLocked(newIndex);
37676             if(pt == "after"){
37677                 newIndex++;
37678             }
37679             if(oldIndex < newIndex){
37680                 newIndex--;
37681             }
37682             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37683                 return false;
37684             }
37685             cm.setLocked(oldIndex, locked, true);
37686             cm.moveColumn(oldIndex, newIndex);
37687             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37688             return true;
37689         }
37690         return false;
37691     }
37692 });
37693 /*
37694  * Based on:
37695  * Ext JS Library 1.1.1
37696  * Copyright(c) 2006-2007, Ext JS, LLC.
37697  *
37698  * Originally Released Under LGPL - original licence link has changed is not relivant.
37699  *
37700  * Fork - LGPL
37701  * <script type="text/javascript">
37702  */
37703   
37704 /**
37705  * @class Roo.grid.GridView
37706  * @extends Roo.util.Observable
37707  *
37708  * @constructor
37709  * @param {Object} config
37710  */
37711 Roo.grid.GridView = function(config){
37712     Roo.grid.GridView.superclass.constructor.call(this);
37713     this.el = null;
37714
37715     Roo.apply(this, config);
37716 };
37717
37718 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37719
37720     unselectable :  'unselectable="on"',
37721     unselectableCls :  'x-unselectable',
37722     
37723     
37724     rowClass : "x-grid-row",
37725
37726     cellClass : "x-grid-col",
37727
37728     tdClass : "x-grid-td",
37729
37730     hdClass : "x-grid-hd",
37731
37732     splitClass : "x-grid-split",
37733
37734     sortClasses : ["sort-asc", "sort-desc"],
37735
37736     enableMoveAnim : false,
37737
37738     hlColor: "C3DAF9",
37739
37740     dh : Roo.DomHelper,
37741
37742     fly : Roo.Element.fly,
37743
37744     css : Roo.util.CSS,
37745
37746     borderWidth: 1,
37747
37748     splitOffset: 3,
37749
37750     scrollIncrement : 22,
37751
37752     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37753
37754     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37755
37756     bind : function(ds, cm){
37757         if(this.ds){
37758             this.ds.un("load", this.onLoad, this);
37759             this.ds.un("datachanged", this.onDataChange, this);
37760             this.ds.un("add", this.onAdd, this);
37761             this.ds.un("remove", this.onRemove, this);
37762             this.ds.un("update", this.onUpdate, this);
37763             this.ds.un("clear", this.onClear, this);
37764         }
37765         if(ds){
37766             ds.on("load", this.onLoad, this);
37767             ds.on("datachanged", this.onDataChange, this);
37768             ds.on("add", this.onAdd, this);
37769             ds.on("remove", this.onRemove, this);
37770             ds.on("update", this.onUpdate, this);
37771             ds.on("clear", this.onClear, this);
37772         }
37773         this.ds = ds;
37774
37775         if(this.cm){
37776             this.cm.un("widthchange", this.onColWidthChange, this);
37777             this.cm.un("headerchange", this.onHeaderChange, this);
37778             this.cm.un("hiddenchange", this.onHiddenChange, this);
37779             this.cm.un("columnmoved", this.onColumnMove, this);
37780             this.cm.un("columnlockchange", this.onColumnLock, this);
37781         }
37782         if(cm){
37783             this.generateRules(cm);
37784             cm.on("widthchange", this.onColWidthChange, this);
37785             cm.on("headerchange", this.onHeaderChange, this);
37786             cm.on("hiddenchange", this.onHiddenChange, this);
37787             cm.on("columnmoved", this.onColumnMove, this);
37788             cm.on("columnlockchange", this.onColumnLock, this);
37789         }
37790         this.cm = cm;
37791     },
37792
37793     init: function(grid){
37794         Roo.grid.GridView.superclass.init.call(this, grid);
37795
37796         this.bind(grid.dataSource, grid.colModel);
37797
37798         grid.on("headerclick", this.handleHeaderClick, this);
37799
37800         if(grid.trackMouseOver){
37801             grid.on("mouseover", this.onRowOver, this);
37802             grid.on("mouseout", this.onRowOut, this);
37803         }
37804         grid.cancelTextSelection = function(){};
37805         this.gridId = grid.id;
37806
37807         var tpls = this.templates || {};
37808
37809         if(!tpls.master){
37810             tpls.master = new Roo.Template(
37811                '<div class="x-grid" hidefocus="true">',
37812                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37813                   '<div class="x-grid-topbar"></div>',
37814                   '<div class="x-grid-scroller"><div></div></div>',
37815                   '<div class="x-grid-locked">',
37816                       '<div class="x-grid-header">{lockedHeader}</div>',
37817                       '<div class="x-grid-body">{lockedBody}</div>',
37818                   "</div>",
37819                   '<div class="x-grid-viewport">',
37820                       '<div class="x-grid-header">{header}</div>',
37821                       '<div class="x-grid-body">{body}</div>',
37822                   "</div>",
37823                   '<div class="x-grid-bottombar"></div>',
37824                  
37825                   '<div class="x-grid-resize-proxy">&#160;</div>',
37826                "</div>"
37827             );
37828             tpls.master.disableformats = true;
37829         }
37830
37831         if(!tpls.header){
37832             tpls.header = new Roo.Template(
37833                '<table border="0" cellspacing="0" cellpadding="0">',
37834                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37835                "</table>{splits}"
37836             );
37837             tpls.header.disableformats = true;
37838         }
37839         tpls.header.compile();
37840
37841         if(!tpls.hcell){
37842             tpls.hcell = new Roo.Template(
37843                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37844                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37845                 "</div></td>"
37846              );
37847              tpls.hcell.disableFormats = true;
37848         }
37849         tpls.hcell.compile();
37850
37851         if(!tpls.hsplit){
37852             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37853                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37854             tpls.hsplit.disableFormats = true;
37855         }
37856         tpls.hsplit.compile();
37857
37858         if(!tpls.body){
37859             tpls.body = new Roo.Template(
37860                '<table border="0" cellspacing="0" cellpadding="0">',
37861                "<tbody>{rows}</tbody>",
37862                "</table>"
37863             );
37864             tpls.body.disableFormats = true;
37865         }
37866         tpls.body.compile();
37867
37868         if(!tpls.row){
37869             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37870             tpls.row.disableFormats = true;
37871         }
37872         tpls.row.compile();
37873
37874         if(!tpls.cell){
37875             tpls.cell = new Roo.Template(
37876                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37877                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37878                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37879                 "</td>"
37880             );
37881             tpls.cell.disableFormats = true;
37882         }
37883         tpls.cell.compile();
37884
37885         this.templates = tpls;
37886     },
37887
37888     // remap these for backwards compat
37889     onColWidthChange : function(){
37890         this.updateColumns.apply(this, arguments);
37891     },
37892     onHeaderChange : function(){
37893         this.updateHeaders.apply(this, arguments);
37894     }, 
37895     onHiddenChange : function(){
37896         this.handleHiddenChange.apply(this, arguments);
37897     },
37898     onColumnMove : function(){
37899         this.handleColumnMove.apply(this, arguments);
37900     },
37901     onColumnLock : function(){
37902         this.handleLockChange.apply(this, arguments);
37903     },
37904
37905     onDataChange : function(){
37906         this.refresh();
37907         this.updateHeaderSortState();
37908     },
37909
37910     onClear : function(){
37911         this.refresh();
37912     },
37913
37914     onUpdate : function(ds, record){
37915         this.refreshRow(record);
37916     },
37917
37918     refreshRow : function(record){
37919         var ds = this.ds, index;
37920         if(typeof record == 'number'){
37921             index = record;
37922             record = ds.getAt(index);
37923         }else{
37924             index = ds.indexOf(record);
37925         }
37926         this.insertRows(ds, index, index, true);
37927         this.onRemove(ds, record, index+1, true);
37928         this.syncRowHeights(index, index);
37929         this.layout();
37930         this.fireEvent("rowupdated", this, index, record);
37931     },
37932
37933     onAdd : function(ds, records, index){
37934         this.insertRows(ds, index, index + (records.length-1));
37935     },
37936
37937     onRemove : function(ds, record, index, isUpdate){
37938         if(isUpdate !== true){
37939             this.fireEvent("beforerowremoved", this, index, record);
37940         }
37941         var bt = this.getBodyTable(), lt = this.getLockedTable();
37942         if(bt.rows[index]){
37943             bt.firstChild.removeChild(bt.rows[index]);
37944         }
37945         if(lt.rows[index]){
37946             lt.firstChild.removeChild(lt.rows[index]);
37947         }
37948         if(isUpdate !== true){
37949             this.stripeRows(index);
37950             this.syncRowHeights(index, index);
37951             this.layout();
37952             this.fireEvent("rowremoved", this, index, record);
37953         }
37954     },
37955
37956     onLoad : function(){
37957         this.scrollToTop();
37958     },
37959
37960     /**
37961      * Scrolls the grid to the top
37962      */
37963     scrollToTop : function(){
37964         if(this.scroller){
37965             this.scroller.dom.scrollTop = 0;
37966             this.syncScroll();
37967         }
37968     },
37969
37970     /**
37971      * Gets a panel in the header of the grid that can be used for toolbars etc.
37972      * After modifying the contents of this panel a call to grid.autoSize() may be
37973      * required to register any changes in size.
37974      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37975      * @return Roo.Element
37976      */
37977     getHeaderPanel : function(doShow){
37978         if(doShow){
37979             this.headerPanel.show();
37980         }
37981         return this.headerPanel;
37982     },
37983
37984     /**
37985      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37986      * After modifying the contents of this panel a call to grid.autoSize() may be
37987      * required to register any changes in size.
37988      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37989      * @return Roo.Element
37990      */
37991     getFooterPanel : function(doShow){
37992         if(doShow){
37993             this.footerPanel.show();
37994         }
37995         return this.footerPanel;
37996     },
37997
37998     initElements : function(){
37999         var E = Roo.Element;
38000         var el = this.grid.getGridEl().dom.firstChild;
38001         var cs = el.childNodes;
38002
38003         this.el = new E(el);
38004         
38005          this.focusEl = new E(el.firstChild);
38006         this.focusEl.swallowEvent("click", true);
38007         
38008         this.headerPanel = new E(cs[1]);
38009         this.headerPanel.enableDisplayMode("block");
38010
38011         this.scroller = new E(cs[2]);
38012         this.scrollSizer = new E(this.scroller.dom.firstChild);
38013
38014         this.lockedWrap = new E(cs[3]);
38015         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38016         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38017
38018         this.mainWrap = new E(cs[4]);
38019         this.mainHd = new E(this.mainWrap.dom.firstChild);
38020         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38021
38022         this.footerPanel = new E(cs[5]);
38023         this.footerPanel.enableDisplayMode("block");
38024
38025         this.resizeProxy = new E(cs[6]);
38026
38027         this.headerSelector = String.format(
38028            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38029            this.lockedHd.id, this.mainHd.id
38030         );
38031
38032         this.splitterSelector = String.format(
38033            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38034            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38035         );
38036     },
38037     idToCssName : function(s)
38038     {
38039         return s.replace(/[^a-z0-9]+/ig, '-');
38040     },
38041
38042     getHeaderCell : function(index){
38043         return Roo.DomQuery.select(this.headerSelector)[index];
38044     },
38045
38046     getHeaderCellMeasure : function(index){
38047         return this.getHeaderCell(index).firstChild;
38048     },
38049
38050     getHeaderCellText : function(index){
38051         return this.getHeaderCell(index).firstChild.firstChild;
38052     },
38053
38054     getLockedTable : function(){
38055         return this.lockedBody.dom.firstChild;
38056     },
38057
38058     getBodyTable : function(){
38059         return this.mainBody.dom.firstChild;
38060     },
38061
38062     getLockedRow : function(index){
38063         return this.getLockedTable().rows[index];
38064     },
38065
38066     getRow : function(index){
38067         return this.getBodyTable().rows[index];
38068     },
38069
38070     getRowComposite : function(index){
38071         if(!this.rowEl){
38072             this.rowEl = new Roo.CompositeElementLite();
38073         }
38074         var els = [], lrow, mrow;
38075         if(lrow = this.getLockedRow(index)){
38076             els.push(lrow);
38077         }
38078         if(mrow = this.getRow(index)){
38079             els.push(mrow);
38080         }
38081         this.rowEl.elements = els;
38082         return this.rowEl;
38083     },
38084     /**
38085      * Gets the 'td' of the cell
38086      * 
38087      * @param {Integer} rowIndex row to select
38088      * @param {Integer} colIndex column to select
38089      * 
38090      * @return {Object} 
38091      */
38092     getCell : function(rowIndex, colIndex){
38093         var locked = this.cm.getLockedCount();
38094         var source;
38095         if(colIndex < locked){
38096             source = this.lockedBody.dom.firstChild;
38097         }else{
38098             source = this.mainBody.dom.firstChild;
38099             colIndex -= locked;
38100         }
38101         return source.rows[rowIndex].childNodes[colIndex];
38102     },
38103
38104     getCellText : function(rowIndex, colIndex){
38105         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38106     },
38107
38108     getCellBox : function(cell){
38109         var b = this.fly(cell).getBox();
38110         if(Roo.isOpera){ // opera fails to report the Y
38111             b.y = cell.offsetTop + this.mainBody.getY();
38112         }
38113         return b;
38114     },
38115
38116     getCellIndex : function(cell){
38117         var id = String(cell.className).match(this.cellRE);
38118         if(id){
38119             return parseInt(id[1], 10);
38120         }
38121         return 0;
38122     },
38123
38124     findHeaderIndex : function(n){
38125         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38126         return r ? this.getCellIndex(r) : false;
38127     },
38128
38129     findHeaderCell : function(n){
38130         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38131         return r ? r : false;
38132     },
38133
38134     findRowIndex : function(n){
38135         if(!n){
38136             return false;
38137         }
38138         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38139         return r ? r.rowIndex : false;
38140     },
38141
38142     findCellIndex : function(node){
38143         var stop = this.el.dom;
38144         while(node && node != stop){
38145             if(this.findRE.test(node.className)){
38146                 return this.getCellIndex(node);
38147             }
38148             node = node.parentNode;
38149         }
38150         return false;
38151     },
38152
38153     getColumnId : function(index){
38154         return this.cm.getColumnId(index);
38155     },
38156
38157     getSplitters : function()
38158     {
38159         if(this.splitterSelector){
38160            return Roo.DomQuery.select(this.splitterSelector);
38161         }else{
38162             return null;
38163       }
38164     },
38165
38166     getSplitter : function(index){
38167         return this.getSplitters()[index];
38168     },
38169
38170     onRowOver : function(e, t){
38171         var row;
38172         if((row = this.findRowIndex(t)) !== false){
38173             this.getRowComposite(row).addClass("x-grid-row-over");
38174         }
38175     },
38176
38177     onRowOut : function(e, t){
38178         var row;
38179         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38180             this.getRowComposite(row).removeClass("x-grid-row-over");
38181         }
38182     },
38183
38184     renderHeaders : function(){
38185         var cm = this.cm;
38186         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38187         var cb = [], lb = [], sb = [], lsb = [], p = {};
38188         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38189             p.cellId = "x-grid-hd-0-" + i;
38190             p.splitId = "x-grid-csplit-0-" + i;
38191             p.id = cm.getColumnId(i);
38192             p.value = cm.getColumnHeader(i) || "";
38193             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38194             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38195             if(!cm.isLocked(i)){
38196                 cb[cb.length] = ct.apply(p);
38197                 sb[sb.length] = st.apply(p);
38198             }else{
38199                 lb[lb.length] = ct.apply(p);
38200                 lsb[lsb.length] = st.apply(p);
38201             }
38202         }
38203         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38204                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38205     },
38206
38207     updateHeaders : function(){
38208         var html = this.renderHeaders();
38209         this.lockedHd.update(html[0]);
38210         this.mainHd.update(html[1]);
38211     },
38212
38213     /**
38214      * Focuses the specified row.
38215      * @param {Number} row The row index
38216      */
38217     focusRow : function(row)
38218     {
38219         //Roo.log('GridView.focusRow');
38220         var x = this.scroller.dom.scrollLeft;
38221         this.focusCell(row, 0, false);
38222         this.scroller.dom.scrollLeft = x;
38223     },
38224
38225     /**
38226      * Focuses the specified cell.
38227      * @param {Number} row The row index
38228      * @param {Number} col The column index
38229      * @param {Boolean} hscroll false to disable horizontal scrolling
38230      */
38231     focusCell : function(row, col, hscroll)
38232     {
38233         //Roo.log('GridView.focusCell');
38234         var el = this.ensureVisible(row, col, hscroll);
38235         this.focusEl.alignTo(el, "tl-tl");
38236         if(Roo.isGecko){
38237             this.focusEl.focus();
38238         }else{
38239             this.focusEl.focus.defer(1, this.focusEl);
38240         }
38241     },
38242
38243     /**
38244      * Scrolls the specified cell into view
38245      * @param {Number} row The row index
38246      * @param {Number} col The column index
38247      * @param {Boolean} hscroll false to disable horizontal scrolling
38248      */
38249     ensureVisible : function(row, col, hscroll)
38250     {
38251         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38252         //return null; //disable for testing.
38253         if(typeof row != "number"){
38254             row = row.rowIndex;
38255         }
38256         if(row < 0 && row >= this.ds.getCount()){
38257             return  null;
38258         }
38259         col = (col !== undefined ? col : 0);
38260         var cm = this.grid.colModel;
38261         while(cm.isHidden(col)){
38262             col++;
38263         }
38264
38265         var el = this.getCell(row, col);
38266         if(!el){
38267             return null;
38268         }
38269         var c = this.scroller.dom;
38270
38271         var ctop = parseInt(el.offsetTop, 10);
38272         var cleft = parseInt(el.offsetLeft, 10);
38273         var cbot = ctop + el.offsetHeight;
38274         var cright = cleft + el.offsetWidth;
38275         
38276         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38277         var stop = parseInt(c.scrollTop, 10);
38278         var sleft = parseInt(c.scrollLeft, 10);
38279         var sbot = stop + ch;
38280         var sright = sleft + c.clientWidth;
38281         /*
38282         Roo.log('GridView.ensureVisible:' +
38283                 ' ctop:' + ctop +
38284                 ' c.clientHeight:' + c.clientHeight +
38285                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38286                 ' stop:' + stop +
38287                 ' cbot:' + cbot +
38288                 ' sbot:' + sbot +
38289                 ' ch:' + ch  
38290                 );
38291         */
38292         if(ctop < stop){
38293             c.scrollTop = ctop;
38294             //Roo.log("set scrolltop to ctop DISABLE?");
38295         }else if(cbot > sbot){
38296             //Roo.log("set scrolltop to cbot-ch");
38297             c.scrollTop = cbot-ch;
38298         }
38299         
38300         if(hscroll !== false){
38301             if(cleft < sleft){
38302                 c.scrollLeft = cleft;
38303             }else if(cright > sright){
38304                 c.scrollLeft = cright-c.clientWidth;
38305             }
38306         }
38307          
38308         return el;
38309     },
38310
38311     updateColumns : function(){
38312         this.grid.stopEditing();
38313         var cm = this.grid.colModel, colIds = this.getColumnIds();
38314         //var totalWidth = cm.getTotalWidth();
38315         var pos = 0;
38316         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38317             //if(cm.isHidden(i)) continue;
38318             var w = cm.getColumnWidth(i);
38319             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38320             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38321         }
38322         this.updateSplitters();
38323     },
38324
38325     generateRules : function(cm){
38326         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38327         Roo.util.CSS.removeStyleSheet(rulesId);
38328         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38329             var cid = cm.getColumnId(i);
38330             var align = '';
38331             if(cm.config[i].align){
38332                 align = 'text-align:'+cm.config[i].align+';';
38333             }
38334             var hidden = '';
38335             if(cm.isHidden(i)){
38336                 hidden = 'display:none;';
38337             }
38338             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38339             ruleBuf.push(
38340                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38341                     this.hdSelector, cid, " {\n", align, width, "}\n",
38342                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38343                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38344         }
38345         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38346     },
38347
38348     updateSplitters : function(){
38349         var cm = this.cm, s = this.getSplitters();
38350         if(s){ // splitters not created yet
38351             var pos = 0, locked = true;
38352             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38353                 if(cm.isHidden(i)) {
38354                     continue;
38355                 }
38356                 var w = cm.getColumnWidth(i); // make sure it's a number
38357                 if(!cm.isLocked(i) && locked){
38358                     pos = 0;
38359                     locked = false;
38360                 }
38361                 pos += w;
38362                 s[i].style.left = (pos-this.splitOffset) + "px";
38363             }
38364         }
38365     },
38366
38367     handleHiddenChange : function(colModel, colIndex, hidden){
38368         if(hidden){
38369             this.hideColumn(colIndex);
38370         }else{
38371             this.unhideColumn(colIndex);
38372         }
38373     },
38374
38375     hideColumn : function(colIndex){
38376         var cid = this.getColumnId(colIndex);
38377         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38378         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38379         if(Roo.isSafari){
38380             this.updateHeaders();
38381         }
38382         this.updateSplitters();
38383         this.layout();
38384     },
38385
38386     unhideColumn : function(colIndex){
38387         var cid = this.getColumnId(colIndex);
38388         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38389         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38390
38391         if(Roo.isSafari){
38392             this.updateHeaders();
38393         }
38394         this.updateSplitters();
38395         this.layout();
38396     },
38397
38398     insertRows : function(dm, firstRow, lastRow, isUpdate){
38399         if(firstRow == 0 && lastRow == dm.getCount()-1){
38400             this.refresh();
38401         }else{
38402             if(!isUpdate){
38403                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38404             }
38405             var s = this.getScrollState();
38406             var markup = this.renderRows(firstRow, lastRow);
38407             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38408             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38409             this.restoreScroll(s);
38410             if(!isUpdate){
38411                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38412                 this.syncRowHeights(firstRow, lastRow);
38413                 this.stripeRows(firstRow);
38414                 this.layout();
38415             }
38416         }
38417     },
38418
38419     bufferRows : function(markup, target, index){
38420         var before = null, trows = target.rows, tbody = target.tBodies[0];
38421         if(index < trows.length){
38422             before = trows[index];
38423         }
38424         var b = document.createElement("div");
38425         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38426         var rows = b.firstChild.rows;
38427         for(var i = 0, len = rows.length; i < len; i++){
38428             if(before){
38429                 tbody.insertBefore(rows[0], before);
38430             }else{
38431                 tbody.appendChild(rows[0]);
38432             }
38433         }
38434         b.innerHTML = "";
38435         b = null;
38436     },
38437
38438     deleteRows : function(dm, firstRow, lastRow){
38439         if(dm.getRowCount()<1){
38440             this.fireEvent("beforerefresh", this);
38441             this.mainBody.update("");
38442             this.lockedBody.update("");
38443             this.fireEvent("refresh", this);
38444         }else{
38445             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38446             var bt = this.getBodyTable();
38447             var tbody = bt.firstChild;
38448             var rows = bt.rows;
38449             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38450                 tbody.removeChild(rows[firstRow]);
38451             }
38452             this.stripeRows(firstRow);
38453             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38454         }
38455     },
38456
38457     updateRows : function(dataSource, firstRow, lastRow){
38458         var s = this.getScrollState();
38459         this.refresh();
38460         this.restoreScroll(s);
38461     },
38462
38463     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38464         if(!noRefresh){
38465            this.refresh();
38466         }
38467         this.updateHeaderSortState();
38468     },
38469
38470     getScrollState : function(){
38471         
38472         var sb = this.scroller.dom;
38473         return {left: sb.scrollLeft, top: sb.scrollTop};
38474     },
38475
38476     stripeRows : function(startRow){
38477         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38478             return;
38479         }
38480         startRow = startRow || 0;
38481         var rows = this.getBodyTable().rows;
38482         var lrows = this.getLockedTable().rows;
38483         var cls = ' x-grid-row-alt ';
38484         for(var i = startRow, len = rows.length; i < len; i++){
38485             var row = rows[i], lrow = lrows[i];
38486             var isAlt = ((i+1) % 2 == 0);
38487             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38488             if(isAlt == hasAlt){
38489                 continue;
38490             }
38491             if(isAlt){
38492                 row.className += " x-grid-row-alt";
38493             }else{
38494                 row.className = row.className.replace("x-grid-row-alt", "");
38495             }
38496             if(lrow){
38497                 lrow.className = row.className;
38498             }
38499         }
38500     },
38501
38502     restoreScroll : function(state){
38503         //Roo.log('GridView.restoreScroll');
38504         var sb = this.scroller.dom;
38505         sb.scrollLeft = state.left;
38506         sb.scrollTop = state.top;
38507         this.syncScroll();
38508     },
38509
38510     syncScroll : function(){
38511         //Roo.log('GridView.syncScroll');
38512         var sb = this.scroller.dom;
38513         var sh = this.mainHd.dom;
38514         var bs = this.mainBody.dom;
38515         var lv = this.lockedBody.dom;
38516         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38517         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38518     },
38519
38520     handleScroll : function(e){
38521         this.syncScroll();
38522         var sb = this.scroller.dom;
38523         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38524         e.stopEvent();
38525     },
38526
38527     handleWheel : function(e){
38528         var d = e.getWheelDelta();
38529         this.scroller.dom.scrollTop -= d*22;
38530         // set this here to prevent jumpy scrolling on large tables
38531         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38532         e.stopEvent();
38533     },
38534
38535     renderRows : function(startRow, endRow){
38536         // pull in all the crap needed to render rows
38537         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38538         var colCount = cm.getColumnCount();
38539
38540         if(ds.getCount() < 1){
38541             return ["", ""];
38542         }
38543
38544         // build a map for all the columns
38545         var cs = [];
38546         for(var i = 0; i < colCount; i++){
38547             var name = cm.getDataIndex(i);
38548             cs[i] = {
38549                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38550                 renderer : cm.getRenderer(i),
38551                 id : cm.getColumnId(i),
38552                 locked : cm.isLocked(i),
38553                 has_editor : cm.isCellEditable(i)
38554             };
38555         }
38556
38557         startRow = startRow || 0;
38558         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38559
38560         // records to render
38561         var rs = ds.getRange(startRow, endRow);
38562
38563         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38564     },
38565
38566     // As much as I hate to duplicate code, this was branched because FireFox really hates
38567     // [].join("") on strings. The performance difference was substantial enough to
38568     // branch this function
38569     doRender : Roo.isGecko ?
38570             function(cs, rs, ds, startRow, colCount, stripe){
38571                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38572                 // buffers
38573                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38574                 
38575                 var hasListener = this.grid.hasListener('rowclass');
38576                 var rowcfg = {};
38577                 for(var j = 0, len = rs.length; j < len; j++){
38578                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38579                     for(var i = 0; i < colCount; i++){
38580                         c = cs[i];
38581                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38582                         p.id = c.id;
38583                         p.css = p.attr = "";
38584                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38585                         if(p.value == undefined || p.value === "") {
38586                             p.value = "&#160;";
38587                         }
38588                         if(c.has_editor){
38589                             p.css += ' x-grid-editable-cell';
38590                         }
38591                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38592                             p.css +=  ' x-grid-dirty-cell';
38593                         }
38594                         var markup = ct.apply(p);
38595                         if(!c.locked){
38596                             cb+= markup;
38597                         }else{
38598                             lcb+= markup;
38599                         }
38600                     }
38601                     var alt = [];
38602                     if(stripe && ((rowIndex+1) % 2 == 0)){
38603                         alt.push("x-grid-row-alt")
38604                     }
38605                     if(r.dirty){
38606                         alt.push(  " x-grid-dirty-row");
38607                     }
38608                     rp.cells = lcb;
38609                     if(this.getRowClass){
38610                         alt.push(this.getRowClass(r, rowIndex));
38611                     }
38612                     if (hasListener) {
38613                         rowcfg = {
38614                              
38615                             record: r,
38616                             rowIndex : rowIndex,
38617                             rowClass : ''
38618                         };
38619                         this.grid.fireEvent('rowclass', this, rowcfg);
38620                         alt.push(rowcfg.rowClass);
38621                     }
38622                     rp.alt = alt.join(" ");
38623                     lbuf+= rt.apply(rp);
38624                     rp.cells = cb;
38625                     buf+=  rt.apply(rp);
38626                 }
38627                 return [lbuf, buf];
38628             } :
38629             function(cs, rs, ds, startRow, colCount, stripe){
38630                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38631                 // buffers
38632                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38633                 var hasListener = this.grid.hasListener('rowclass');
38634  
38635                 var rowcfg = {};
38636                 for(var j = 0, len = rs.length; j < len; j++){
38637                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38638                     for(var i = 0; i < colCount; i++){
38639                         c = cs[i];
38640                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38641                         p.id = c.id;
38642                         p.css = p.attr = "";
38643                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38644                         if(p.value == undefined || p.value === "") {
38645                             p.value = "&#160;";
38646                         }
38647                         //Roo.log(c);
38648                          if(c.has_editor){
38649                             p.css += ' x-grid-editable-cell';
38650                         }
38651                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38652                             p.css += ' x-grid-dirty-cell' 
38653                         }
38654                         
38655                         var markup = ct.apply(p);
38656                         if(!c.locked){
38657                             cb[cb.length] = markup;
38658                         }else{
38659                             lcb[lcb.length] = markup;
38660                         }
38661                     }
38662                     var alt = [];
38663                     if(stripe && ((rowIndex+1) % 2 == 0)){
38664                         alt.push( "x-grid-row-alt");
38665                     }
38666                     if(r.dirty){
38667                         alt.push(" x-grid-dirty-row");
38668                     }
38669                     rp.cells = lcb;
38670                     if(this.getRowClass){
38671                         alt.push( this.getRowClass(r, rowIndex));
38672                     }
38673                     if (hasListener) {
38674                         rowcfg = {
38675                              
38676                             record: r,
38677                             rowIndex : rowIndex,
38678                             rowClass : ''
38679                         };
38680                         this.grid.fireEvent('rowclass', this, rowcfg);
38681                         alt.push(rowcfg.rowClass);
38682                     }
38683                     
38684                     rp.alt = alt.join(" ");
38685                     rp.cells = lcb.join("");
38686                     lbuf[lbuf.length] = rt.apply(rp);
38687                     rp.cells = cb.join("");
38688                     buf[buf.length] =  rt.apply(rp);
38689                 }
38690                 return [lbuf.join(""), buf.join("")];
38691             },
38692
38693     renderBody : function(){
38694         var markup = this.renderRows();
38695         var bt = this.templates.body;
38696         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38697     },
38698
38699     /**
38700      * Refreshes the grid
38701      * @param {Boolean} headersToo
38702      */
38703     refresh : function(headersToo){
38704         this.fireEvent("beforerefresh", this);
38705         this.grid.stopEditing();
38706         var result = this.renderBody();
38707         this.lockedBody.update(result[0]);
38708         this.mainBody.update(result[1]);
38709         if(headersToo === true){
38710             this.updateHeaders();
38711             this.updateColumns();
38712             this.updateSplitters();
38713             this.updateHeaderSortState();
38714         }
38715         this.syncRowHeights();
38716         this.layout();
38717         this.fireEvent("refresh", this);
38718     },
38719
38720     handleColumnMove : function(cm, oldIndex, newIndex){
38721         this.indexMap = null;
38722         var s = this.getScrollState();
38723         this.refresh(true);
38724         this.restoreScroll(s);
38725         this.afterMove(newIndex);
38726     },
38727
38728     afterMove : function(colIndex){
38729         if(this.enableMoveAnim && Roo.enableFx){
38730             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38731         }
38732         // if multisort - fix sortOrder, and reload..
38733         if (this.grid.dataSource.multiSort) {
38734             // the we can call sort again..
38735             var dm = this.grid.dataSource;
38736             var cm = this.grid.colModel;
38737             var so = [];
38738             for(var i = 0; i < cm.config.length; i++ ) {
38739                 
38740                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38741                     continue; // dont' bother, it's not in sort list or being set.
38742                 }
38743                 
38744                 so.push(cm.config[i].dataIndex);
38745             };
38746             dm.sortOrder = so;
38747             dm.load(dm.lastOptions);
38748             
38749             
38750         }
38751         
38752     },
38753
38754     updateCell : function(dm, rowIndex, dataIndex){
38755         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38756         if(typeof colIndex == "undefined"){ // not present in grid
38757             return;
38758         }
38759         var cm = this.grid.colModel;
38760         var cell = this.getCell(rowIndex, colIndex);
38761         var cellText = this.getCellText(rowIndex, colIndex);
38762
38763         var p = {
38764             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38765             id : cm.getColumnId(colIndex),
38766             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38767         };
38768         var renderer = cm.getRenderer(colIndex);
38769         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38770         if(typeof val == "undefined" || val === "") {
38771             val = "&#160;";
38772         }
38773         cellText.innerHTML = val;
38774         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38775         this.syncRowHeights(rowIndex, rowIndex);
38776     },
38777
38778     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38779         var maxWidth = 0;
38780         if(this.grid.autoSizeHeaders){
38781             var h = this.getHeaderCellMeasure(colIndex);
38782             maxWidth = Math.max(maxWidth, h.scrollWidth);
38783         }
38784         var tb, index;
38785         if(this.cm.isLocked(colIndex)){
38786             tb = this.getLockedTable();
38787             index = colIndex;
38788         }else{
38789             tb = this.getBodyTable();
38790             index = colIndex - this.cm.getLockedCount();
38791         }
38792         if(tb && tb.rows){
38793             var rows = tb.rows;
38794             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38795             for(var i = 0; i < stopIndex; i++){
38796                 var cell = rows[i].childNodes[index].firstChild;
38797                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38798             }
38799         }
38800         return maxWidth + /*margin for error in IE*/ 5;
38801     },
38802     /**
38803      * Autofit a column to its content.
38804      * @param {Number} colIndex
38805      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38806      */
38807      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38808          if(this.cm.isHidden(colIndex)){
38809              return; // can't calc a hidden column
38810          }
38811         if(forceMinSize){
38812             var cid = this.cm.getColumnId(colIndex);
38813             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38814            if(this.grid.autoSizeHeaders){
38815                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38816            }
38817         }
38818         var newWidth = this.calcColumnWidth(colIndex);
38819         this.cm.setColumnWidth(colIndex,
38820             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38821         if(!suppressEvent){
38822             this.grid.fireEvent("columnresize", colIndex, newWidth);
38823         }
38824     },
38825
38826     /**
38827      * Autofits all columns to their content and then expands to fit any extra space in the grid
38828      */
38829      autoSizeColumns : function(){
38830         var cm = this.grid.colModel;
38831         var colCount = cm.getColumnCount();
38832         for(var i = 0; i < colCount; i++){
38833             this.autoSizeColumn(i, true, true);
38834         }
38835         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38836             this.fitColumns();
38837         }else{
38838             this.updateColumns();
38839             this.layout();
38840         }
38841     },
38842
38843     /**
38844      * Autofits all columns to the grid's width proportionate with their current size
38845      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38846      */
38847     fitColumns : function(reserveScrollSpace){
38848         var cm = this.grid.colModel;
38849         var colCount = cm.getColumnCount();
38850         var cols = [];
38851         var width = 0;
38852         var i, w;
38853         for (i = 0; i < colCount; i++){
38854             if(!cm.isHidden(i) && !cm.isFixed(i)){
38855                 w = cm.getColumnWidth(i);
38856                 cols.push(i);
38857                 cols.push(w);
38858                 width += w;
38859             }
38860         }
38861         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38862         if(reserveScrollSpace){
38863             avail -= 17;
38864         }
38865         var frac = (avail - cm.getTotalWidth())/width;
38866         while (cols.length){
38867             w = cols.pop();
38868             i = cols.pop();
38869             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38870         }
38871         this.updateColumns();
38872         this.layout();
38873     },
38874
38875     onRowSelect : function(rowIndex){
38876         var row = this.getRowComposite(rowIndex);
38877         row.addClass("x-grid-row-selected");
38878     },
38879
38880     onRowDeselect : function(rowIndex){
38881         var row = this.getRowComposite(rowIndex);
38882         row.removeClass("x-grid-row-selected");
38883     },
38884
38885     onCellSelect : function(row, col){
38886         var cell = this.getCell(row, col);
38887         if(cell){
38888             Roo.fly(cell).addClass("x-grid-cell-selected");
38889         }
38890     },
38891
38892     onCellDeselect : function(row, col){
38893         var cell = this.getCell(row, col);
38894         if(cell){
38895             Roo.fly(cell).removeClass("x-grid-cell-selected");
38896         }
38897     },
38898
38899     updateHeaderSortState : function(){
38900         
38901         // sort state can be single { field: xxx, direction : yyy}
38902         // or   { xxx=>ASC , yyy : DESC ..... }
38903         
38904         var mstate = {};
38905         if (!this.ds.multiSort) { 
38906             var state = this.ds.getSortState();
38907             if(!state){
38908                 return;
38909             }
38910             mstate[state.field] = state.direction;
38911             // FIXME... - this is not used here.. but might be elsewhere..
38912             this.sortState = state;
38913             
38914         } else {
38915             mstate = this.ds.sortToggle;
38916         }
38917         //remove existing sort classes..
38918         
38919         var sc = this.sortClasses;
38920         var hds = this.el.select(this.headerSelector).removeClass(sc);
38921         
38922         for(var f in mstate) {
38923         
38924             var sortColumn = this.cm.findColumnIndex(f);
38925             
38926             if(sortColumn != -1){
38927                 var sortDir = mstate[f];        
38928                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38929             }
38930         }
38931         
38932          
38933         
38934     },
38935
38936
38937     handleHeaderClick : function(g, index,e){
38938         
38939         Roo.log("header click");
38940         
38941         if (Roo.isTouch) {
38942             // touch events on header are handled by context
38943             this.handleHdCtx(g,index,e);
38944             return;
38945         }
38946         
38947         
38948         if(this.headersDisabled){
38949             return;
38950         }
38951         var dm = g.dataSource, cm = g.colModel;
38952         if(!cm.isSortable(index)){
38953             return;
38954         }
38955         g.stopEditing();
38956         
38957         if (dm.multiSort) {
38958             // update the sortOrder
38959             var so = [];
38960             for(var i = 0; i < cm.config.length; i++ ) {
38961                 
38962                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38963                     continue; // dont' bother, it's not in sort list or being set.
38964                 }
38965                 
38966                 so.push(cm.config[i].dataIndex);
38967             };
38968             dm.sortOrder = so;
38969         }
38970         
38971         
38972         dm.sort(cm.getDataIndex(index));
38973     },
38974
38975
38976     destroy : function(){
38977         if(this.colMenu){
38978             this.colMenu.removeAll();
38979             Roo.menu.MenuMgr.unregister(this.colMenu);
38980             this.colMenu.getEl().remove();
38981             delete this.colMenu;
38982         }
38983         if(this.hmenu){
38984             this.hmenu.removeAll();
38985             Roo.menu.MenuMgr.unregister(this.hmenu);
38986             this.hmenu.getEl().remove();
38987             delete this.hmenu;
38988         }
38989         if(this.grid.enableColumnMove){
38990             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38991             if(dds){
38992                 for(var dd in dds){
38993                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38994                         var elid = dds[dd].dragElId;
38995                         dds[dd].unreg();
38996                         Roo.get(elid).remove();
38997                     } else if(dds[dd].config.isTarget){
38998                         dds[dd].proxyTop.remove();
38999                         dds[dd].proxyBottom.remove();
39000                         dds[dd].unreg();
39001                     }
39002                     if(Roo.dd.DDM.locationCache[dd]){
39003                         delete Roo.dd.DDM.locationCache[dd];
39004                     }
39005                 }
39006                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39007             }
39008         }
39009         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39010         this.bind(null, null);
39011         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39012     },
39013
39014     handleLockChange : function(){
39015         this.refresh(true);
39016     },
39017
39018     onDenyColumnLock : function(){
39019
39020     },
39021
39022     onDenyColumnHide : function(){
39023
39024     },
39025
39026     handleHdMenuClick : function(item){
39027         var index = this.hdCtxIndex;
39028         var cm = this.cm, ds = this.ds;
39029         switch(item.id){
39030             case "asc":
39031                 ds.sort(cm.getDataIndex(index), "ASC");
39032                 break;
39033             case "desc":
39034                 ds.sort(cm.getDataIndex(index), "DESC");
39035                 break;
39036             case "lock":
39037                 var lc = cm.getLockedCount();
39038                 if(cm.getColumnCount(true) <= lc+1){
39039                     this.onDenyColumnLock();
39040                     return;
39041                 }
39042                 if(lc != index){
39043                     cm.setLocked(index, true, true);
39044                     cm.moveColumn(index, lc);
39045                     this.grid.fireEvent("columnmove", index, lc);
39046                 }else{
39047                     cm.setLocked(index, true);
39048                 }
39049             break;
39050             case "unlock":
39051                 var lc = cm.getLockedCount();
39052                 if((lc-1) != index){
39053                     cm.setLocked(index, false, true);
39054                     cm.moveColumn(index, lc-1);
39055                     this.grid.fireEvent("columnmove", index, lc-1);
39056                 }else{
39057                     cm.setLocked(index, false);
39058                 }
39059             break;
39060             case 'wider': // used to expand cols on touch..
39061             case 'narrow':
39062                 var cw = cm.getColumnWidth(index);
39063                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39064                 cw = Math.max(0, cw);
39065                 cw = Math.min(cw,4000);
39066                 cm.setColumnWidth(index, cw);
39067                 break;
39068                 
39069             default:
39070                 index = cm.getIndexById(item.id.substr(4));
39071                 if(index != -1){
39072                     if(item.checked && cm.getColumnCount(true) <= 1){
39073                         this.onDenyColumnHide();
39074                         return false;
39075                     }
39076                     cm.setHidden(index, item.checked);
39077                 }
39078         }
39079         return true;
39080     },
39081
39082     beforeColMenuShow : function(){
39083         var cm = this.cm,  colCount = cm.getColumnCount();
39084         this.colMenu.removeAll();
39085         
39086         var items = [];
39087         for(var i = 0; i < colCount; i++){
39088             items.push({
39089                 id: "col-"+cm.getColumnId(i),
39090                 text: cm.getColumnHeader(i),
39091                 checked: !cm.isHidden(i),
39092                 hideOnClick:false
39093             });
39094         }
39095         
39096         if (this.grid.sortColMenu) {
39097             items.sort(function(a,b) {
39098                 if (a.text == b.text) {
39099                     return 0;
39100                 }
39101                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39102             });
39103         }
39104         
39105         for(var i = 0; i < colCount; i++){
39106             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39107         }
39108     },
39109
39110     handleHdCtx : function(g, index, e){
39111         e.stopEvent();
39112         var hd = this.getHeaderCell(index);
39113         this.hdCtxIndex = index;
39114         var ms = this.hmenu.items, cm = this.cm;
39115         ms.get("asc").setDisabled(!cm.isSortable(index));
39116         ms.get("desc").setDisabled(!cm.isSortable(index));
39117         if(this.grid.enableColLock !== false){
39118             ms.get("lock").setDisabled(cm.isLocked(index));
39119             ms.get("unlock").setDisabled(!cm.isLocked(index));
39120         }
39121         this.hmenu.show(hd, "tl-bl");
39122     },
39123
39124     handleHdOver : function(e){
39125         var hd = this.findHeaderCell(e.getTarget());
39126         if(hd && !this.headersDisabled){
39127             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39128                this.fly(hd).addClass("x-grid-hd-over");
39129             }
39130         }
39131     },
39132
39133     handleHdOut : function(e){
39134         var hd = this.findHeaderCell(e.getTarget());
39135         if(hd){
39136             this.fly(hd).removeClass("x-grid-hd-over");
39137         }
39138     },
39139
39140     handleSplitDblClick : function(e, t){
39141         var i = this.getCellIndex(t);
39142         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39143             this.autoSizeColumn(i, true);
39144             this.layout();
39145         }
39146     },
39147
39148     render : function(){
39149
39150         var cm = this.cm;
39151         var colCount = cm.getColumnCount();
39152
39153         if(this.grid.monitorWindowResize === true){
39154             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39155         }
39156         var header = this.renderHeaders();
39157         var body = this.templates.body.apply({rows:""});
39158         var html = this.templates.master.apply({
39159             lockedBody: body,
39160             body: body,
39161             lockedHeader: header[0],
39162             header: header[1]
39163         });
39164
39165         //this.updateColumns();
39166
39167         this.grid.getGridEl().dom.innerHTML = html;
39168
39169         this.initElements();
39170         
39171         // a kludge to fix the random scolling effect in webkit
39172         this.el.on("scroll", function() {
39173             this.el.dom.scrollTop=0; // hopefully not recursive..
39174         },this);
39175
39176         this.scroller.on("scroll", this.handleScroll, this);
39177         this.lockedBody.on("mousewheel", this.handleWheel, this);
39178         this.mainBody.on("mousewheel", this.handleWheel, this);
39179
39180         this.mainHd.on("mouseover", this.handleHdOver, this);
39181         this.mainHd.on("mouseout", this.handleHdOut, this);
39182         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39183                 {delegate: "."+this.splitClass});
39184
39185         this.lockedHd.on("mouseover", this.handleHdOver, this);
39186         this.lockedHd.on("mouseout", this.handleHdOut, this);
39187         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39188                 {delegate: "."+this.splitClass});
39189
39190         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39191             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39192         }
39193
39194         this.updateSplitters();
39195
39196         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39197             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39198             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39199         }
39200
39201         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39202             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39203             this.hmenu.add(
39204                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39205                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39206             );
39207             if(this.grid.enableColLock !== false){
39208                 this.hmenu.add('-',
39209                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39210                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39211                 );
39212             }
39213             if (Roo.isTouch) {
39214                  this.hmenu.add('-',
39215                     {id:"wider", text: this.columnsWiderText},
39216                     {id:"narrow", text: this.columnsNarrowText }
39217                 );
39218                 
39219                  
39220             }
39221             
39222             if(this.grid.enableColumnHide !== false){
39223
39224                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39225                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39226                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39227
39228                 this.hmenu.add('-',
39229                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39230                 );
39231             }
39232             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39233
39234             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39235         }
39236
39237         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39238             this.dd = new Roo.grid.GridDragZone(this.grid, {
39239                 ddGroup : this.grid.ddGroup || 'GridDD'
39240             });
39241             
39242         }
39243
39244         /*
39245         for(var i = 0; i < colCount; i++){
39246             if(cm.isHidden(i)){
39247                 this.hideColumn(i);
39248             }
39249             if(cm.config[i].align){
39250                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39251                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39252             }
39253         }*/
39254         
39255         this.updateHeaderSortState();
39256
39257         this.beforeInitialResize();
39258         this.layout(true);
39259
39260         // two part rendering gives faster view to the user
39261         this.renderPhase2.defer(1, this);
39262     },
39263
39264     renderPhase2 : function(){
39265         // render the rows now
39266         this.refresh();
39267         if(this.grid.autoSizeColumns){
39268             this.autoSizeColumns();
39269         }
39270     },
39271
39272     beforeInitialResize : function(){
39273
39274     },
39275
39276     onColumnSplitterMoved : function(i, w){
39277         this.userResized = true;
39278         var cm = this.grid.colModel;
39279         cm.setColumnWidth(i, w, true);
39280         var cid = cm.getColumnId(i);
39281         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39282         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39283         this.updateSplitters();
39284         this.layout();
39285         this.grid.fireEvent("columnresize", i, w);
39286     },
39287
39288     syncRowHeights : function(startIndex, endIndex){
39289         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39290             startIndex = startIndex || 0;
39291             var mrows = this.getBodyTable().rows;
39292             var lrows = this.getLockedTable().rows;
39293             var len = mrows.length-1;
39294             endIndex = Math.min(endIndex || len, len);
39295             for(var i = startIndex; i <= endIndex; i++){
39296                 var m = mrows[i], l = lrows[i];
39297                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39298                 m.style.height = l.style.height = h + "px";
39299             }
39300         }
39301     },
39302
39303     layout : function(initialRender, is2ndPass)
39304     {
39305         var g = this.grid;
39306         var auto = g.autoHeight;
39307         var scrollOffset = 16;
39308         var c = g.getGridEl(), cm = this.cm,
39309                 expandCol = g.autoExpandColumn,
39310                 gv = this;
39311         //c.beginMeasure();
39312
39313         if(!c.dom.offsetWidth){ // display:none?
39314             if(initialRender){
39315                 this.lockedWrap.show();
39316                 this.mainWrap.show();
39317             }
39318             return;
39319         }
39320
39321         var hasLock = this.cm.isLocked(0);
39322
39323         var tbh = this.headerPanel.getHeight();
39324         var bbh = this.footerPanel.getHeight();
39325
39326         if(auto){
39327             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39328             var newHeight = ch + c.getBorderWidth("tb");
39329             if(g.maxHeight){
39330                 newHeight = Math.min(g.maxHeight, newHeight);
39331             }
39332             c.setHeight(newHeight);
39333         }
39334
39335         if(g.autoWidth){
39336             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39337         }
39338
39339         var s = this.scroller;
39340
39341         var csize = c.getSize(true);
39342
39343         this.el.setSize(csize.width, csize.height);
39344
39345         this.headerPanel.setWidth(csize.width);
39346         this.footerPanel.setWidth(csize.width);
39347
39348         var hdHeight = this.mainHd.getHeight();
39349         var vw = csize.width;
39350         var vh = csize.height - (tbh + bbh);
39351
39352         s.setSize(vw, vh);
39353
39354         var bt = this.getBodyTable();
39355         
39356         if(cm.getLockedCount() == cm.config.length){
39357             bt = this.getLockedTable();
39358         }
39359         
39360         var ltWidth = hasLock ?
39361                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39362
39363         var scrollHeight = bt.offsetHeight;
39364         var scrollWidth = ltWidth + bt.offsetWidth;
39365         var vscroll = false, hscroll = false;
39366
39367         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39368
39369         var lw = this.lockedWrap, mw = this.mainWrap;
39370         var lb = this.lockedBody, mb = this.mainBody;
39371
39372         setTimeout(function(){
39373             var t = s.dom.offsetTop;
39374             var w = s.dom.clientWidth,
39375                 h = s.dom.clientHeight;
39376
39377             lw.setTop(t);
39378             lw.setSize(ltWidth, h);
39379
39380             mw.setLeftTop(ltWidth, t);
39381             mw.setSize(w-ltWidth, h);
39382
39383             lb.setHeight(h-hdHeight);
39384             mb.setHeight(h-hdHeight);
39385
39386             if(is2ndPass !== true && !gv.userResized && expandCol){
39387                 // high speed resize without full column calculation
39388                 
39389                 var ci = cm.getIndexById(expandCol);
39390                 if (ci < 0) {
39391                     ci = cm.findColumnIndex(expandCol);
39392                 }
39393                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39394                 var expandId = cm.getColumnId(ci);
39395                 var  tw = cm.getTotalWidth(false);
39396                 var currentWidth = cm.getColumnWidth(ci);
39397                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39398                 if(currentWidth != cw){
39399                     cm.setColumnWidth(ci, cw, true);
39400                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39401                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39402                     gv.updateSplitters();
39403                     gv.layout(false, true);
39404                 }
39405             }
39406
39407             if(initialRender){
39408                 lw.show();
39409                 mw.show();
39410             }
39411             //c.endMeasure();
39412         }, 10);
39413     },
39414
39415     onWindowResize : function(){
39416         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39417             return;
39418         }
39419         this.layout();
39420     },
39421
39422     appendFooter : function(parentEl){
39423         return null;
39424     },
39425
39426     sortAscText : "Sort Ascending",
39427     sortDescText : "Sort Descending",
39428     lockText : "Lock Column",
39429     unlockText : "Unlock Column",
39430     columnsText : "Columns",
39431  
39432     columnsWiderText : "Wider",
39433     columnsNarrowText : "Thinner"
39434 });
39435
39436
39437 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39438     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39439     this.proxy.el.addClass('x-grid3-col-dd');
39440 };
39441
39442 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39443     handleMouseDown : function(e){
39444
39445     },
39446
39447     callHandleMouseDown : function(e){
39448         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39449     }
39450 });
39451 /*
39452  * Based on:
39453  * Ext JS Library 1.1.1
39454  * Copyright(c) 2006-2007, Ext JS, LLC.
39455  *
39456  * Originally Released Under LGPL - original licence link has changed is not relivant.
39457  *
39458  * Fork - LGPL
39459  * <script type="text/javascript">
39460  */
39461  /**
39462  * @extends Roo.dd.DDProxy
39463  * @class Roo.grid.SplitDragZone
39464  * Support for Column Header resizing
39465  * @constructor
39466  * @param {Object} config
39467  */
39468 // private
39469 // This is a support class used internally by the Grid components
39470 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39471     this.grid = grid;
39472     this.view = grid.getView();
39473     this.proxy = this.view.resizeProxy;
39474     Roo.grid.SplitDragZone.superclass.constructor.call(
39475         this,
39476         hd, // ID
39477         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39478         {  // CONFIG
39479             dragElId : Roo.id(this.proxy.dom),
39480             resizeFrame:false
39481         }
39482     );
39483     
39484     this.setHandleElId(Roo.id(hd));
39485     if (hd2 !== false) {
39486         this.setOuterHandleElId(Roo.id(hd2));
39487     }
39488     
39489     this.scroll = false;
39490 };
39491 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39492     fly: Roo.Element.fly,
39493
39494     b4StartDrag : function(x, y){
39495         this.view.headersDisabled = true;
39496         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39497                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39498         );
39499         this.proxy.setHeight(h);
39500         
39501         // for old system colWidth really stored the actual width?
39502         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39503         // which in reality did not work.. - it worked only for fixed sizes
39504         // for resizable we need to use actual sizes.
39505         var w = this.cm.getColumnWidth(this.cellIndex);
39506         if (!this.view.mainWrap) {
39507             // bootstrap.
39508             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39509         }
39510         
39511         
39512         
39513         // this was w-this.grid.minColumnWidth;
39514         // doesnt really make sense? - w = thie curren width or the rendered one?
39515         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39516         this.resetConstraints();
39517         this.setXConstraint(minw, 1000);
39518         this.setYConstraint(0, 0);
39519         this.minX = x - minw;
39520         this.maxX = x + 1000;
39521         this.startPos = x;
39522         if (!this.view.mainWrap) { // this is Bootstrap code..
39523             this.getDragEl().style.display='block';
39524         }
39525         
39526         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39527     },
39528
39529
39530     handleMouseDown : function(e){
39531         ev = Roo.EventObject.setEvent(e);
39532         var t = this.fly(ev.getTarget());
39533         if(t.hasClass("x-grid-split")){
39534             this.cellIndex = this.view.getCellIndex(t.dom);
39535             this.split = t.dom;
39536             this.cm = this.grid.colModel;
39537             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39538                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39539             }
39540         }
39541     },
39542
39543     endDrag : function(e){
39544         this.view.headersDisabled = false;
39545         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39546         var diff = endX - this.startPos;
39547         // 
39548         var w = this.cm.getColumnWidth(this.cellIndex);
39549         if (!this.view.mainWrap) {
39550             w = 0;
39551         }
39552         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39553     },
39554
39555     autoOffset : function(){
39556         this.setDelta(0,0);
39557     }
39558 });/*
39559  * Based on:
39560  * Ext JS Library 1.1.1
39561  * Copyright(c) 2006-2007, Ext JS, LLC.
39562  *
39563  * Originally Released Under LGPL - original licence link has changed is not relivant.
39564  *
39565  * Fork - LGPL
39566  * <script type="text/javascript">
39567  */
39568  
39569 // private
39570 // This is a support class used internally by the Grid components
39571 Roo.grid.GridDragZone = function(grid, config){
39572     this.view = grid.getView();
39573     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39574     if(this.view.lockedBody){
39575         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39576         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39577     }
39578     this.scroll = false;
39579     this.grid = grid;
39580     this.ddel = document.createElement('div');
39581     this.ddel.className = 'x-grid-dd-wrap';
39582 };
39583
39584 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39585     ddGroup : "GridDD",
39586
39587     getDragData : function(e){
39588         var t = Roo.lib.Event.getTarget(e);
39589         var rowIndex = this.view.findRowIndex(t);
39590         var sm = this.grid.selModel;
39591             
39592         //Roo.log(rowIndex);
39593         
39594         if (sm.getSelectedCell) {
39595             // cell selection..
39596             if (!sm.getSelectedCell()) {
39597                 return false;
39598             }
39599             if (rowIndex != sm.getSelectedCell()[0]) {
39600                 return false;
39601             }
39602         
39603         }
39604         if (sm.getSelections && sm.getSelections().length < 1) {
39605             return false;
39606         }
39607         
39608         
39609         // before it used to all dragging of unseleted... - now we dont do that.
39610         if(rowIndex !== false){
39611             
39612             // if editorgrid.. 
39613             
39614             
39615             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39616                
39617             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39618               //  
39619             //}
39620             if (e.hasModifier()){
39621                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39622             }
39623             
39624             Roo.log("getDragData");
39625             
39626             return {
39627                 grid: this.grid,
39628                 ddel: this.ddel,
39629                 rowIndex: rowIndex,
39630                 selections: sm.getSelections ? sm.getSelections() : (
39631                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39632             };
39633         }
39634         return false;
39635     },
39636     
39637     
39638     onInitDrag : function(e){
39639         var data = this.dragData;
39640         this.ddel.innerHTML = this.grid.getDragDropText();
39641         this.proxy.update(this.ddel);
39642         // fire start drag?
39643     },
39644
39645     afterRepair : function(){
39646         this.dragging = false;
39647     },
39648
39649     getRepairXY : function(e, data){
39650         return false;
39651     },
39652
39653     onEndDrag : function(data, e){
39654         // fire end drag?
39655     },
39656
39657     onValidDrop : function(dd, e, id){
39658         // fire drag drop?
39659         this.hideProxy();
39660     },
39661
39662     beforeInvalidDrop : function(e, id){
39663
39664     }
39665 });/*
39666  * Based on:
39667  * Ext JS Library 1.1.1
39668  * Copyright(c) 2006-2007, Ext JS, LLC.
39669  *
39670  * Originally Released Under LGPL - original licence link has changed is not relivant.
39671  *
39672  * Fork - LGPL
39673  * <script type="text/javascript">
39674  */
39675  
39676
39677 /**
39678  * @class Roo.grid.ColumnModel
39679  * @extends Roo.util.Observable
39680  * This is the default implementation of a ColumnModel used by the Grid. It defines
39681  * the columns in the grid.
39682  * <br>Usage:<br>
39683  <pre><code>
39684  var colModel = new Roo.grid.ColumnModel([
39685         {header: "Ticker", width: 60, sortable: true, locked: true},
39686         {header: "Company Name", width: 150, sortable: true},
39687         {header: "Market Cap.", width: 100, sortable: true},
39688         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39689         {header: "Employees", width: 100, sortable: true, resizable: false}
39690  ]);
39691  </code></pre>
39692  * <p>
39693  
39694  * The config options listed for this class are options which may appear in each
39695  * individual column definition.
39696  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39697  * @constructor
39698  * @param {Object} config An Array of column config objects. See this class's
39699  * config objects for details.
39700 */
39701 Roo.grid.ColumnModel = function(config){
39702         /**
39703      * The config passed into the constructor
39704      */
39705     this.config = []; //config;
39706     this.lookup = {};
39707
39708     // if no id, create one
39709     // if the column does not have a dataIndex mapping,
39710     // map it to the order it is in the config
39711     for(var i = 0, len = config.length; i < len; i++){
39712         this.addColumn(config[i]);
39713         
39714     }
39715
39716     /**
39717      * The width of columns which have no width specified (defaults to 100)
39718      * @type Number
39719      */
39720     this.defaultWidth = 100;
39721
39722     /**
39723      * Default sortable of columns which have no sortable specified (defaults to false)
39724      * @type Boolean
39725      */
39726     this.defaultSortable = false;
39727
39728     this.addEvents({
39729         /**
39730              * @event widthchange
39731              * Fires when the width of a column changes.
39732              * @param {ColumnModel} this
39733              * @param {Number} columnIndex The column index
39734              * @param {Number} newWidth The new width
39735              */
39736             "widthchange": true,
39737         /**
39738              * @event headerchange
39739              * Fires when the text of a header changes.
39740              * @param {ColumnModel} this
39741              * @param {Number} columnIndex The column index
39742              * @param {Number} newText The new header text
39743              */
39744             "headerchange": true,
39745         /**
39746              * @event hiddenchange
39747              * Fires when a column is hidden or "unhidden".
39748              * @param {ColumnModel} this
39749              * @param {Number} columnIndex The column index
39750              * @param {Boolean} hidden true if hidden, false otherwise
39751              */
39752             "hiddenchange": true,
39753             /**
39754          * @event columnmoved
39755          * Fires when a column is moved.
39756          * @param {ColumnModel} this
39757          * @param {Number} oldIndex
39758          * @param {Number} newIndex
39759          */
39760         "columnmoved" : true,
39761         /**
39762          * @event columlockchange
39763          * Fires when a column's locked state is changed
39764          * @param {ColumnModel} this
39765          * @param {Number} colIndex
39766          * @param {Boolean} locked true if locked
39767          */
39768         "columnlockchange" : true
39769     });
39770     Roo.grid.ColumnModel.superclass.constructor.call(this);
39771 };
39772 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39773     /**
39774      * @cfg {String} header The header text to display in the Grid view.
39775      */
39776         /**
39777      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39778      */
39779         /**
39780      * @cfg {String} smHeader Header at Bootsrap Small width
39781      */
39782         /**
39783      * @cfg {String} mdHeader Header at Bootsrap Medium width
39784      */
39785         /**
39786      * @cfg {String} lgHeader Header at Bootsrap Large width
39787      */
39788         /**
39789      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39790      */
39791     /**
39792      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39793      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39794      * specified, the column's index is used as an index into the Record's data Array.
39795      */
39796     /**
39797      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39798      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39799      */
39800     /**
39801      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39802      * Defaults to the value of the {@link #defaultSortable} property.
39803      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39804      */
39805     /**
39806      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39807      */
39808     /**
39809      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39810      */
39811     /**
39812      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39813      */
39814     /**
39815      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39816      */
39817     /**
39818      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39819      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39820      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39821      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39822      */
39823        /**
39824      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39825      */
39826     /**
39827      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39828      */
39829     /**
39830      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
39831      */
39832     /**
39833      * @cfg {String} cursor (Optional)
39834      */
39835     /**
39836      * @cfg {String} tooltip (Optional)
39837      */
39838     /**
39839      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
39840      */
39841     /**
39842      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
39843      */
39844     /**
39845      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
39846      */
39847     /**
39848      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
39849      */
39850         /**
39851      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
39852      */
39853     /**
39854      * Returns the id of the column at the specified index.
39855      * @param {Number} index The column index
39856      * @return {String} the id
39857      */
39858     getColumnId : function(index){
39859         return this.config[index].id;
39860     },
39861
39862     /**
39863      * Returns the column for a specified id.
39864      * @param {String} id The column id
39865      * @return {Object} the column
39866      */
39867     getColumnById : function(id){
39868         return this.lookup[id];
39869     },
39870
39871     
39872     /**
39873      * Returns the column Object for a specified dataIndex.
39874      * @param {String} dataIndex The column dataIndex
39875      * @return {Object|Boolean} the column or false if not found
39876      */
39877     getColumnByDataIndex: function(dataIndex){
39878         var index = this.findColumnIndex(dataIndex);
39879         return index > -1 ? this.config[index] : false;
39880     },
39881     
39882     /**
39883      * Returns the index for a specified column id.
39884      * @param {String} id The column id
39885      * @return {Number} the index, or -1 if not found
39886      */
39887     getIndexById : function(id){
39888         for(var i = 0, len = this.config.length; i < len; i++){
39889             if(this.config[i].id == id){
39890                 return i;
39891             }
39892         }
39893         return -1;
39894     },
39895     
39896     /**
39897      * Returns the index for a specified column dataIndex.
39898      * @param {String} dataIndex The column dataIndex
39899      * @return {Number} the index, or -1 if not found
39900      */
39901     
39902     findColumnIndex : function(dataIndex){
39903         for(var i = 0, len = this.config.length; i < len; i++){
39904             if(this.config[i].dataIndex == dataIndex){
39905                 return i;
39906             }
39907         }
39908         return -1;
39909     },
39910     
39911     
39912     moveColumn : function(oldIndex, newIndex){
39913         var c = this.config[oldIndex];
39914         this.config.splice(oldIndex, 1);
39915         this.config.splice(newIndex, 0, c);
39916         this.dataMap = null;
39917         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39918     },
39919
39920     isLocked : function(colIndex){
39921         return this.config[colIndex].locked === true;
39922     },
39923
39924     setLocked : function(colIndex, value, suppressEvent){
39925         if(this.isLocked(colIndex) == value){
39926             return;
39927         }
39928         this.config[colIndex].locked = value;
39929         if(!suppressEvent){
39930             this.fireEvent("columnlockchange", this, colIndex, value);
39931         }
39932     },
39933
39934     getTotalLockedWidth : function(){
39935         var totalWidth = 0;
39936         for(var i = 0; i < this.config.length; i++){
39937             if(this.isLocked(i) && !this.isHidden(i)){
39938                 this.totalWidth += this.getColumnWidth(i);
39939             }
39940         }
39941         return totalWidth;
39942     },
39943
39944     getLockedCount : function(){
39945         for(var i = 0, len = this.config.length; i < len; i++){
39946             if(!this.isLocked(i)){
39947                 return i;
39948             }
39949         }
39950         
39951         return this.config.length;
39952     },
39953
39954     /**
39955      * Returns the number of columns.
39956      * @return {Number}
39957      */
39958     getColumnCount : function(visibleOnly){
39959         if(visibleOnly === true){
39960             var c = 0;
39961             for(var i = 0, len = this.config.length; i < len; i++){
39962                 if(!this.isHidden(i)){
39963                     c++;
39964                 }
39965             }
39966             return c;
39967         }
39968         return this.config.length;
39969     },
39970
39971     /**
39972      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39973      * @param {Function} fn
39974      * @param {Object} scope (optional)
39975      * @return {Array} result
39976      */
39977     getColumnsBy : function(fn, scope){
39978         var r = [];
39979         for(var i = 0, len = this.config.length; i < len; i++){
39980             var c = this.config[i];
39981             if(fn.call(scope||this, c, i) === true){
39982                 r[r.length] = c;
39983             }
39984         }
39985         return r;
39986     },
39987
39988     /**
39989      * Returns true if the specified column is sortable.
39990      * @param {Number} col The column index
39991      * @return {Boolean}
39992      */
39993     isSortable : function(col){
39994         if(typeof this.config[col].sortable == "undefined"){
39995             return this.defaultSortable;
39996         }
39997         return this.config[col].sortable;
39998     },
39999
40000     /**
40001      * Returns the rendering (formatting) function defined for the column.
40002      * @param {Number} col The column index.
40003      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40004      */
40005     getRenderer : function(col){
40006         if(!this.config[col].renderer){
40007             return Roo.grid.ColumnModel.defaultRenderer;
40008         }
40009         return this.config[col].renderer;
40010     },
40011
40012     /**
40013      * Sets the rendering (formatting) function for a column.
40014      * @param {Number} col The column index
40015      * @param {Function} fn The function to use to process the cell's raw data
40016      * to return HTML markup for the grid view. The render function is called with
40017      * the following parameters:<ul>
40018      * <li>Data value.</li>
40019      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40020      * <li>css A CSS style string to apply to the table cell.</li>
40021      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40022      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40023      * <li>Row index</li>
40024      * <li>Column index</li>
40025      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40026      */
40027     setRenderer : function(col, fn){
40028         this.config[col].renderer = fn;
40029     },
40030
40031     /**
40032      * Returns the width for the specified column.
40033      * @param {Number} col The column index
40034      * @param (optional) {String} gridSize bootstrap width size.
40035      * @return {Number}
40036      */
40037     getColumnWidth : function(col, gridSize)
40038         {
40039                 var cfg = this.config[col];
40040                 
40041                 if (typeof(gridSize) == 'undefined') {
40042                         return cfg.width * 1 || this.defaultWidth;
40043                 }
40044                 if (gridSize === false) { // if we set it..
40045                         return cfg.width || false;
40046                 }
40047                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40048                 
40049                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40050                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40051                                 continue;
40052                         }
40053                         return cfg[ sizes[i] ];
40054                 }
40055                 return 1;
40056                 
40057     },
40058
40059     /**
40060      * Sets the width for a column.
40061      * @param {Number} col The column index
40062      * @param {Number} width The new width
40063      */
40064     setColumnWidth : function(col, width, suppressEvent){
40065         this.config[col].width = width;
40066         this.totalWidth = null;
40067         if(!suppressEvent){
40068              this.fireEvent("widthchange", this, col, width);
40069         }
40070     },
40071
40072     /**
40073      * Returns the total width of all columns.
40074      * @param {Boolean} includeHidden True to include hidden column widths
40075      * @return {Number}
40076      */
40077     getTotalWidth : function(includeHidden){
40078         if(!this.totalWidth){
40079             this.totalWidth = 0;
40080             for(var i = 0, len = this.config.length; i < len; i++){
40081                 if(includeHidden || !this.isHidden(i)){
40082                     this.totalWidth += this.getColumnWidth(i);
40083                 }
40084             }
40085         }
40086         return this.totalWidth;
40087     },
40088
40089     /**
40090      * Returns the header for the specified column.
40091      * @param {Number} col The column index
40092      * @return {String}
40093      */
40094     getColumnHeader : function(col){
40095         return this.config[col].header;
40096     },
40097
40098     /**
40099      * Sets the header for a column.
40100      * @param {Number} col The column index
40101      * @param {String} header The new header
40102      */
40103     setColumnHeader : function(col, header){
40104         this.config[col].header = header;
40105         this.fireEvent("headerchange", this, col, header);
40106     },
40107
40108     /**
40109      * Returns the tooltip for the specified column.
40110      * @param {Number} col The column index
40111      * @return {String}
40112      */
40113     getColumnTooltip : function(col){
40114             return this.config[col].tooltip;
40115     },
40116     /**
40117      * Sets the tooltip for a column.
40118      * @param {Number} col The column index
40119      * @param {String} tooltip The new tooltip
40120      */
40121     setColumnTooltip : function(col, tooltip){
40122             this.config[col].tooltip = tooltip;
40123     },
40124
40125     /**
40126      * Returns the dataIndex for the specified column.
40127      * @param {Number} col The column index
40128      * @return {Number}
40129      */
40130     getDataIndex : function(col){
40131         return this.config[col].dataIndex;
40132     },
40133
40134     /**
40135      * Sets the dataIndex for a column.
40136      * @param {Number} col The column index
40137      * @param {Number} dataIndex The new dataIndex
40138      */
40139     setDataIndex : function(col, dataIndex){
40140         this.config[col].dataIndex = dataIndex;
40141     },
40142
40143     
40144     
40145     /**
40146      * Returns true if the cell is editable.
40147      * @param {Number} colIndex The column index
40148      * @param {Number} rowIndex The row index - this is nto actually used..?
40149      * @return {Boolean}
40150      */
40151     isCellEditable : function(colIndex, rowIndex){
40152         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40153     },
40154
40155     /**
40156      * Returns the editor defined for the cell/column.
40157      * return false or null to disable editing.
40158      * @param {Number} colIndex The column index
40159      * @param {Number} rowIndex The row index
40160      * @return {Object}
40161      */
40162     getCellEditor : function(colIndex, rowIndex){
40163         return this.config[colIndex].editor;
40164     },
40165
40166     /**
40167      * Sets if a column is editable.
40168      * @param {Number} col The column index
40169      * @param {Boolean} editable True if the column is editable
40170      */
40171     setEditable : function(col, editable){
40172         this.config[col].editable = editable;
40173     },
40174
40175
40176     /**
40177      * Returns true if the column is hidden.
40178      * @param {Number} colIndex The column index
40179      * @return {Boolean}
40180      */
40181     isHidden : function(colIndex){
40182         return this.config[colIndex].hidden;
40183     },
40184
40185
40186     /**
40187      * Returns true if the column width cannot be changed
40188      */
40189     isFixed : function(colIndex){
40190         return this.config[colIndex].fixed;
40191     },
40192
40193     /**
40194      * Returns true if the column can be resized
40195      * @return {Boolean}
40196      */
40197     isResizable : function(colIndex){
40198         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40199     },
40200     /**
40201      * Sets if a column is hidden.
40202      * @param {Number} colIndex The column index
40203      * @param {Boolean} hidden True if the column is hidden
40204      */
40205     setHidden : function(colIndex, hidden){
40206         this.config[colIndex].hidden = hidden;
40207         this.totalWidth = null;
40208         this.fireEvent("hiddenchange", this, colIndex, hidden);
40209     },
40210
40211     /**
40212      * Sets the editor for a column.
40213      * @param {Number} col The column index
40214      * @param {Object} editor The editor object
40215      */
40216     setEditor : function(col, editor){
40217         this.config[col].editor = editor;
40218     },
40219     /**
40220      * Add a column (experimental...) - defaults to adding to the end..
40221      * @param {Object} config 
40222     */
40223     addColumn : function(c)
40224     {
40225     
40226         var i = this.config.length;
40227         this.config[i] = c;
40228         
40229         if(typeof c.dataIndex == "undefined"){
40230             c.dataIndex = i;
40231         }
40232         if(typeof c.renderer == "string"){
40233             c.renderer = Roo.util.Format[c.renderer];
40234         }
40235         if(typeof c.id == "undefined"){
40236             c.id = Roo.id();
40237         }
40238         if(c.editor && c.editor.xtype){
40239             c.editor  = Roo.factory(c.editor, Roo.grid);
40240         }
40241         if(c.editor && c.editor.isFormField){
40242             c.editor = new Roo.grid.GridEditor(c.editor);
40243         }
40244         this.lookup[c.id] = c;
40245     }
40246     
40247 });
40248
40249 Roo.grid.ColumnModel.defaultRenderer = function(value)
40250 {
40251     if(typeof value == "object") {
40252         return value;
40253     }
40254         if(typeof value == "string" && value.length < 1){
40255             return "&#160;";
40256         }
40257     
40258         return String.format("{0}", value);
40259 };
40260
40261 // Alias for backwards compatibility
40262 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40263 /*
40264  * Based on:
40265  * Ext JS Library 1.1.1
40266  * Copyright(c) 2006-2007, Ext JS, LLC.
40267  *
40268  * Originally Released Under LGPL - original licence link has changed is not relivant.
40269  *
40270  * Fork - LGPL
40271  * <script type="text/javascript">
40272  */
40273
40274 /**
40275  * @class Roo.grid.AbstractSelectionModel
40276  * @extends Roo.util.Observable
40277  * @abstract
40278  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40279  * implemented by descendant classes.  This class should not be directly instantiated.
40280  * @constructor
40281  */
40282 Roo.grid.AbstractSelectionModel = function(){
40283     this.locked = false;
40284     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40285 };
40286
40287 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40288     /** @ignore Called by the grid automatically. Do not call directly. */
40289     init : function(grid){
40290         this.grid = grid;
40291         this.initEvents();
40292     },
40293
40294     /**
40295      * Locks the selections.
40296      */
40297     lock : function(){
40298         this.locked = true;
40299     },
40300
40301     /**
40302      * Unlocks the selections.
40303      */
40304     unlock : function(){
40305         this.locked = false;
40306     },
40307
40308     /**
40309      * Returns true if the selections are locked.
40310      * @return {Boolean}
40311      */
40312     isLocked : function(){
40313         return this.locked;
40314     }
40315 });/*
40316  * Based on:
40317  * Ext JS Library 1.1.1
40318  * Copyright(c) 2006-2007, Ext JS, LLC.
40319  *
40320  * Originally Released Under LGPL - original licence link has changed is not relivant.
40321  *
40322  * Fork - LGPL
40323  * <script type="text/javascript">
40324  */
40325 /**
40326  * @extends Roo.grid.AbstractSelectionModel
40327  * @class Roo.grid.RowSelectionModel
40328  * The default SelectionModel used by {@link Roo.grid.Grid}.
40329  * It supports multiple selections and keyboard selection/navigation. 
40330  * @constructor
40331  * @param {Object} config
40332  */
40333 Roo.grid.RowSelectionModel = function(config){
40334     Roo.apply(this, config);
40335     this.selections = new Roo.util.MixedCollection(false, function(o){
40336         return o.id;
40337     });
40338
40339     this.last = false;
40340     this.lastActive = false;
40341
40342     this.addEvents({
40343         /**
40344         * @event selectionchange
40345         * Fires when the selection changes
40346         * @param {SelectionModel} this
40347         */
40348        "selectionchange" : true,
40349        /**
40350         * @event afterselectionchange
40351         * Fires after the selection changes (eg. by key press or clicking)
40352         * @param {SelectionModel} this
40353         */
40354        "afterselectionchange" : true,
40355        /**
40356         * @event beforerowselect
40357         * Fires when a row is selected being selected, return false to cancel.
40358         * @param {SelectionModel} this
40359         * @param {Number} rowIndex The selected index
40360         * @param {Boolean} keepExisting False if other selections will be cleared
40361         */
40362        "beforerowselect" : true,
40363        /**
40364         * @event rowselect
40365         * Fires when a row is selected.
40366         * @param {SelectionModel} this
40367         * @param {Number} rowIndex The selected index
40368         * @param {Roo.data.Record} r The record
40369         */
40370        "rowselect" : true,
40371        /**
40372         * @event rowdeselect
40373         * Fires when a row is deselected.
40374         * @param {SelectionModel} this
40375         * @param {Number} rowIndex The selected index
40376         */
40377         "rowdeselect" : true
40378     });
40379     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40380     this.locked = false;
40381 };
40382
40383 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40384     /**
40385      * @cfg {Boolean} singleSelect
40386      * True to allow selection of only one row at a time (defaults to false)
40387      */
40388     singleSelect : false,
40389
40390     // private
40391     initEvents : function(){
40392
40393         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40394             this.grid.on("mousedown", this.handleMouseDown, this);
40395         }else{ // allow click to work like normal
40396             this.grid.on("rowclick", this.handleDragableRowClick, this);
40397         }
40398         // bootstrap does not have a view..
40399         var view = this.grid.view ? this.grid.view : this.grid;
40400         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40401             "up" : function(e){
40402                 if(!e.shiftKey){
40403                     this.selectPrevious(e.shiftKey);
40404                 }else if(this.last !== false && this.lastActive !== false){
40405                     var last = this.last;
40406                     this.selectRange(this.last,  this.lastActive-1);
40407                     view.focusRow(this.lastActive);
40408                     if(last !== false){
40409                         this.last = last;
40410                     }
40411                 }else{
40412                     this.selectFirstRow();
40413                 }
40414                 this.fireEvent("afterselectionchange", this);
40415             },
40416             "down" : function(e){
40417                 if(!e.shiftKey){
40418                     this.selectNext(e.shiftKey);
40419                 }else if(this.last !== false && this.lastActive !== false){
40420                     var last = this.last;
40421                     this.selectRange(this.last,  this.lastActive+1);
40422                     view.focusRow(this.lastActive);
40423                     if(last !== false){
40424                         this.last = last;
40425                     }
40426                 }else{
40427                     this.selectFirstRow();
40428                 }
40429                 this.fireEvent("afterselectionchange", this);
40430             },
40431             scope: this
40432         });
40433
40434          
40435         view.on("refresh", this.onRefresh, this);
40436         view.on("rowupdated", this.onRowUpdated, this);
40437         view.on("rowremoved", this.onRemove, this);
40438     },
40439
40440     // private
40441     onRefresh : function(){
40442         var ds = this.grid.ds, i, v = this.grid.view;
40443         var s = this.selections;
40444         s.each(function(r){
40445             if((i = ds.indexOfId(r.id)) != -1){
40446                 v.onRowSelect(i);
40447                 s.add(ds.getAt(i)); // updating the selection relate data
40448             }else{
40449                 s.remove(r);
40450             }
40451         });
40452     },
40453
40454     // private
40455     onRemove : function(v, index, r){
40456         this.selections.remove(r);
40457     },
40458
40459     // private
40460     onRowUpdated : function(v, index, r){
40461         if(this.isSelected(r)){
40462             v.onRowSelect(index);
40463         }
40464     },
40465
40466     /**
40467      * Select records.
40468      * @param {Array} records The records to select
40469      * @param {Boolean} keepExisting (optional) True to keep existing selections
40470      */
40471     selectRecords : function(records, keepExisting){
40472         if(!keepExisting){
40473             this.clearSelections();
40474         }
40475         var ds = this.grid.ds;
40476         for(var i = 0, len = records.length; i < len; i++){
40477             this.selectRow(ds.indexOf(records[i]), true);
40478         }
40479     },
40480
40481     /**
40482      * Gets the number of selected rows.
40483      * @return {Number}
40484      */
40485     getCount : function(){
40486         return this.selections.length;
40487     },
40488
40489     /**
40490      * Selects the first row in the grid.
40491      */
40492     selectFirstRow : function(){
40493         this.selectRow(0);
40494     },
40495
40496     /**
40497      * Select the last row.
40498      * @param {Boolean} keepExisting (optional) True to keep existing selections
40499      */
40500     selectLastRow : function(keepExisting){
40501         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40502     },
40503
40504     /**
40505      * Selects the row immediately following the last selected row.
40506      * @param {Boolean} keepExisting (optional) True to keep existing selections
40507      */
40508     selectNext : function(keepExisting){
40509         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40510             this.selectRow(this.last+1, keepExisting);
40511             var view = this.grid.view ? this.grid.view : this.grid;
40512             view.focusRow(this.last);
40513         }
40514     },
40515
40516     /**
40517      * Selects the row that precedes the last selected row.
40518      * @param {Boolean} keepExisting (optional) True to keep existing selections
40519      */
40520     selectPrevious : function(keepExisting){
40521         if(this.last){
40522             this.selectRow(this.last-1, keepExisting);
40523             var view = this.grid.view ? this.grid.view : this.grid;
40524             view.focusRow(this.last);
40525         }
40526     },
40527
40528     /**
40529      * Returns the selected records
40530      * @return {Array} Array of selected records
40531      */
40532     getSelections : function(){
40533         return [].concat(this.selections.items);
40534     },
40535
40536     /**
40537      * Returns the first selected record.
40538      * @return {Record}
40539      */
40540     getSelected : function(){
40541         return this.selections.itemAt(0);
40542     },
40543
40544
40545     /**
40546      * Clears all selections.
40547      */
40548     clearSelections : function(fast){
40549         if(this.locked) {
40550             return;
40551         }
40552         if(fast !== true){
40553             var ds = this.grid.ds;
40554             var s = this.selections;
40555             s.each(function(r){
40556                 this.deselectRow(ds.indexOfId(r.id));
40557             }, this);
40558             s.clear();
40559         }else{
40560             this.selections.clear();
40561         }
40562         this.last = false;
40563     },
40564
40565
40566     /**
40567      * Selects all rows.
40568      */
40569     selectAll : function(){
40570         if(this.locked) {
40571             return;
40572         }
40573         this.selections.clear();
40574         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40575             this.selectRow(i, true);
40576         }
40577     },
40578
40579     /**
40580      * Returns True if there is a selection.
40581      * @return {Boolean}
40582      */
40583     hasSelection : function(){
40584         return this.selections.length > 0;
40585     },
40586
40587     /**
40588      * Returns True if the specified row is selected.
40589      * @param {Number/Record} record The record or index of the record to check
40590      * @return {Boolean}
40591      */
40592     isSelected : function(index){
40593         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40594         return (r && this.selections.key(r.id) ? true : false);
40595     },
40596
40597     /**
40598      * Returns True if the specified record id is selected.
40599      * @param {String} id The id of record to check
40600      * @return {Boolean}
40601      */
40602     isIdSelected : function(id){
40603         return (this.selections.key(id) ? true : false);
40604     },
40605
40606     // private
40607     handleMouseDown : function(e, t)
40608     {
40609         var view = this.grid.view ? this.grid.view : this.grid;
40610         var rowIndex;
40611         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40612             return;
40613         };
40614         if(e.shiftKey && this.last !== false){
40615             var last = this.last;
40616             this.selectRange(last, rowIndex, e.ctrlKey);
40617             this.last = last; // reset the last
40618             view.focusRow(rowIndex);
40619         }else{
40620             var isSelected = this.isSelected(rowIndex);
40621             if(e.button !== 0 && isSelected){
40622                 view.focusRow(rowIndex);
40623             }else if(e.ctrlKey && isSelected){
40624                 this.deselectRow(rowIndex);
40625             }else if(!isSelected){
40626                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40627                 view.focusRow(rowIndex);
40628             }
40629         }
40630         this.fireEvent("afterselectionchange", this);
40631     },
40632     // private
40633     handleDragableRowClick :  function(grid, rowIndex, e) 
40634     {
40635         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40636             this.selectRow(rowIndex, false);
40637             var view = this.grid.view ? this.grid.view : this.grid;
40638             view.focusRow(rowIndex);
40639              this.fireEvent("afterselectionchange", this);
40640         }
40641     },
40642     
40643     /**
40644      * Selects multiple rows.
40645      * @param {Array} rows Array of the indexes of the row to select
40646      * @param {Boolean} keepExisting (optional) True to keep existing selections
40647      */
40648     selectRows : function(rows, keepExisting){
40649         if(!keepExisting){
40650             this.clearSelections();
40651         }
40652         for(var i = 0, len = rows.length; i < len; i++){
40653             this.selectRow(rows[i], true);
40654         }
40655     },
40656
40657     /**
40658      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40659      * @param {Number} startRow The index of the first row in the range
40660      * @param {Number} endRow The index of the last row in the range
40661      * @param {Boolean} keepExisting (optional) True to retain existing selections
40662      */
40663     selectRange : function(startRow, endRow, keepExisting){
40664         if(this.locked) {
40665             return;
40666         }
40667         if(!keepExisting){
40668             this.clearSelections();
40669         }
40670         if(startRow <= endRow){
40671             for(var i = startRow; i <= endRow; i++){
40672                 this.selectRow(i, true);
40673             }
40674         }else{
40675             for(var i = startRow; i >= endRow; i--){
40676                 this.selectRow(i, true);
40677             }
40678         }
40679     },
40680
40681     /**
40682      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40683      * @param {Number} startRow The index of the first row in the range
40684      * @param {Number} endRow The index of the last row in the range
40685      */
40686     deselectRange : function(startRow, endRow, preventViewNotify){
40687         if(this.locked) {
40688             return;
40689         }
40690         for(var i = startRow; i <= endRow; i++){
40691             this.deselectRow(i, preventViewNotify);
40692         }
40693     },
40694
40695     /**
40696      * Selects a row.
40697      * @param {Number} row The index of the row to select
40698      * @param {Boolean} keepExisting (optional) True to keep existing selections
40699      */
40700     selectRow : function(index, keepExisting, preventViewNotify){
40701         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40702             return;
40703         }
40704         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40705             if(!keepExisting || this.singleSelect){
40706                 this.clearSelections();
40707             }
40708             var r = this.grid.ds.getAt(index);
40709             this.selections.add(r);
40710             this.last = this.lastActive = index;
40711             if(!preventViewNotify){
40712                 var view = this.grid.view ? this.grid.view : this.grid;
40713                 view.onRowSelect(index);
40714             }
40715             this.fireEvent("rowselect", this, index, r);
40716             this.fireEvent("selectionchange", this);
40717         }
40718     },
40719
40720     /**
40721      * Deselects a row.
40722      * @param {Number} row The index of the row to deselect
40723      */
40724     deselectRow : function(index, preventViewNotify){
40725         if(this.locked) {
40726             return;
40727         }
40728         if(this.last == index){
40729             this.last = false;
40730         }
40731         if(this.lastActive == index){
40732             this.lastActive = false;
40733         }
40734         var r = this.grid.ds.getAt(index);
40735         this.selections.remove(r);
40736         if(!preventViewNotify){
40737             var view = this.grid.view ? this.grid.view : this.grid;
40738             view.onRowDeselect(index);
40739         }
40740         this.fireEvent("rowdeselect", this, index);
40741         this.fireEvent("selectionchange", this);
40742     },
40743
40744     // private
40745     restoreLast : function(){
40746         if(this._last){
40747             this.last = this._last;
40748         }
40749     },
40750
40751     // private
40752     acceptsNav : function(row, col, cm){
40753         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40754     },
40755
40756     // private
40757     onEditorKey : function(field, e){
40758         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40759         if(k == e.TAB){
40760             e.stopEvent();
40761             ed.completeEdit();
40762             if(e.shiftKey){
40763                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40764             }else{
40765                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40766             }
40767         }else if(k == e.ENTER && !e.ctrlKey){
40768             e.stopEvent();
40769             ed.completeEdit();
40770             if(e.shiftKey){
40771                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40772             }else{
40773                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40774             }
40775         }else if(k == e.ESC){
40776             ed.cancelEdit();
40777         }
40778         if(newCell){
40779             g.startEditing(newCell[0], newCell[1]);
40780         }
40781     }
40782 });/*
40783  * Based on:
40784  * Ext JS Library 1.1.1
40785  * Copyright(c) 2006-2007, Ext JS, LLC.
40786  *
40787  * Originally Released Under LGPL - original licence link has changed is not relivant.
40788  *
40789  * Fork - LGPL
40790  * <script type="text/javascript">
40791  */
40792 /**
40793  * @class Roo.grid.CellSelectionModel
40794  * @extends Roo.grid.AbstractSelectionModel
40795  * This class provides the basic implementation for cell selection in a grid.
40796  * @constructor
40797  * @param {Object} config The object containing the configuration of this model.
40798  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40799  */
40800 Roo.grid.CellSelectionModel = function(config){
40801     Roo.apply(this, config);
40802
40803     this.selection = null;
40804
40805     this.addEvents({
40806         /**
40807              * @event beforerowselect
40808              * Fires before a cell is selected.
40809              * @param {SelectionModel} this
40810              * @param {Number} rowIndex The selected row index
40811              * @param {Number} colIndex The selected cell index
40812              */
40813             "beforecellselect" : true,
40814         /**
40815              * @event cellselect
40816              * Fires when a cell is selected.
40817              * @param {SelectionModel} this
40818              * @param {Number} rowIndex The selected row index
40819              * @param {Number} colIndex The selected cell index
40820              */
40821             "cellselect" : true,
40822         /**
40823              * @event selectionchange
40824              * Fires when the active selection changes.
40825              * @param {SelectionModel} this
40826              * @param {Object} selection null for no selection or an object (o) with two properties
40827                 <ul>
40828                 <li>o.record: the record object for the row the selection is in</li>
40829                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40830                 </ul>
40831              */
40832             "selectionchange" : true,
40833         /**
40834              * @event tabend
40835              * Fires when the tab (or enter) was pressed on the last editable cell
40836              * You can use this to trigger add new row.
40837              * @param {SelectionModel} this
40838              */
40839             "tabend" : true,
40840          /**
40841              * @event beforeeditnext
40842              * Fires before the next editable sell is made active
40843              * You can use this to skip to another cell or fire the tabend
40844              *    if you set cell to false
40845              * @param {Object} eventdata object : { cell : [ row, col ] } 
40846              */
40847             "beforeeditnext" : true
40848     });
40849     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40850 };
40851
40852 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40853     
40854     enter_is_tab: false,
40855
40856     /** @ignore */
40857     initEvents : function(){
40858         this.grid.on("mousedown", this.handleMouseDown, this);
40859         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40860         var view = this.grid.view;
40861         view.on("refresh", this.onViewChange, this);
40862         view.on("rowupdated", this.onRowUpdated, this);
40863         view.on("beforerowremoved", this.clearSelections, this);
40864         view.on("beforerowsinserted", this.clearSelections, this);
40865         if(this.grid.isEditor){
40866             this.grid.on("beforeedit", this.beforeEdit,  this);
40867         }
40868     },
40869
40870         //private
40871     beforeEdit : function(e){
40872         this.select(e.row, e.column, false, true, e.record);
40873     },
40874
40875         //private
40876     onRowUpdated : function(v, index, r){
40877         if(this.selection && this.selection.record == r){
40878             v.onCellSelect(index, this.selection.cell[1]);
40879         }
40880     },
40881
40882         //private
40883     onViewChange : function(){
40884         this.clearSelections(true);
40885     },
40886
40887         /**
40888          * Returns the currently selected cell,.
40889          * @return {Array} The selected cell (row, column) or null if none selected.
40890          */
40891     getSelectedCell : function(){
40892         return this.selection ? this.selection.cell : null;
40893     },
40894
40895     /**
40896      * Clears all selections.
40897      * @param {Boolean} true to prevent the gridview from being notified about the change.
40898      */
40899     clearSelections : function(preventNotify){
40900         var s = this.selection;
40901         if(s){
40902             if(preventNotify !== true){
40903                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40904             }
40905             this.selection = null;
40906             this.fireEvent("selectionchange", this, null);
40907         }
40908     },
40909
40910     /**
40911      * Returns true if there is a selection.
40912      * @return {Boolean}
40913      */
40914     hasSelection : function(){
40915         return this.selection ? true : false;
40916     },
40917
40918     /** @ignore */
40919     handleMouseDown : function(e, t){
40920         var v = this.grid.getView();
40921         if(this.isLocked()){
40922             return;
40923         };
40924         var row = v.findRowIndex(t);
40925         var cell = v.findCellIndex(t);
40926         if(row !== false && cell !== false){
40927             this.select(row, cell);
40928         }
40929     },
40930
40931     /**
40932      * Selects a cell.
40933      * @param {Number} rowIndex
40934      * @param {Number} collIndex
40935      */
40936     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40937         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40938             this.clearSelections();
40939             r = r || this.grid.dataSource.getAt(rowIndex);
40940             this.selection = {
40941                 record : r,
40942                 cell : [rowIndex, colIndex]
40943             };
40944             if(!preventViewNotify){
40945                 var v = this.grid.getView();
40946                 v.onCellSelect(rowIndex, colIndex);
40947                 if(preventFocus !== true){
40948                     v.focusCell(rowIndex, colIndex);
40949                 }
40950             }
40951             this.fireEvent("cellselect", this, rowIndex, colIndex);
40952             this.fireEvent("selectionchange", this, this.selection);
40953         }
40954     },
40955
40956         //private
40957     isSelectable : function(rowIndex, colIndex, cm){
40958         return !cm.isHidden(colIndex);
40959     },
40960
40961     /** @ignore */
40962     handleKeyDown : function(e){
40963         //Roo.log('Cell Sel Model handleKeyDown');
40964         if(!e.isNavKeyPress()){
40965             return;
40966         }
40967         var g = this.grid, s = this.selection;
40968         if(!s){
40969             e.stopEvent();
40970             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40971             if(cell){
40972                 this.select(cell[0], cell[1]);
40973             }
40974             return;
40975         }
40976         var sm = this;
40977         var walk = function(row, col, step){
40978             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40979         };
40980         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40981         var newCell;
40982
40983       
40984
40985         switch(k){
40986             case e.TAB:
40987                 // handled by onEditorKey
40988                 if (g.isEditor && g.editing) {
40989                     return;
40990                 }
40991                 if(e.shiftKey) {
40992                     newCell = walk(r, c-1, -1);
40993                 } else {
40994                     newCell = walk(r, c+1, 1);
40995                 }
40996                 break;
40997             
40998             case e.DOWN:
40999                newCell = walk(r+1, c, 1);
41000                 break;
41001             
41002             case e.UP:
41003                 newCell = walk(r-1, c, -1);
41004                 break;
41005             
41006             case e.RIGHT:
41007                 newCell = walk(r, c+1, 1);
41008                 break;
41009             
41010             case e.LEFT:
41011                 newCell = walk(r, c-1, -1);
41012                 break;
41013             
41014             case e.ENTER:
41015                 
41016                 if(g.isEditor && !g.editing){
41017                    g.startEditing(r, c);
41018                    e.stopEvent();
41019                    return;
41020                 }
41021                 
41022                 
41023              break;
41024         };
41025         if(newCell){
41026             this.select(newCell[0], newCell[1]);
41027             e.stopEvent();
41028             
41029         }
41030     },
41031
41032     acceptsNav : function(row, col, cm){
41033         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41034     },
41035     /**
41036      * Selects a cell.
41037      * @param {Number} field (not used) - as it's normally used as a listener
41038      * @param {Number} e - event - fake it by using
41039      *
41040      * var e = Roo.EventObjectImpl.prototype;
41041      * e.keyCode = e.TAB
41042      *
41043      * 
41044      */
41045     onEditorKey : function(field, e){
41046         
41047         var k = e.getKey(),
41048             newCell,
41049             g = this.grid,
41050             ed = g.activeEditor,
41051             forward = false;
41052         ///Roo.log('onEditorKey' + k);
41053         
41054         
41055         if (this.enter_is_tab && k == e.ENTER) {
41056             k = e.TAB;
41057         }
41058         
41059         if(k == e.TAB){
41060             if(e.shiftKey){
41061                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41062             }else{
41063                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41064                 forward = true;
41065             }
41066             
41067             e.stopEvent();
41068             
41069         } else if(k == e.ENTER &&  !e.ctrlKey){
41070             ed.completeEdit();
41071             e.stopEvent();
41072             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41073         
41074                 } else if(k == e.ESC){
41075             ed.cancelEdit();
41076         }
41077                 
41078         if (newCell) {
41079             var ecall = { cell : newCell, forward : forward };
41080             this.fireEvent('beforeeditnext', ecall );
41081             newCell = ecall.cell;
41082                         forward = ecall.forward;
41083         }
41084                 
41085         if(newCell){
41086             //Roo.log('next cell after edit');
41087             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41088         } else if (forward) {
41089             // tabbed past last
41090             this.fireEvent.defer(100, this, ['tabend',this]);
41091         }
41092     }
41093 });/*
41094  * Based on:
41095  * Ext JS Library 1.1.1
41096  * Copyright(c) 2006-2007, Ext JS, LLC.
41097  *
41098  * Originally Released Under LGPL - original licence link has changed is not relivant.
41099  *
41100  * Fork - LGPL
41101  * <script type="text/javascript">
41102  */
41103  
41104 /**
41105  * @class Roo.grid.EditorGrid
41106  * @extends Roo.grid.Grid
41107  * Class for creating and editable grid.
41108  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41109  * The container MUST have some type of size defined for the grid to fill. The container will be 
41110  * automatically set to position relative if it isn't already.
41111  * @param {Object} dataSource The data model to bind to
41112  * @param {Object} colModel The column model with info about this grid's columns
41113  */
41114 Roo.grid.EditorGrid = function(container, config){
41115     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41116     this.getGridEl().addClass("xedit-grid");
41117
41118     if(!this.selModel){
41119         this.selModel = new Roo.grid.CellSelectionModel();
41120     }
41121
41122     this.activeEditor = null;
41123
41124         this.addEvents({
41125             /**
41126              * @event beforeedit
41127              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41128              * <ul style="padding:5px;padding-left:16px;">
41129              * <li>grid - This grid</li>
41130              * <li>record - The record being edited</li>
41131              * <li>field - The field name being edited</li>
41132              * <li>value - The value for the field being edited.</li>
41133              * <li>row - The grid row index</li>
41134              * <li>column - The grid column index</li>
41135              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41136              * </ul>
41137              * @param {Object} e An edit event (see above for description)
41138              */
41139             "beforeedit" : true,
41140             /**
41141              * @event afteredit
41142              * Fires after a cell is edited. <br />
41143              * <ul style="padding:5px;padding-left:16px;">
41144              * <li>grid - This grid</li>
41145              * <li>record - The record being edited</li>
41146              * <li>field - The field name being edited</li>
41147              * <li>value - The value being set</li>
41148              * <li>originalValue - The original value for the field, before the edit.</li>
41149              * <li>row - The grid row index</li>
41150              * <li>column - The grid column index</li>
41151              * </ul>
41152              * @param {Object} e An edit event (see above for description)
41153              */
41154             "afteredit" : true,
41155             /**
41156              * @event validateedit
41157              * Fires after a cell is edited, but before the value is set in the record. 
41158          * You can use this to modify the value being set in the field, Return false
41159              * to cancel the change. The edit event object has the following properties <br />
41160              * <ul style="padding:5px;padding-left:16px;">
41161          * <li>editor - This editor</li>
41162              * <li>grid - This grid</li>
41163              * <li>record - The record being edited</li>
41164              * <li>field - The field name being edited</li>
41165              * <li>value - The value being set</li>
41166              * <li>originalValue - The original value for the field, before the edit.</li>
41167              * <li>row - The grid row index</li>
41168              * <li>column - The grid column index</li>
41169              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41170              * </ul>
41171              * @param {Object} e An edit event (see above for description)
41172              */
41173             "validateedit" : true
41174         });
41175     this.on("bodyscroll", this.stopEditing,  this);
41176     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41177 };
41178
41179 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41180     /**
41181      * @cfg {Number} clicksToEdit
41182      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41183      */
41184     clicksToEdit: 2,
41185
41186     // private
41187     isEditor : true,
41188     // private
41189     trackMouseOver: false, // causes very odd FF errors
41190
41191     onCellDblClick : function(g, row, col){
41192         this.startEditing(row, col);
41193     },
41194
41195     onEditComplete : function(ed, value, startValue){
41196         this.editing = false;
41197         this.activeEditor = null;
41198         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41199         var r = ed.record;
41200         var field = this.colModel.getDataIndex(ed.col);
41201         var e = {
41202             grid: this,
41203             record: r,
41204             field: field,
41205             originalValue: startValue,
41206             value: value,
41207             row: ed.row,
41208             column: ed.col,
41209             cancel:false,
41210             editor: ed
41211         };
41212         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41213         cell.show();
41214           
41215         if(String(value) !== String(startValue)){
41216             
41217             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41218                 r.set(field, e.value);
41219                 // if we are dealing with a combo box..
41220                 // then we also set the 'name' colum to be the displayField
41221                 if (ed.field.displayField && ed.field.name) {
41222                     r.set(ed.field.name, ed.field.el.dom.value);
41223                 }
41224                 
41225                 delete e.cancel; //?? why!!!
41226                 this.fireEvent("afteredit", e);
41227             }
41228         } else {
41229             this.fireEvent("afteredit", e); // always fire it!
41230         }
41231         this.view.focusCell(ed.row, ed.col);
41232     },
41233
41234     /**
41235      * Starts editing the specified for the specified row/column
41236      * @param {Number} rowIndex
41237      * @param {Number} colIndex
41238      */
41239     startEditing : function(row, col){
41240         this.stopEditing();
41241         if(this.colModel.isCellEditable(col, row)){
41242             this.view.ensureVisible(row, col, true);
41243           
41244             var r = this.dataSource.getAt(row);
41245             var field = this.colModel.getDataIndex(col);
41246             var cell = Roo.get(this.view.getCell(row,col));
41247             var e = {
41248                 grid: this,
41249                 record: r,
41250                 field: field,
41251                 value: r.data[field],
41252                 row: row,
41253                 column: col,
41254                 cancel:false 
41255             };
41256             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41257                 this.editing = true;
41258                 var ed = this.colModel.getCellEditor(col, row);
41259                 
41260                 if (!ed) {
41261                     return;
41262                 }
41263                 if(!ed.rendered){
41264                     ed.render(ed.parentEl || document.body);
41265                 }
41266                 ed.field.reset();
41267                
41268                 cell.hide();
41269                 
41270                 (function(){ // complex but required for focus issues in safari, ie and opera
41271                     ed.row = row;
41272                     ed.col = col;
41273                     ed.record = r;
41274                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41275                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41276                     this.activeEditor = ed;
41277                     var v = r.data[field];
41278                     ed.startEdit(this.view.getCell(row, col), v);
41279                     // combo's with 'displayField and name set
41280                     if (ed.field.displayField && ed.field.name) {
41281                         ed.field.el.dom.value = r.data[ed.field.name];
41282                     }
41283                     
41284                     
41285                 }).defer(50, this);
41286             }
41287         }
41288     },
41289         
41290     /**
41291      * Stops any active editing
41292      */
41293     stopEditing : function(){
41294         if(this.activeEditor){
41295             this.activeEditor.completeEdit();
41296         }
41297         this.activeEditor = null;
41298     },
41299         
41300          /**
41301      * Called to get grid's drag proxy text, by default returns this.ddText.
41302      * @return {String}
41303      */
41304     getDragDropText : function(){
41305         var count = this.selModel.getSelectedCell() ? 1 : 0;
41306         return String.format(this.ddText, count, count == 1 ? '' : 's');
41307     }
41308         
41309 });/*
41310  * Based on:
41311  * Ext JS Library 1.1.1
41312  * Copyright(c) 2006-2007, Ext JS, LLC.
41313  *
41314  * Originally Released Under LGPL - original licence link has changed is not relivant.
41315  *
41316  * Fork - LGPL
41317  * <script type="text/javascript">
41318  */
41319
41320 // private - not really -- you end up using it !
41321 // This is a support class used internally by the Grid components
41322
41323 /**
41324  * @class Roo.grid.GridEditor
41325  * @extends Roo.Editor
41326  * Class for creating and editable grid elements.
41327  * @param {Object} config any settings (must include field)
41328  */
41329 Roo.grid.GridEditor = function(field, config){
41330     if (!config && field.field) {
41331         config = field;
41332         field = Roo.factory(config.field, Roo.form);
41333     }
41334     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41335     field.monitorTab = false;
41336 };
41337
41338 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41339     
41340     /**
41341      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41342      */
41343     
41344     alignment: "tl-tl",
41345     autoSize: "width",
41346     hideEl : false,
41347     cls: "x-small-editor x-grid-editor",
41348     shim:false,
41349     shadow:"frame"
41350 });/*
41351  * Based on:
41352  * Ext JS Library 1.1.1
41353  * Copyright(c) 2006-2007, Ext JS, LLC.
41354  *
41355  * Originally Released Under LGPL - original licence link has changed is not relivant.
41356  *
41357  * Fork - LGPL
41358  * <script type="text/javascript">
41359  */
41360   
41361
41362   
41363 Roo.grid.PropertyRecord = Roo.data.Record.create([
41364     {name:'name',type:'string'},  'value'
41365 ]);
41366
41367
41368 Roo.grid.PropertyStore = function(grid, source){
41369     this.grid = grid;
41370     this.store = new Roo.data.Store({
41371         recordType : Roo.grid.PropertyRecord
41372     });
41373     this.store.on('update', this.onUpdate,  this);
41374     if(source){
41375         this.setSource(source);
41376     }
41377     Roo.grid.PropertyStore.superclass.constructor.call(this);
41378 };
41379
41380
41381
41382 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41383     setSource : function(o){
41384         this.source = o;
41385         this.store.removeAll();
41386         var data = [];
41387         for(var k in o){
41388             if(this.isEditableValue(o[k])){
41389                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41390             }
41391         }
41392         this.store.loadRecords({records: data}, {}, true);
41393     },
41394
41395     onUpdate : function(ds, record, type){
41396         if(type == Roo.data.Record.EDIT){
41397             var v = record.data['value'];
41398             var oldValue = record.modified['value'];
41399             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41400                 this.source[record.id] = v;
41401                 record.commit();
41402                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41403             }else{
41404                 record.reject();
41405             }
41406         }
41407     },
41408
41409     getProperty : function(row){
41410        return this.store.getAt(row);
41411     },
41412
41413     isEditableValue: function(val){
41414         if(val && val instanceof Date){
41415             return true;
41416         }else if(typeof val == 'object' || typeof val == 'function'){
41417             return false;
41418         }
41419         return true;
41420     },
41421
41422     setValue : function(prop, value){
41423         this.source[prop] = value;
41424         this.store.getById(prop).set('value', value);
41425     },
41426
41427     getSource : function(){
41428         return this.source;
41429     }
41430 });
41431
41432 Roo.grid.PropertyColumnModel = function(grid, store){
41433     this.grid = grid;
41434     var g = Roo.grid;
41435     g.PropertyColumnModel.superclass.constructor.call(this, [
41436         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41437         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41438     ]);
41439     this.store = store;
41440     this.bselect = Roo.DomHelper.append(document.body, {
41441         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41442             {tag: 'option', value: 'true', html: 'true'},
41443             {tag: 'option', value: 'false', html: 'false'}
41444         ]
41445     });
41446     Roo.id(this.bselect);
41447     var f = Roo.form;
41448     this.editors = {
41449         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41450         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41451         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41452         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41453         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41454     };
41455     this.renderCellDelegate = this.renderCell.createDelegate(this);
41456     this.renderPropDelegate = this.renderProp.createDelegate(this);
41457 };
41458
41459 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41460     
41461     
41462     nameText : 'Name',
41463     valueText : 'Value',
41464     
41465     dateFormat : 'm/j/Y',
41466     
41467     
41468     renderDate : function(dateVal){
41469         return dateVal.dateFormat(this.dateFormat);
41470     },
41471
41472     renderBool : function(bVal){
41473         return bVal ? 'true' : 'false';
41474     },
41475
41476     isCellEditable : function(colIndex, rowIndex){
41477         return colIndex == 1;
41478     },
41479
41480     getRenderer : function(col){
41481         return col == 1 ?
41482             this.renderCellDelegate : this.renderPropDelegate;
41483     },
41484
41485     renderProp : function(v){
41486         return this.getPropertyName(v);
41487     },
41488
41489     renderCell : function(val){
41490         var rv = val;
41491         if(val instanceof Date){
41492             rv = this.renderDate(val);
41493         }else if(typeof val == 'boolean'){
41494             rv = this.renderBool(val);
41495         }
41496         return Roo.util.Format.htmlEncode(rv);
41497     },
41498
41499     getPropertyName : function(name){
41500         var pn = this.grid.propertyNames;
41501         return pn && pn[name] ? pn[name] : name;
41502     },
41503
41504     getCellEditor : function(colIndex, rowIndex){
41505         var p = this.store.getProperty(rowIndex);
41506         var n = p.data['name'], val = p.data['value'];
41507         
41508         if(typeof(this.grid.customEditors[n]) == 'string'){
41509             return this.editors[this.grid.customEditors[n]];
41510         }
41511         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41512             return this.grid.customEditors[n];
41513         }
41514         if(val instanceof Date){
41515             return this.editors['date'];
41516         }else if(typeof val == 'number'){
41517             return this.editors['number'];
41518         }else if(typeof val == 'boolean'){
41519             return this.editors['boolean'];
41520         }else{
41521             return this.editors['string'];
41522         }
41523     }
41524 });
41525
41526 /**
41527  * @class Roo.grid.PropertyGrid
41528  * @extends Roo.grid.EditorGrid
41529  * This class represents the  interface of a component based property grid control.
41530  * <br><br>Usage:<pre><code>
41531  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41532       
41533  });
41534  // set any options
41535  grid.render();
41536  * </code></pre>
41537   
41538  * @constructor
41539  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41540  * The container MUST have some type of size defined for the grid to fill. The container will be
41541  * automatically set to position relative if it isn't already.
41542  * @param {Object} config A config object that sets properties on this grid.
41543  */
41544 Roo.grid.PropertyGrid = function(container, config){
41545     config = config || {};
41546     var store = new Roo.grid.PropertyStore(this);
41547     this.store = store;
41548     var cm = new Roo.grid.PropertyColumnModel(this, store);
41549     store.store.sort('name', 'ASC');
41550     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41551         ds: store.store,
41552         cm: cm,
41553         enableColLock:false,
41554         enableColumnMove:false,
41555         stripeRows:false,
41556         trackMouseOver: false,
41557         clicksToEdit:1
41558     }, config));
41559     this.getGridEl().addClass('x-props-grid');
41560     this.lastEditRow = null;
41561     this.on('columnresize', this.onColumnResize, this);
41562     this.addEvents({
41563          /**
41564              * @event beforepropertychange
41565              * Fires before a property changes (return false to stop?)
41566              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41567              * @param {String} id Record Id
41568              * @param {String} newval New Value
41569          * @param {String} oldval Old Value
41570              */
41571         "beforepropertychange": true,
41572         /**
41573              * @event propertychange
41574              * Fires after a property changes
41575              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41576              * @param {String} id Record Id
41577              * @param {String} newval New Value
41578          * @param {String} oldval Old Value
41579              */
41580         "propertychange": true
41581     });
41582     this.customEditors = this.customEditors || {};
41583 };
41584 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41585     
41586      /**
41587      * @cfg {Object} customEditors map of colnames=> custom editors.
41588      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41589      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41590      * false disables editing of the field.
41591          */
41592     
41593       /**
41594      * @cfg {Object} propertyNames map of property Names to their displayed value
41595          */
41596     
41597     render : function(){
41598         Roo.grid.PropertyGrid.superclass.render.call(this);
41599         this.autoSize.defer(100, this);
41600     },
41601
41602     autoSize : function(){
41603         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41604         if(this.view){
41605             this.view.fitColumns();
41606         }
41607     },
41608
41609     onColumnResize : function(){
41610         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41611         this.autoSize();
41612     },
41613     /**
41614      * Sets the data for the Grid
41615      * accepts a Key => Value object of all the elements avaiable.
41616      * @param {Object} data  to appear in grid.
41617      */
41618     setSource : function(source){
41619         this.store.setSource(source);
41620         //this.autoSize();
41621     },
41622     /**
41623      * Gets all the data from the grid.
41624      * @return {Object} data  data stored in grid
41625      */
41626     getSource : function(){
41627         return this.store.getSource();
41628     }
41629 });/*
41630   
41631  * Licence LGPL
41632  
41633  */
41634  
41635 /**
41636  * @class Roo.grid.Calendar
41637  * @extends Roo.grid.Grid
41638  * This class extends the Grid to provide a calendar widget
41639  * <br><br>Usage:<pre><code>
41640  var grid = new Roo.grid.Calendar("my-container-id", {
41641      ds: myDataStore,
41642      cm: myColModel,
41643      selModel: mySelectionModel,
41644      autoSizeColumns: true,
41645      monitorWindowResize: false,
41646      trackMouseOver: true
41647      eventstore : real data store..
41648  });
41649  // set any options
41650  grid.render();
41651   
41652   * @constructor
41653  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41654  * The container MUST have some type of size defined for the grid to fill. The container will be
41655  * automatically set to position relative if it isn't already.
41656  * @param {Object} config A config object that sets properties on this grid.
41657  */
41658 Roo.grid.Calendar = function(container, config){
41659         // initialize the container
41660         this.container = Roo.get(container);
41661         this.container.update("");
41662         this.container.setStyle("overflow", "hidden");
41663     this.container.addClass('x-grid-container');
41664
41665     this.id = this.container.id;
41666
41667     Roo.apply(this, config);
41668     // check and correct shorthanded configs
41669     
41670     var rows = [];
41671     var d =1;
41672     for (var r = 0;r < 6;r++) {
41673         
41674         rows[r]=[];
41675         for (var c =0;c < 7;c++) {
41676             rows[r][c]= '';
41677         }
41678     }
41679     if (this.eventStore) {
41680         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41681         this.eventStore.on('load',this.onLoad, this);
41682         this.eventStore.on('beforeload',this.clearEvents, this);
41683          
41684     }
41685     
41686     this.dataSource = new Roo.data.Store({
41687             proxy: new Roo.data.MemoryProxy(rows),
41688             reader: new Roo.data.ArrayReader({}, [
41689                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41690     });
41691
41692     this.dataSource.load();
41693     this.ds = this.dataSource;
41694     this.ds.xmodule = this.xmodule || false;
41695     
41696     
41697     var cellRender = function(v,x,r)
41698     {
41699         return String.format(
41700             '<div class="fc-day  fc-widget-content"><div>' +
41701                 '<div class="fc-event-container"></div>' +
41702                 '<div class="fc-day-number">{0}</div>'+
41703                 
41704                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41705             '</div></div>', v);
41706     
41707     }
41708     
41709     
41710     this.colModel = new Roo.grid.ColumnModel( [
41711         {
41712             xtype: 'ColumnModel',
41713             xns: Roo.grid,
41714             dataIndex : 'weekday0',
41715             header : 'Sunday',
41716             renderer : cellRender
41717         },
41718         {
41719             xtype: 'ColumnModel',
41720             xns: Roo.grid,
41721             dataIndex : 'weekday1',
41722             header : 'Monday',
41723             renderer : cellRender
41724         },
41725         {
41726             xtype: 'ColumnModel',
41727             xns: Roo.grid,
41728             dataIndex : 'weekday2',
41729             header : 'Tuesday',
41730             renderer : cellRender
41731         },
41732         {
41733             xtype: 'ColumnModel',
41734             xns: Roo.grid,
41735             dataIndex : 'weekday3',
41736             header : 'Wednesday',
41737             renderer : cellRender
41738         },
41739         {
41740             xtype: 'ColumnModel',
41741             xns: Roo.grid,
41742             dataIndex : 'weekday4',
41743             header : 'Thursday',
41744             renderer : cellRender
41745         },
41746         {
41747             xtype: 'ColumnModel',
41748             xns: Roo.grid,
41749             dataIndex : 'weekday5',
41750             header : 'Friday',
41751             renderer : cellRender
41752         },
41753         {
41754             xtype: 'ColumnModel',
41755             xns: Roo.grid,
41756             dataIndex : 'weekday6',
41757             header : 'Saturday',
41758             renderer : cellRender
41759         }
41760     ]);
41761     this.cm = this.colModel;
41762     this.cm.xmodule = this.xmodule || false;
41763  
41764         
41765           
41766     //this.selModel = new Roo.grid.CellSelectionModel();
41767     //this.sm = this.selModel;
41768     //this.selModel.init(this);
41769     
41770     
41771     if(this.width){
41772         this.container.setWidth(this.width);
41773     }
41774
41775     if(this.height){
41776         this.container.setHeight(this.height);
41777     }
41778     /** @private */
41779         this.addEvents({
41780         // raw events
41781         /**
41782          * @event click
41783          * The raw click event for the entire grid.
41784          * @param {Roo.EventObject} e
41785          */
41786         "click" : true,
41787         /**
41788          * @event dblclick
41789          * The raw dblclick event for the entire grid.
41790          * @param {Roo.EventObject} e
41791          */
41792         "dblclick" : true,
41793         /**
41794          * @event contextmenu
41795          * The raw contextmenu event for the entire grid.
41796          * @param {Roo.EventObject} e
41797          */
41798         "contextmenu" : true,
41799         /**
41800          * @event mousedown
41801          * The raw mousedown event for the entire grid.
41802          * @param {Roo.EventObject} e
41803          */
41804         "mousedown" : true,
41805         /**
41806          * @event mouseup
41807          * The raw mouseup event for the entire grid.
41808          * @param {Roo.EventObject} e
41809          */
41810         "mouseup" : true,
41811         /**
41812          * @event mouseover
41813          * The raw mouseover event for the entire grid.
41814          * @param {Roo.EventObject} e
41815          */
41816         "mouseover" : true,
41817         /**
41818          * @event mouseout
41819          * The raw mouseout event for the entire grid.
41820          * @param {Roo.EventObject} e
41821          */
41822         "mouseout" : true,
41823         /**
41824          * @event keypress
41825          * The raw keypress event for the entire grid.
41826          * @param {Roo.EventObject} e
41827          */
41828         "keypress" : true,
41829         /**
41830          * @event keydown
41831          * The raw keydown event for the entire grid.
41832          * @param {Roo.EventObject} e
41833          */
41834         "keydown" : true,
41835
41836         // custom events
41837
41838         /**
41839          * @event cellclick
41840          * Fires when a cell is clicked
41841          * @param {Grid} this
41842          * @param {Number} rowIndex
41843          * @param {Number} columnIndex
41844          * @param {Roo.EventObject} e
41845          */
41846         "cellclick" : true,
41847         /**
41848          * @event celldblclick
41849          * Fires when a cell is double clicked
41850          * @param {Grid} this
41851          * @param {Number} rowIndex
41852          * @param {Number} columnIndex
41853          * @param {Roo.EventObject} e
41854          */
41855         "celldblclick" : true,
41856         /**
41857          * @event rowclick
41858          * Fires when a row is clicked
41859          * @param {Grid} this
41860          * @param {Number} rowIndex
41861          * @param {Roo.EventObject} e
41862          */
41863         "rowclick" : true,
41864         /**
41865          * @event rowdblclick
41866          * Fires when a row is double clicked
41867          * @param {Grid} this
41868          * @param {Number} rowIndex
41869          * @param {Roo.EventObject} e
41870          */
41871         "rowdblclick" : true,
41872         /**
41873          * @event headerclick
41874          * Fires when a header is clicked
41875          * @param {Grid} this
41876          * @param {Number} columnIndex
41877          * @param {Roo.EventObject} e
41878          */
41879         "headerclick" : true,
41880         /**
41881          * @event headerdblclick
41882          * Fires when a header cell is double clicked
41883          * @param {Grid} this
41884          * @param {Number} columnIndex
41885          * @param {Roo.EventObject} e
41886          */
41887         "headerdblclick" : true,
41888         /**
41889          * @event rowcontextmenu
41890          * Fires when a row is right clicked
41891          * @param {Grid} this
41892          * @param {Number} rowIndex
41893          * @param {Roo.EventObject} e
41894          */
41895         "rowcontextmenu" : true,
41896         /**
41897          * @event cellcontextmenu
41898          * Fires when a cell is right clicked
41899          * @param {Grid} this
41900          * @param {Number} rowIndex
41901          * @param {Number} cellIndex
41902          * @param {Roo.EventObject} e
41903          */
41904          "cellcontextmenu" : true,
41905         /**
41906          * @event headercontextmenu
41907          * Fires when a header is right clicked
41908          * @param {Grid} this
41909          * @param {Number} columnIndex
41910          * @param {Roo.EventObject} e
41911          */
41912         "headercontextmenu" : true,
41913         /**
41914          * @event bodyscroll
41915          * Fires when the body element is scrolled
41916          * @param {Number} scrollLeft
41917          * @param {Number} scrollTop
41918          */
41919         "bodyscroll" : true,
41920         /**
41921          * @event columnresize
41922          * Fires when the user resizes a column
41923          * @param {Number} columnIndex
41924          * @param {Number} newSize
41925          */
41926         "columnresize" : true,
41927         /**
41928          * @event columnmove
41929          * Fires when the user moves a column
41930          * @param {Number} oldIndex
41931          * @param {Number} newIndex
41932          */
41933         "columnmove" : true,
41934         /**
41935          * @event startdrag
41936          * Fires when row(s) start being dragged
41937          * @param {Grid} this
41938          * @param {Roo.GridDD} dd The drag drop object
41939          * @param {event} e The raw browser event
41940          */
41941         "startdrag" : true,
41942         /**
41943          * @event enddrag
41944          * Fires when a drag operation is complete
41945          * @param {Grid} this
41946          * @param {Roo.GridDD} dd The drag drop object
41947          * @param {event} e The raw browser event
41948          */
41949         "enddrag" : true,
41950         /**
41951          * @event dragdrop
41952          * Fires when dragged row(s) are dropped on a valid DD target
41953          * @param {Grid} this
41954          * @param {Roo.GridDD} dd The drag drop object
41955          * @param {String} targetId The target drag drop object
41956          * @param {event} e The raw browser event
41957          */
41958         "dragdrop" : true,
41959         /**
41960          * @event dragover
41961          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41962          * @param {Grid} this
41963          * @param {Roo.GridDD} dd The drag drop object
41964          * @param {String} targetId The target drag drop object
41965          * @param {event} e The raw browser event
41966          */
41967         "dragover" : true,
41968         /**
41969          * @event dragenter
41970          *  Fires when the dragged row(s) first cross another DD target while being dragged
41971          * @param {Grid} this
41972          * @param {Roo.GridDD} dd The drag drop object
41973          * @param {String} targetId The target drag drop object
41974          * @param {event} e The raw browser event
41975          */
41976         "dragenter" : true,
41977         /**
41978          * @event dragout
41979          * Fires when the dragged row(s) leave another DD target while being dragged
41980          * @param {Grid} this
41981          * @param {Roo.GridDD} dd The drag drop object
41982          * @param {String} targetId The target drag drop object
41983          * @param {event} e The raw browser event
41984          */
41985         "dragout" : true,
41986         /**
41987          * @event rowclass
41988          * Fires when a row is rendered, so you can change add a style to it.
41989          * @param {GridView} gridview   The grid view
41990          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41991          */
41992         'rowclass' : true,
41993
41994         /**
41995          * @event render
41996          * Fires when the grid is rendered
41997          * @param {Grid} grid
41998          */
41999         'render' : true,
42000             /**
42001              * @event select
42002              * Fires when a date is selected
42003              * @param {DatePicker} this
42004              * @param {Date} date The selected date
42005              */
42006         'select': true,
42007         /**
42008              * @event monthchange
42009              * Fires when the displayed month changes 
42010              * @param {DatePicker} this
42011              * @param {Date} date The selected month
42012              */
42013         'monthchange': true,
42014         /**
42015              * @event evententer
42016              * Fires when mouse over an event
42017              * @param {Calendar} this
42018              * @param {event} Event
42019              */
42020         'evententer': true,
42021         /**
42022              * @event eventleave
42023              * Fires when the mouse leaves an
42024              * @param {Calendar} this
42025              * @param {event}
42026              */
42027         'eventleave': true,
42028         /**
42029              * @event eventclick
42030              * Fires when the mouse click an
42031              * @param {Calendar} this
42032              * @param {event}
42033              */
42034         'eventclick': true,
42035         /**
42036              * @event eventrender
42037              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42038              * @param {Calendar} this
42039              * @param {data} data to be modified
42040              */
42041         'eventrender': true
42042         
42043     });
42044
42045     Roo.grid.Grid.superclass.constructor.call(this);
42046     this.on('render', function() {
42047         this.view.el.addClass('x-grid-cal'); 
42048         
42049         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42050
42051     },this);
42052     
42053     if (!Roo.grid.Calendar.style) {
42054         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42055             
42056             
42057             '.x-grid-cal .x-grid-col' :  {
42058                 height: 'auto !important',
42059                 'vertical-align': 'top'
42060             },
42061             '.x-grid-cal  .fc-event-hori' : {
42062                 height: '14px'
42063             }
42064              
42065             
42066         }, Roo.id());
42067     }
42068
42069     
42070     
42071 };
42072 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42073     /**
42074      * @cfg {Store} eventStore The store that loads events.
42075      */
42076     eventStore : 25,
42077
42078      
42079     activeDate : false,
42080     startDay : 0,
42081     autoWidth : true,
42082     monitorWindowResize : false,
42083
42084     
42085     resizeColumns : function() {
42086         var col = (this.view.el.getWidth() / 7) - 3;
42087         // loop through cols, and setWidth
42088         for(var i =0 ; i < 7 ; i++){
42089             this.cm.setColumnWidth(i, col);
42090         }
42091     },
42092      setDate :function(date) {
42093         
42094         Roo.log('setDate?');
42095         
42096         this.resizeColumns();
42097         var vd = this.activeDate;
42098         this.activeDate = date;
42099 //        if(vd && this.el){
42100 //            var t = date.getTime();
42101 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42102 //                Roo.log('using add remove');
42103 //                
42104 //                this.fireEvent('monthchange', this, date);
42105 //                
42106 //                this.cells.removeClass("fc-state-highlight");
42107 //                this.cells.each(function(c){
42108 //                   if(c.dateValue == t){
42109 //                       c.addClass("fc-state-highlight");
42110 //                       setTimeout(function(){
42111 //                            try{c.dom.firstChild.focus();}catch(e){}
42112 //                       }, 50);
42113 //                       return false;
42114 //                   }
42115 //                   return true;
42116 //                });
42117 //                return;
42118 //            }
42119 //        }
42120         
42121         var days = date.getDaysInMonth();
42122         
42123         var firstOfMonth = date.getFirstDateOfMonth();
42124         var startingPos = firstOfMonth.getDay()-this.startDay;
42125         
42126         if(startingPos < this.startDay){
42127             startingPos += 7;
42128         }
42129         
42130         var pm = date.add(Date.MONTH, -1);
42131         var prevStart = pm.getDaysInMonth()-startingPos;
42132 //        
42133         
42134         
42135         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42136         
42137         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42138         //this.cells.addClassOnOver('fc-state-hover');
42139         
42140         var cells = this.cells.elements;
42141         var textEls = this.textNodes;
42142         
42143         //Roo.each(cells, function(cell){
42144         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42145         //});
42146         
42147         days += startingPos;
42148
42149         // convert everything to numbers so it's fast
42150         var day = 86400000;
42151         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42152         //Roo.log(d);
42153         //Roo.log(pm);
42154         //Roo.log(prevStart);
42155         
42156         var today = new Date().clearTime().getTime();
42157         var sel = date.clearTime().getTime();
42158         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42159         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42160         var ddMatch = this.disabledDatesRE;
42161         var ddText = this.disabledDatesText;
42162         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42163         var ddaysText = this.disabledDaysText;
42164         var format = this.format;
42165         
42166         var setCellClass = function(cal, cell){
42167             
42168             //Roo.log('set Cell Class');
42169             cell.title = "";
42170             var t = d.getTime();
42171             
42172             //Roo.log(d);
42173             
42174             
42175             cell.dateValue = t;
42176             if(t == today){
42177                 cell.className += " fc-today";
42178                 cell.className += " fc-state-highlight";
42179                 cell.title = cal.todayText;
42180             }
42181             if(t == sel){
42182                 // disable highlight in other month..
42183                 cell.className += " fc-state-highlight";
42184                 
42185             }
42186             // disabling
42187             if(t < min) {
42188                 //cell.className = " fc-state-disabled";
42189                 cell.title = cal.minText;
42190                 return;
42191             }
42192             if(t > max) {
42193                 //cell.className = " fc-state-disabled";
42194                 cell.title = cal.maxText;
42195                 return;
42196             }
42197             if(ddays){
42198                 if(ddays.indexOf(d.getDay()) != -1){
42199                     // cell.title = ddaysText;
42200                    // cell.className = " fc-state-disabled";
42201                 }
42202             }
42203             if(ddMatch && format){
42204                 var fvalue = d.dateFormat(format);
42205                 if(ddMatch.test(fvalue)){
42206                     cell.title = ddText.replace("%0", fvalue);
42207                    cell.className = " fc-state-disabled";
42208                 }
42209             }
42210             
42211             if (!cell.initialClassName) {
42212                 cell.initialClassName = cell.dom.className;
42213             }
42214             
42215             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42216         };
42217
42218         var i = 0;
42219         
42220         for(; i < startingPos; i++) {
42221             cells[i].dayName =  (++prevStart);
42222             Roo.log(textEls[i]);
42223             d.setDate(d.getDate()+1);
42224             
42225             //cells[i].className = "fc-past fc-other-month";
42226             setCellClass(this, cells[i]);
42227         }
42228         
42229         var intDay = 0;
42230         
42231         for(; i < days; i++){
42232             intDay = i - startingPos + 1;
42233             cells[i].dayName =  (intDay);
42234             d.setDate(d.getDate()+1);
42235             
42236             cells[i].className = ''; // "x-date-active";
42237             setCellClass(this, cells[i]);
42238         }
42239         var extraDays = 0;
42240         
42241         for(; i < 42; i++) {
42242             //textEls[i].innerHTML = (++extraDays);
42243             
42244             d.setDate(d.getDate()+1);
42245             cells[i].dayName = (++extraDays);
42246             cells[i].className = "fc-future fc-other-month";
42247             setCellClass(this, cells[i]);
42248         }
42249         
42250         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42251         
42252         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42253         
42254         // this will cause all the cells to mis
42255         var rows= [];
42256         var i =0;
42257         for (var r = 0;r < 6;r++) {
42258             for (var c =0;c < 7;c++) {
42259                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42260             }    
42261         }
42262         
42263         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42264         for(i=0;i<cells.length;i++) {
42265             
42266             this.cells.elements[i].dayName = cells[i].dayName ;
42267             this.cells.elements[i].className = cells[i].className;
42268             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42269             this.cells.elements[i].title = cells[i].title ;
42270             this.cells.elements[i].dateValue = cells[i].dateValue ;
42271         }
42272         
42273         
42274         
42275         
42276         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42277         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42278         
42279         ////if(totalRows != 6){
42280             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42281            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42282        // }
42283         
42284         this.fireEvent('monthchange', this, date);
42285         
42286         
42287     },
42288  /**
42289      * Returns the grid's SelectionModel.
42290      * @return {SelectionModel}
42291      */
42292     getSelectionModel : function(){
42293         if(!this.selModel){
42294             this.selModel = new Roo.grid.CellSelectionModel();
42295         }
42296         return this.selModel;
42297     },
42298
42299     load: function() {
42300         this.eventStore.load()
42301         
42302         
42303         
42304     },
42305     
42306     findCell : function(dt) {
42307         dt = dt.clearTime().getTime();
42308         var ret = false;
42309         this.cells.each(function(c){
42310             //Roo.log("check " +c.dateValue + '?=' + dt);
42311             if(c.dateValue == dt){
42312                 ret = c;
42313                 return false;
42314             }
42315             return true;
42316         });
42317         
42318         return ret;
42319     },
42320     
42321     findCells : function(rec) {
42322         var s = rec.data.start_dt.clone().clearTime().getTime();
42323        // Roo.log(s);
42324         var e= rec.data.end_dt.clone().clearTime().getTime();
42325        // Roo.log(e);
42326         var ret = [];
42327         this.cells.each(function(c){
42328              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42329             
42330             if(c.dateValue > e){
42331                 return ;
42332             }
42333             if(c.dateValue < s){
42334                 return ;
42335             }
42336             ret.push(c);
42337         });
42338         
42339         return ret;    
42340     },
42341     
42342     findBestRow: function(cells)
42343     {
42344         var ret = 0;
42345         
42346         for (var i =0 ; i < cells.length;i++) {
42347             ret  = Math.max(cells[i].rows || 0,ret);
42348         }
42349         return ret;
42350         
42351     },
42352     
42353     
42354     addItem : function(rec)
42355     {
42356         // look for vertical location slot in
42357         var cells = this.findCells(rec);
42358         
42359         rec.row = this.findBestRow(cells);
42360         
42361         // work out the location.
42362         
42363         var crow = false;
42364         var rows = [];
42365         for(var i =0; i < cells.length; i++) {
42366             if (!crow) {
42367                 crow = {
42368                     start : cells[i],
42369                     end :  cells[i]
42370                 };
42371                 continue;
42372             }
42373             if (crow.start.getY() == cells[i].getY()) {
42374                 // on same row.
42375                 crow.end = cells[i];
42376                 continue;
42377             }
42378             // different row.
42379             rows.push(crow);
42380             crow = {
42381                 start: cells[i],
42382                 end : cells[i]
42383             };
42384             
42385         }
42386         
42387         rows.push(crow);
42388         rec.els = [];
42389         rec.rows = rows;
42390         rec.cells = cells;
42391         for (var i = 0; i < cells.length;i++) {
42392             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42393             
42394         }
42395         
42396         
42397     },
42398     
42399     clearEvents: function() {
42400         
42401         if (!this.eventStore.getCount()) {
42402             return;
42403         }
42404         // reset number of rows in cells.
42405         Roo.each(this.cells.elements, function(c){
42406             c.rows = 0;
42407         });
42408         
42409         this.eventStore.each(function(e) {
42410             this.clearEvent(e);
42411         },this);
42412         
42413     },
42414     
42415     clearEvent : function(ev)
42416     {
42417         if (ev.els) {
42418             Roo.each(ev.els, function(el) {
42419                 el.un('mouseenter' ,this.onEventEnter, this);
42420                 el.un('mouseleave' ,this.onEventLeave, this);
42421                 el.remove();
42422             },this);
42423             ev.els = [];
42424         }
42425     },
42426     
42427     
42428     renderEvent : function(ev,ctr) {
42429         if (!ctr) {
42430              ctr = this.view.el.select('.fc-event-container',true).first();
42431         }
42432         
42433          
42434         this.clearEvent(ev);
42435             //code
42436        
42437         
42438         
42439         ev.els = [];
42440         var cells = ev.cells;
42441         var rows = ev.rows;
42442         this.fireEvent('eventrender', this, ev);
42443         
42444         for(var i =0; i < rows.length; i++) {
42445             
42446             cls = '';
42447             if (i == 0) {
42448                 cls += ' fc-event-start';
42449             }
42450             if ((i+1) == rows.length) {
42451                 cls += ' fc-event-end';
42452             }
42453             
42454             //Roo.log(ev.data);
42455             // how many rows should it span..
42456             var cg = this.eventTmpl.append(ctr,Roo.apply({
42457                 fccls : cls
42458                 
42459             }, ev.data) , true);
42460             
42461             
42462             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42463             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42464             cg.on('click', this.onEventClick, this, ev);
42465             
42466             ev.els.push(cg);
42467             
42468             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42469             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42470             //Roo.log(cg);
42471              
42472             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42473             cg.setWidth(ebox.right - sbox.x -2);
42474         }
42475     },
42476     
42477     renderEvents: function()
42478     {   
42479         // first make sure there is enough space..
42480         
42481         if (!this.eventTmpl) {
42482             this.eventTmpl = new Roo.Template(
42483                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42484                     '<div class="fc-event-inner">' +
42485                         '<span class="fc-event-time">{time}</span>' +
42486                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42487                     '</div>' +
42488                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42489                 '</div>'
42490             );
42491                 
42492         }
42493                
42494         
42495         
42496         this.cells.each(function(c) {
42497             //Roo.log(c.select('.fc-day-content div',true).first());
42498             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42499         });
42500         
42501         var ctr = this.view.el.select('.fc-event-container',true).first();
42502         
42503         var cls;
42504         this.eventStore.each(function(ev){
42505             
42506             this.renderEvent(ev);
42507              
42508              
42509         }, this);
42510         this.view.layout();
42511         
42512     },
42513     
42514     onEventEnter: function (e, el,event,d) {
42515         this.fireEvent('evententer', this, el, event);
42516     },
42517     
42518     onEventLeave: function (e, el,event,d) {
42519         this.fireEvent('eventleave', this, el, event);
42520     },
42521     
42522     onEventClick: function (e, el,event,d) {
42523         this.fireEvent('eventclick', this, el, event);
42524     },
42525     
42526     onMonthChange: function () {
42527         this.store.load();
42528     },
42529     
42530     onLoad: function () {
42531         
42532         //Roo.log('calendar onload');
42533 //         
42534         if(this.eventStore.getCount() > 0){
42535             
42536            
42537             
42538             this.eventStore.each(function(d){
42539                 
42540                 
42541                 // FIXME..
42542                 var add =   d.data;
42543                 if (typeof(add.end_dt) == 'undefined')  {
42544                     Roo.log("Missing End time in calendar data: ");
42545                     Roo.log(d);
42546                     return;
42547                 }
42548                 if (typeof(add.start_dt) == 'undefined')  {
42549                     Roo.log("Missing Start time in calendar data: ");
42550                     Roo.log(d);
42551                     return;
42552                 }
42553                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42554                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42555                 add.id = add.id || d.id;
42556                 add.title = add.title || '??';
42557                 
42558                 this.addItem(d);
42559                 
42560              
42561             },this);
42562         }
42563         
42564         this.renderEvents();
42565     }
42566     
42567
42568 });
42569 /*
42570  grid : {
42571                 xtype: 'Grid',
42572                 xns: Roo.grid,
42573                 listeners : {
42574                     render : function ()
42575                     {
42576                         _this.grid = this;
42577                         
42578                         if (!this.view.el.hasClass('course-timesheet')) {
42579                             this.view.el.addClass('course-timesheet');
42580                         }
42581                         if (this.tsStyle) {
42582                             this.ds.load({});
42583                             return; 
42584                         }
42585                         Roo.log('width');
42586                         Roo.log(_this.grid.view.el.getWidth());
42587                         
42588                         
42589                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42590                             '.course-timesheet .x-grid-row' : {
42591                                 height: '80px'
42592                             },
42593                             '.x-grid-row td' : {
42594                                 'vertical-align' : 0
42595                             },
42596                             '.course-edit-link' : {
42597                                 'color' : 'blue',
42598                                 'text-overflow' : 'ellipsis',
42599                                 'overflow' : 'hidden',
42600                                 'white-space' : 'nowrap',
42601                                 'cursor' : 'pointer'
42602                             },
42603                             '.sub-link' : {
42604                                 'color' : 'green'
42605                             },
42606                             '.de-act-sup-link' : {
42607                                 'color' : 'purple',
42608                                 'text-decoration' : 'line-through'
42609                             },
42610                             '.de-act-link' : {
42611                                 'color' : 'red',
42612                                 'text-decoration' : 'line-through'
42613                             },
42614                             '.course-timesheet .course-highlight' : {
42615                                 'border-top-style': 'dashed !important',
42616                                 'border-bottom-bottom': 'dashed !important'
42617                             },
42618                             '.course-timesheet .course-item' : {
42619                                 'font-family'   : 'tahoma, arial, helvetica',
42620                                 'font-size'     : '11px',
42621                                 'overflow'      : 'hidden',
42622                                 'padding-left'  : '10px',
42623                                 'padding-right' : '10px',
42624                                 'padding-top' : '10px' 
42625                             }
42626                             
42627                         }, Roo.id());
42628                                 this.ds.load({});
42629                     }
42630                 },
42631                 autoWidth : true,
42632                 monitorWindowResize : false,
42633                 cellrenderer : function(v,x,r)
42634                 {
42635                     return v;
42636                 },
42637                 sm : {
42638                     xtype: 'CellSelectionModel',
42639                     xns: Roo.grid
42640                 },
42641                 dataSource : {
42642                     xtype: 'Store',
42643                     xns: Roo.data,
42644                     listeners : {
42645                         beforeload : function (_self, options)
42646                         {
42647                             options.params = options.params || {};
42648                             options.params._month = _this.monthField.getValue();
42649                             options.params.limit = 9999;
42650                             options.params['sort'] = 'when_dt';    
42651                             options.params['dir'] = 'ASC';    
42652                             this.proxy.loadResponse = this.loadResponse;
42653                             Roo.log("load?");
42654                             //this.addColumns();
42655                         },
42656                         load : function (_self, records, options)
42657                         {
42658                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42659                                 // if you click on the translation.. you can edit it...
42660                                 var el = Roo.get(this);
42661                                 var id = el.dom.getAttribute('data-id');
42662                                 var d = el.dom.getAttribute('data-date');
42663                                 var t = el.dom.getAttribute('data-time');
42664                                 //var id = this.child('span').dom.textContent;
42665                                 
42666                                 //Roo.log(this);
42667                                 Pman.Dialog.CourseCalendar.show({
42668                                     id : id,
42669                                     when_d : d,
42670                                     when_t : t,
42671                                     productitem_active : id ? 1 : 0
42672                                 }, function() {
42673                                     _this.grid.ds.load({});
42674                                 });
42675                            
42676                            });
42677                            
42678                            _this.panel.fireEvent('resize', [ '', '' ]);
42679                         }
42680                     },
42681                     loadResponse : function(o, success, response){
42682                             // this is overridden on before load..
42683                             
42684                             Roo.log("our code?");       
42685                             //Roo.log(success);
42686                             //Roo.log(response)
42687                             delete this.activeRequest;
42688                             if(!success){
42689                                 this.fireEvent("loadexception", this, o, response);
42690                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42691                                 return;
42692                             }
42693                             var result;
42694                             try {
42695                                 result = o.reader.read(response);
42696                             }catch(e){
42697                                 Roo.log("load exception?");
42698                                 this.fireEvent("loadexception", this, o, response, e);
42699                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42700                                 return;
42701                             }
42702                             Roo.log("ready...");        
42703                             // loop through result.records;
42704                             // and set this.tdate[date] = [] << array of records..
42705                             _this.tdata  = {};
42706                             Roo.each(result.records, function(r){
42707                                 //Roo.log(r.data);
42708                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42709                                     _this.tdata[r.data.when_dt.format('j')] = [];
42710                                 }
42711                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42712                             });
42713                             
42714                             //Roo.log(_this.tdata);
42715                             
42716                             result.records = [];
42717                             result.totalRecords = 6;
42718                     
42719                             // let's generate some duumy records for the rows.
42720                             //var st = _this.dateField.getValue();
42721                             
42722                             // work out monday..
42723                             //st = st.add(Date.DAY, -1 * st.format('w'));
42724                             
42725                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42726                             
42727                             var firstOfMonth = date.getFirstDayOfMonth();
42728                             var days = date.getDaysInMonth();
42729                             var d = 1;
42730                             var firstAdded = false;
42731                             for (var i = 0; i < result.totalRecords ; i++) {
42732                                 //var d= st.add(Date.DAY, i);
42733                                 var row = {};
42734                                 var added = 0;
42735                                 for(var w = 0 ; w < 7 ; w++){
42736                                     if(!firstAdded && firstOfMonth != w){
42737                                         continue;
42738                                     }
42739                                     if(d > days){
42740                                         continue;
42741                                     }
42742                                     firstAdded = true;
42743                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42744                                     row['weekday'+w] = String.format(
42745                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42746                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42747                                                     d,
42748                                                     date.format('Y-m-')+dd
42749                                                 );
42750                                     added++;
42751                                     if(typeof(_this.tdata[d]) != 'undefined'){
42752                                         Roo.each(_this.tdata[d], function(r){
42753                                             var is_sub = '';
42754                                             var deactive = '';
42755                                             var id = r.id;
42756                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42757                                             if(r.parent_id*1>0){
42758                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42759                                                 id = r.parent_id;
42760                                             }
42761                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42762                                                 deactive = 'de-act-link';
42763                                             }
42764                                             
42765                                             row['weekday'+w] += String.format(
42766                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42767                                                     id, //0
42768                                                     r.product_id_name, //1
42769                                                     r.when_dt.format('h:ia'), //2
42770                                                     is_sub, //3
42771                                                     deactive, //4
42772                                                     desc // 5
42773                                             );
42774                                         });
42775                                     }
42776                                     d++;
42777                                 }
42778                                 
42779                                 // only do this if something added..
42780                                 if(added > 0){ 
42781                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42782                                 }
42783                                 
42784                                 
42785                                 // push it twice. (second one with an hour..
42786                                 
42787                             }
42788                             //Roo.log(result);
42789                             this.fireEvent("load", this, o, o.request.arg);
42790                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42791                         },
42792                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42793                     proxy : {
42794                         xtype: 'HttpProxy',
42795                         xns: Roo.data,
42796                         method : 'GET',
42797                         url : baseURL + '/Roo/Shop_course.php'
42798                     },
42799                     reader : {
42800                         xtype: 'JsonReader',
42801                         xns: Roo.data,
42802                         id : 'id',
42803                         fields : [
42804                             {
42805                                 'name': 'id',
42806                                 'type': 'int'
42807                             },
42808                             {
42809                                 'name': 'when_dt',
42810                                 'type': 'string'
42811                             },
42812                             {
42813                                 'name': 'end_dt',
42814                                 'type': 'string'
42815                             },
42816                             {
42817                                 'name': 'parent_id',
42818                                 'type': 'int'
42819                             },
42820                             {
42821                                 'name': 'product_id',
42822                                 'type': 'int'
42823                             },
42824                             {
42825                                 'name': 'productitem_id',
42826                                 'type': 'int'
42827                             },
42828                             {
42829                                 'name': 'guid',
42830                                 'type': 'int'
42831                             }
42832                         ]
42833                     }
42834                 },
42835                 toolbar : {
42836                     xtype: 'Toolbar',
42837                     xns: Roo,
42838                     items : [
42839                         {
42840                             xtype: 'Button',
42841                             xns: Roo.Toolbar,
42842                             listeners : {
42843                                 click : function (_self, e)
42844                                 {
42845                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42846                                     sd.setMonth(sd.getMonth()-1);
42847                                     _this.monthField.setValue(sd.format('Y-m-d'));
42848                                     _this.grid.ds.load({});
42849                                 }
42850                             },
42851                             text : "Back"
42852                         },
42853                         {
42854                             xtype: 'Separator',
42855                             xns: Roo.Toolbar
42856                         },
42857                         {
42858                             xtype: 'MonthField',
42859                             xns: Roo.form,
42860                             listeners : {
42861                                 render : function (_self)
42862                                 {
42863                                     _this.monthField = _self;
42864                                    // _this.monthField.set  today
42865                                 },
42866                                 select : function (combo, date)
42867                                 {
42868                                     _this.grid.ds.load({});
42869                                 }
42870                             },
42871                             value : (function() { return new Date(); })()
42872                         },
42873                         {
42874                             xtype: 'Separator',
42875                             xns: Roo.Toolbar
42876                         },
42877                         {
42878                             xtype: 'TextItem',
42879                             xns: Roo.Toolbar,
42880                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42881                         },
42882                         {
42883                             xtype: 'Fill',
42884                             xns: Roo.Toolbar
42885                         },
42886                         {
42887                             xtype: 'Button',
42888                             xns: Roo.Toolbar,
42889                             listeners : {
42890                                 click : function (_self, e)
42891                                 {
42892                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42893                                     sd.setMonth(sd.getMonth()+1);
42894                                     _this.monthField.setValue(sd.format('Y-m-d'));
42895                                     _this.grid.ds.load({});
42896                                 }
42897                             },
42898                             text : "Next"
42899                         }
42900                     ]
42901                 },
42902                  
42903             }
42904         };
42905         
42906         *//*
42907  * Based on:
42908  * Ext JS Library 1.1.1
42909  * Copyright(c) 2006-2007, Ext JS, LLC.
42910  *
42911  * Originally Released Under LGPL - original licence link has changed is not relivant.
42912  *
42913  * Fork - LGPL
42914  * <script type="text/javascript">
42915  */
42916  
42917 /**
42918  * @class Roo.LoadMask
42919  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42920  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42921  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42922  * element's UpdateManager load indicator and will be destroyed after the initial load.
42923  * @constructor
42924  * Create a new LoadMask
42925  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42926  * @param {Object} config The config object
42927  */
42928 Roo.LoadMask = function(el, config){
42929     this.el = Roo.get(el);
42930     Roo.apply(this, config);
42931     if(this.store){
42932         this.store.on('beforeload', this.onBeforeLoad, this);
42933         this.store.on('load', this.onLoad, this);
42934         this.store.on('loadexception', this.onLoadException, this);
42935         this.removeMask = false;
42936     }else{
42937         var um = this.el.getUpdateManager();
42938         um.showLoadIndicator = false; // disable the default indicator
42939         um.on('beforeupdate', this.onBeforeLoad, this);
42940         um.on('update', this.onLoad, this);
42941         um.on('failure', this.onLoad, this);
42942         this.removeMask = true;
42943     }
42944 };
42945
42946 Roo.LoadMask.prototype = {
42947     /**
42948      * @cfg {Boolean} removeMask
42949      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42950      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42951      */
42952     removeMask : false,
42953     /**
42954      * @cfg {String} msg
42955      * The text to display in a centered loading message box (defaults to 'Loading...')
42956      */
42957     msg : 'Loading...',
42958     /**
42959      * @cfg {String} msgCls
42960      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42961      */
42962     msgCls : 'x-mask-loading',
42963
42964     /**
42965      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42966      * @type Boolean
42967      */
42968     disabled: false,
42969
42970     /**
42971      * Disables the mask to prevent it from being displayed
42972      */
42973     disable : function(){
42974        this.disabled = true;
42975     },
42976
42977     /**
42978      * Enables the mask so that it can be displayed
42979      */
42980     enable : function(){
42981         this.disabled = false;
42982     },
42983     
42984     onLoadException : function()
42985     {
42986         Roo.log(arguments);
42987         
42988         if (typeof(arguments[3]) != 'undefined') {
42989             Roo.MessageBox.alert("Error loading",arguments[3]);
42990         } 
42991         /*
42992         try {
42993             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42994                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42995             }   
42996         } catch(e) {
42997             
42998         }
42999         */
43000     
43001         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43002     },
43003     // private
43004     onLoad : function()
43005     {
43006         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43007     },
43008
43009     // private
43010     onBeforeLoad : function(){
43011         if(!this.disabled){
43012             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43013         }
43014     },
43015
43016     // private
43017     destroy : function(){
43018         if(this.store){
43019             this.store.un('beforeload', this.onBeforeLoad, this);
43020             this.store.un('load', this.onLoad, this);
43021             this.store.un('loadexception', this.onLoadException, this);
43022         }else{
43023             var um = this.el.getUpdateManager();
43024             um.un('beforeupdate', this.onBeforeLoad, this);
43025             um.un('update', this.onLoad, this);
43026             um.un('failure', this.onLoad, this);
43027         }
43028     }
43029 };/*
43030  * Based on:
43031  * Ext JS Library 1.1.1
43032  * Copyright(c) 2006-2007, Ext JS, LLC.
43033  *
43034  * Originally Released Under LGPL - original licence link has changed is not relivant.
43035  *
43036  * Fork - LGPL
43037  * <script type="text/javascript">
43038  */
43039
43040
43041 /**
43042  * @class Roo.XTemplate
43043  * @extends Roo.Template
43044  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43045 <pre><code>
43046 var t = new Roo.XTemplate(
43047         '&lt;select name="{name}"&gt;',
43048                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43049         '&lt;/select&gt;'
43050 );
43051  
43052 // then append, applying the master template values
43053  </code></pre>
43054  *
43055  * Supported features:
43056  *
43057  *  Tags:
43058
43059 <pre><code>
43060       {a_variable} - output encoded.
43061       {a_variable.format:("Y-m-d")} - call a method on the variable
43062       {a_variable:raw} - unencoded output
43063       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43064       {a_variable:this.method_on_template(...)} - call a method on the template object.
43065  
43066 </code></pre>
43067  *  The tpl tag:
43068 <pre><code>
43069         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43070         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43071         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43072         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43073   
43074         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43075         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43076 </code></pre>
43077  *      
43078  */
43079 Roo.XTemplate = function()
43080 {
43081     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43082     if (this.html) {
43083         this.compile();
43084     }
43085 };
43086
43087
43088 Roo.extend(Roo.XTemplate, Roo.Template, {
43089
43090     /**
43091      * The various sub templates
43092      */
43093     tpls : false,
43094     /**
43095      *
43096      * basic tag replacing syntax
43097      * WORD:WORD()
43098      *
43099      * // you can fake an object call by doing this
43100      *  x.t:(test,tesT) 
43101      * 
43102      */
43103     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43104
43105     /**
43106      * compile the template
43107      *
43108      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43109      *
43110      */
43111     compile: function()
43112     {
43113         var s = this.html;
43114      
43115         s = ['<tpl>', s, '</tpl>'].join('');
43116     
43117         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43118             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43119             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43120             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43121             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43122             m,
43123             id     = 0,
43124             tpls   = [];
43125     
43126         while(true == !!(m = s.match(re))){
43127             var forMatch   = m[0].match(nameRe),
43128                 ifMatch   = m[0].match(ifRe),
43129                 execMatch   = m[0].match(execRe),
43130                 namedMatch   = m[0].match(namedRe),
43131                 
43132                 exp  = null, 
43133                 fn   = null,
43134                 exec = null,
43135                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43136                 
43137             if (ifMatch) {
43138                 // if - puts fn into test..
43139                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43140                 if(exp){
43141                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43142                 }
43143             }
43144             
43145             if (execMatch) {
43146                 // exec - calls a function... returns empty if true is  returned.
43147                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43148                 if(exp){
43149                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43150                 }
43151             }
43152             
43153             
43154             if (name) {
43155                 // for = 
43156                 switch(name){
43157                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43158                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43159                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43160                 }
43161             }
43162             var uid = namedMatch ? namedMatch[1] : id;
43163             
43164             
43165             tpls.push({
43166                 id:     namedMatch ? namedMatch[1] : id,
43167                 target: name,
43168                 exec:   exec,
43169                 test:   fn,
43170                 body:   m[1] || ''
43171             });
43172             if (namedMatch) {
43173                 s = s.replace(m[0], '');
43174             } else { 
43175                 s = s.replace(m[0], '{xtpl'+ id + '}');
43176             }
43177             ++id;
43178         }
43179         this.tpls = [];
43180         for(var i = tpls.length-1; i >= 0; --i){
43181             this.compileTpl(tpls[i]);
43182             this.tpls[tpls[i].id] = tpls[i];
43183         }
43184         this.master = tpls[tpls.length-1];
43185         return this;
43186     },
43187     /**
43188      * same as applyTemplate, except it's done to one of the subTemplates
43189      * when using named templates, you can do:
43190      *
43191      * var str = pl.applySubTemplate('your-name', values);
43192      *
43193      * 
43194      * @param {Number} id of the template
43195      * @param {Object} values to apply to template
43196      * @param {Object} parent (normaly the instance of this object)
43197      */
43198     applySubTemplate : function(id, values, parent)
43199     {
43200         
43201         
43202         var t = this.tpls[id];
43203         
43204         
43205         try { 
43206             if(t.test && !t.test.call(this, values, parent)){
43207                 return '';
43208             }
43209         } catch(e) {
43210             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43211             Roo.log(e.toString());
43212             Roo.log(t.test);
43213             return ''
43214         }
43215         try { 
43216             
43217             if(t.exec && t.exec.call(this, values, parent)){
43218                 return '';
43219             }
43220         } catch(e) {
43221             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43222             Roo.log(e.toString());
43223             Roo.log(t.exec);
43224             return ''
43225         }
43226         try {
43227             var vs = t.target ? t.target.call(this, values, parent) : values;
43228             parent = t.target ? values : parent;
43229             if(t.target && vs instanceof Array){
43230                 var buf = [];
43231                 for(var i = 0, len = vs.length; i < len; i++){
43232                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43233                 }
43234                 return buf.join('');
43235             }
43236             return t.compiled.call(this, vs, parent);
43237         } catch (e) {
43238             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43239             Roo.log(e.toString());
43240             Roo.log(t.compiled);
43241             return '';
43242         }
43243     },
43244
43245     compileTpl : function(tpl)
43246     {
43247         var fm = Roo.util.Format;
43248         var useF = this.disableFormats !== true;
43249         var sep = Roo.isGecko ? "+" : ",";
43250         var undef = function(str) {
43251             Roo.log("Property not found :"  + str);
43252             return '';
43253         };
43254         
43255         var fn = function(m, name, format, args)
43256         {
43257             //Roo.log(arguments);
43258             args = args ? args.replace(/\\'/g,"'") : args;
43259             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43260             if (typeof(format) == 'undefined') {
43261                 format= 'htmlEncode';
43262             }
43263             if (format == 'raw' ) {
43264                 format = false;
43265             }
43266             
43267             if(name.substr(0, 4) == 'xtpl'){
43268                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43269             }
43270             
43271             // build an array of options to determine if value is undefined..
43272             
43273             // basically get 'xxxx.yyyy' then do
43274             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43275             //    (function () { Roo.log("Property not found"); return ''; })() :
43276             //    ......
43277             
43278             var udef_ar = [];
43279             var lookfor = '';
43280             Roo.each(name.split('.'), function(st) {
43281                 lookfor += (lookfor.length ? '.': '') + st;
43282                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43283             });
43284             
43285             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43286             
43287             
43288             if(format && useF){
43289                 
43290                 args = args ? ',' + args : "";
43291                  
43292                 if(format.substr(0, 5) != "this."){
43293                     format = "fm." + format + '(';
43294                 }else{
43295                     format = 'this.call("'+ format.substr(5) + '", ';
43296                     args = ", values";
43297                 }
43298                 
43299                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43300             }
43301              
43302             if (args.length) {
43303                 // called with xxyx.yuu:(test,test)
43304                 // change to ()
43305                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43306             }
43307             // raw.. - :raw modifier..
43308             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43309             
43310         };
43311         var body;
43312         // branched to use + in gecko and [].join() in others
43313         if(Roo.isGecko){
43314             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43315                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43316                     "';};};";
43317         }else{
43318             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43319             body.push(tpl.body.replace(/(\r\n|\n)/g,
43320                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43321             body.push("'].join('');};};");
43322             body = body.join('');
43323         }
43324         
43325         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43326        
43327         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43328         eval(body);
43329         
43330         return this;
43331     },
43332
43333     applyTemplate : function(values){
43334         return this.master.compiled.call(this, values, {});
43335         //var s = this.subs;
43336     },
43337
43338     apply : function(){
43339         return this.applyTemplate.apply(this, arguments);
43340     }
43341
43342  });
43343
43344 Roo.XTemplate.from = function(el){
43345     el = Roo.getDom(el);
43346     return new Roo.XTemplate(el.value || el.innerHTML);
43347 };